In C++, we often use the substitution failure is not an error (SFINAE) rule in template overload resolution. A common problem is to write template specializations for class template arguments that expose member functions with a certain signature. The below macro allows to easily create member function detector type traits for user defined classes
#include <type_traits>
#define MEMBER_FUNCTION_DETECTOR(DetectorName, FunctionName, ReturnType, ...) \
template<typename T> \
struct DetectorName \
{ \
/* \
* \brief The second argument must be the return type of the first. We need to \
* implement two versions of the test - one for non-const and one for \
* const member functions. \
*/ \
template<typename U, U u> class Checker; \
\
template<typename V> \
static std::true_type test(Checker<ReturnType (V::*)(__VA_ARGS__), &V::FunctionName> *); \
\
template<typename V> \
static std::true_type test(Checker<ReturnType (V::*)(__VA_ARGS__) const, &V::FunctionName> *); \
\
template<typename V> \
static std::false_type test(...); \
\
typedef decltype(test<T>(nullptr)) type; \
static const bool value = std::is_same<std::true_type, type>::value; \
};
The macro creates a templated detector struct DetectorName<T> that allows us to check whether a function with the signature ReturnType T::FunctionName(...) exists. The variable macro arguments correspond to the function’s parameter types. The following snippet demonstrates the usage.
struct A
{
bool foo(double x);
}
MEMBER_FUNCTION_DETECTOR(FooDetector, foo, bool, double)
int main() {
static_assert(FooDetector<A>::value, "Foo not implemented.");
}