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."); }