Add nodiscard attribute to functions in third party header

441 views Asked by At

I'm using a library that I can't edit and don't want to fork, which has a bunch of functions of the int error return style:

int libnamespace_dosomething(int, sometype*);

I'd like the library to have [[nodiscard]] or __attribute__((warn_unused_result)) on most, probably all of the interface, so that the compiler will bark when I fail to check whether a call succeeded.

Any suggestions for how to achieve this?

Current ideas are quite labour intensive:

  • editing the header, fixing errors, putting the header back. Repeat occasionally
  • something ugly based on macros, might be able to generate the intercept from nm | bash
  • clang plugin

edit: wrapper approach I was considering is

int libcall(float);
#define libcall(...) mylibcall(__VA_ARGS__)
[[nodiscard]] inline int mylibcall(float x)
{
  return (libcall)(x);
}
1

There are 1 answers

0
Jon Chesterfield On

In hindsight, I should have tried the obvious before posting.

Clang treats the attribute as additive on repeat declarations,

[[nodiscard]] int libnamespace_dosomething(int, sometype*);

so writing that ^ before, or after, the declaration in the header is included works.

With some effort it's probably possible to wrap that in a macro to handle the repeated typing. I think

#define NODISCARD(ARITY,X) /*careful*/

is implementable in C++, such that

NODISCARD(2, libnamespace_dosomething);

expands to a declaration that matches the function passed but with nodiscard added. Not confident about inferring the arity from a function pointer. I'll update this answer if I hack out the above.

Example of the above suggestion

#include <tuple>

namespace nodiscard {
// Extract return / argument types from address of function symbol
template <typename F> struct trait;
template <typename R, typename... Ts> struct trait<R (*)(Ts...)> {
  constexpr static const size_t nargs = sizeof...(Ts);
  typedef R ReturnType;
  template <size_t i> struct arg {
    typedef typename std::tuple_element<i, std::tuple<Ts...>>::type type;
  };
};
} // namespace nodiscard

#define NODISCARD_INSTANTIATE(SYM, ARITY)                                      \
  static_assert(ARITY == nodiscard::trait<decltype(&SYM)>::nargs,              \
                "Arity Error");                                                \
  NODISCARD_INSTANTIATE_##ARITY(SYM, nodiscard::trait<decltype(&SYM)>)

#define NODISCARD_INSTANTIATE_0(SYM, T) [[nodiscard]] T::ReturnType SYM();

#define NODISCARD_INSTANTIATE_1(SYM, T)                                        \
  [[nodiscard]] T::ReturnType SYM(typename T::template arg<0>::type);

#define NODISCARD_INSTANTIATE_2(SYM, T)                                        \
  [[nodiscard]] T::ReturnType SYM(typename T::template arg<0>::type,           \
                                  typename T::template arg<1>::type);

#define NODISCARD_INSTANTIATE_3(SYM, T)                                        \
  [[nodiscard]] T::ReturnType SYM(typename T::template arg<0>::type,           \
                                  typename T::template arg<1>::type,           \
                                  typename T::template arg<2>::type);

// had to go up to 8, copy&paste style