32

Using SFINAE, i can detect wether a given class has a certain member function. But what if i want to test for inherited member functions?

The following does not work in VC8 and GCC4 (i.e. detects that A has a member function foo(), but not that B inherits one):

#include <iostream>

template<typename T, typename Sig>                                 
struct has_foo {                     
    template <typename U, U> struct type_check;
    template <typename V> static char (& chk(type_check<Sig, &V::foo>*))[1];
    template <typename  > static char (& chk(...))[2]; 
    static bool const value = (sizeof(chk<T>(0)) == 1);
};

struct A {
    void foo();
};

struct B : A {};

int main()
{
    using namespace std;
    cout << boolalpha << has_foo<A, void (A::*)()>::value << endl; // true
    cout << boolalpha << has_foo<B, void (B::*)()>::value << endl; // false
}

So, is there a way to test for inherited member functions?

Community
  • 1
  • 1
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236

4 Answers4

31

Take a look at this thread:

http://lists.boost.org/boost-users/2009/01/44538.php

Derived from the code linked to in that discussion:

#include <iostream>

template <typename Type> 
class has_foo
{ 
   class yes { char m;}; 
   class no { yes m[2];}; 
   struct BaseMixin 
   { 
     void foo(){} 
   }; 
   struct Base : public Type, public BaseMixin {}; 
   template <typename T, T t>  class Helper{}; 
   template <typename U> 
   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::foo>* = 0); 
   static yes deduce(...); 
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
}; 

struct A {
    void foo();
};

struct B : A {};

struct C {};

int main()
{
    using namespace std;
    cout << boolalpha << has_foo<A>::result << endl;
    cout << boolalpha << has_foo<B>::result << endl;
    cout << boolalpha << has_foo<C>::result;
}

Result:

true
true
false
joshperry
  • 41,167
  • 16
  • 88
  • 103
  • 4
    It's the one shown here: http://stackoverflow.com/questions/1005476/how-to-detect-whether-there-is-a-specific-member-variable-in-class/1007175#1007175 and explained here: http://cplusplus.co.il/2009/09/11/substitution-failure-is-not-an-error-1/ . Keep in mind it will signal that there is a member of that name even if the member isn't a function or the function has some other signature, so it's doing something different. It needs the other codes shown in the usenet discussion to do further checks. – Johannes Schaub - litb Dec 28 '09 at 15:44
  • Didn't notice the missing type check, good point. You probably mean using `is_call_possible<>` from here http://groups.google.com/group/comp.lang.c++.moderated/msg/e5fbc9305539f699 ? Looks interesting, will have to test later. – Georg Fritzsche Dec 29 '09 at 10:17
  • 1
    This code fails to compile with `struct Cpp11 final : A {};` because it tries to inherit from a final class. Any workaround? – Marc Mutz - mmutz Mar 02 '12 at 08:42
  • @mmutz: see my post below as a possible solution – kispaljr Jan 07 '13 at 15:09
23

joshperry's answer is very clever and elegant, but (as it is stated below the post) it doesn't check the signature of foo() properly and doesn't work with fundamental types (like int): it causes a compiler error. I will propose a technique that handles inherited members correctly and also checks the signature of the member function. Instead of going into details I will give you two exampes and hope that the code will speak for itself.

Example1:

We are checking for a member with the following signature: T::const_iterator begin() const

template<class T> struct has_const_begin
{
    typedef char (&Yes)[1];
    typedef char (&No)[2];

    template<class U> 
    static Yes test(U const * data, 
                    typename std::enable_if<std::is_same<
                             typename U::const_iterator, 
                             decltype(data->begin())
                    >::value>::type * = 0);
    static No test(...);
    static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::type*)0));
};

Please notice that it even checks the constness of the method, and works with primitive types, as well. (I mean has_const_begin<int>::value is false and doesn't cause a compile-time error.)

Example 2

Now we are looking for the signature: void foo(MyClass&, unsigned)

template<class T> struct has_foo
{
    typedef char (&Yes)[1];
    typedef char (&No)[2];

    template<class U>
    static Yes test(U * data, MyClass* arg1 = 0,
                    typename std::enable_if<std::is_void<
                             decltype(data->foo(*arg1, 1u))
                    >::value>::type * = 0);
    static No test(...);
    static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::type*)0));
};

Please notice that MyClass doesn't has to be default constructible or to satisfy any special concept. The technique works with template members, as well.

I am eagerly waiting opinions regarding this.

kispaljr
  • 1,942
  • 2
  • 16
  • 22
  • +1 I was looking for a solution for a problem of mine, and this is exactly what I needed :) – Tom Knapen Jan 27 '12 at 21:06
  • I can't for the life of me tell if this class (or something equivalent) is currently in Boost... Do you know? – Dan Nissenbaum Mar 29 '13 at 15:16
  • While this seems the best way, I can't seem to turn it into a macro. When doing tests for many functions, a macro is a must-have and in this particular case it's impossible. I would love to be proven wrong tho. – Paweł Stawarz Aug 09 '14 at 01:59
  • @kispaljr I am also curious if this can be somehow evaluated in a macro. Something in the form of CREATE_HAS_METHOD(Type, ReturnType, (Arg1Type, Arg2Type, Arg3Type) const) – M2tM Sep 05 '14 at 23:53
  • I don't think this method distinguishes between inherited and non-inherited methods though. it will return `true` in both cases because `data->foo(*arg1, 1u)` is a valid expression in both cases – blue Apr 23 '18 at 18:07
17

Here are some usage snippets: *The guts for all this are farther down

Check for member x in a given class. Could be var, func, class, union, or enum:

CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;

Check for member function void x():

//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;

Check for member variable x:

CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

Check for member class x:

CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

Check for member union x:

CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

Check for member enum x:

CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

Check for any member function x regardless of signature:

CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

OR

CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

Details and core:

/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

//Variadic to force ambiguity of class members.  C++11 and up.
template <typename... Args> struct ambiguate : public Args... {};

//Non-variadic version of the line above.
//template <typename A, typename B> struct ambiguate : public A, public B {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};

Macros (El Diablo!):

CREATE_MEMBER_CHECK:

//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}

CREATE_MEMBER_VAR_CHECK:

//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_SIG_CHECK:

//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_func_##templ_postfix : std::false_type {};                \
                                                                            \
template<typename T>                                                        \
struct has_member_func_##templ_postfix<                                     \
    T, std::integral_constant<                                              \
        bool                                                                \
        , sig_check<func_sig, &T::func_name>::value                         \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_CLASS_CHECK:

//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_UNION_CHECK:

//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_ENUM_CHECK:

//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_CHECK:

//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}

CREATE_MEMBER_CHECKS:

//Create all the checks for one member.  Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)
Brett Rossier
  • 3,420
  • 3
  • 27
  • 36
  • 1
    do you know if its possible to make a trait to check for a free function's existence ? and not a member ? because everytime I try I get "invalid template parameter" or "checked_func was not declared in this scope". absolutely like if SFINAE was an error suddenly. – v.oddou Mar 06 '15 at 03:35
  • I assume not because a free function isn't a class or struct. This technique of deducing the presence of a member really centers on the multiple inheritance mechanism in C++ forcing ambiguity between a stub class that only exists for the purpose of hosting the member you're checking for vs the class you're actually checking for the member in. That's an interesting question though, hadn't thought about it. You might check around for other C++11/14 member-check techniques, I've seen some clever things in the new standard. :) – Brett Rossier Mar 07 '15 at 05:03
5

As all of the answers look too complicated to me, I'd like to introduce my own solution using std::declval and std::enable_if (GCC 4.8.3)

#define MEMBER_FUNC_CHECKER(name, fn, ret, args) \
template<class C, typename=void> struct name : std::false_type {}; \
template<class C> struct name<C, typename std::enable_if< \
  std::is_convertible<decltype(std::declval<C>().fn args), ret \
  >::value>::type> : std::true_type {};

NOTE: It is not precise check for signature, but for callable function with convertible return type. (edit: changed from is_same to is_convertible)

Test

struct One {
    int get() { return 0; }
    int add(int x, int y) { return x+y; }
};
struct Two: One {};
struct Not {};

MEMBER_FUNC_CHECKER(has_get, get, int, ())
MEMBER_FUNC_CHECKER(has_add, add, int, (1,2))

int main() {
    cout << "One " << (has_get<One>() ? "has" : "does not have")
        << " int get()" << endl;
    cout << "Two " << (has_get<Two>() ? "has" : "does not have")
        << " int get()" << endl;
    cout << "Not " << (has_get<Not>() ? "has" : "does not have")
        << " int get()" << endl;
    cout << "One " << (has_add<One>() ? "has" : "does not have")
        << " int add(int, int)" << endl;
    cout << "Two " << (has_add<Two>() ? "has" : "does not have")
        << " int add(int, int)" << endl;
    cout << "Not " << (has_add<Not>() ? "has" : "does not have")
        << " int add(int, int)" << endl;
    cout << "int " << (has_get<int>() ? "has" : "does not have")
        << " int get()" << endl;
}

Output

One has int get()
Two has int get()
Not does not have int get()
One has int add(int, int)
Two has int add(int, int)
Not does not have int add(int, int)
int does not have int get()

UPDATE: My checkers

/// Checker for typedef with given name and convertible type
#define TYPEDEF_CHECKER(checker, name) \
template<class C, typename T, typename = void> struct checker : std::false_type {}; \
template<class C, typename T> struct checker<C, T, typename std::enable_if< \
  std::is_convertible<typename C::name, T>::value>::type> : std::true_type {}
/// Checker for typedef with given name and exact type
#define TYPEDEF_CHECKER_STRICT(checker, name) \
template<class C, typename T, typename = void> struct checker : std::false_type {}; \
template<class C, typename T> struct checker<C, T, typename std::enable_if< \
  std::is_same<typename C::name, T>::value>::type> : std::true_type {}
/// Checker for typedef with given name and any type
#define TYPEDEF_CHECKER_ANY(checker, name) \
template<class C, typename = void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
  !std::is_same<typename C::name*, void>::value>::type> : std::true_type {}

/// Checker for member with given name and convertible type
#define MTYPE_CHECKER(checker, name) \
template<class C, typename T, typename = void> struct checker : std::false_type {}; \
template<class C, typename T> struct checker<C, T, typename std::enable_if< \
  std::is_convertible<decltype(C::name), T>::value>::type> : std::true_type {}
/// Checker for member with given name and exact type
#define MTYPE_CHECKER_STRICT(checker, name) \
template<class C, typename T, typename = void> struct checker : std::false_type {}; \
template<class C, typename T> struct checker<C, T, typename std::enable_if< \
  std::is_same<decltype(C::name), T>::value>::type> : std::true_type {}
/// Checker for member with given name and any type
#define MTYPE_CHECKER_ANY(checker, name) \
template<class C, typename = void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
  !std::is_same<decltype(C::name)*, void>::value>::type> : std::true_type {}

/// Checker for static const variable with given name and value
#define MVALUE_CHECKER(checker, name, val) \
template<class C, typename = void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
  std::is_convertible<decltype(C::name), const decltype(val)>::value && C::name == val>::type> : std::true_type {}
/// Checker for static const variable with given name, value and type
#define MVALUE_CHECKER_STRICT(checker, name, val) \
template<class C, typename = void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
  std::is_same<decltype(C::name), const decltype(val)>::value && C::name == val>::type> : std::true_type {}

/// Checker for member function with convertible return type and accepting given arguments
#define METHOD_CHECKER(checker, name, ret, args) \
template<class C, typename=void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
  std::is_convertible<decltype(std::declval<C>().name args), ret>::value>::type> : std::true_type {};
/// Checker for member function with exact retutn type and accepting given arguments
#define METHOD_CHECKER_STRICT_RET(name, fn, ret, args) \
template<class C, typename=void> struct name : std::false_type {}; \
template<class C> struct name<C, typename std::enable_if< \
  std::is_same<decltype(std::declval<C>().fn args), ret>::value>::type> : std::true_type {};
/// Checker for member function accepting given arguments
#define METHOD_CHECKER_ANY(name, fn, args) \
template<class C, typename=void> struct name : std::false_type {}; \
template<class C> struct name<C, typename std::enable_if< \
  !std::is_same<decltype(std::declval<C>().fn args)*, void>::value>::type> : std::true_type {};

Test Code

struct One {
    typedef int type;
    static constexpr bool v = true;
    type x;
    One(type x = 0): x(x) {}
    ~One() {}
    type get() { return x; }
    type add(type x, type y) { return x+y; }
};
struct Two: One {};
struct Not {};

TYPEDEF_CHECKER(has_type, type);
TYPEDEF_CHECKER_ANY(any_type, type);
TYPEDEF_CHECKER_STRICT(exact_type, type);
MTYPE_CHECKER(has_x, x);
MTYPE_CHECKER_ANY(any_x, x);
MTYPE_CHECKER_STRICT(exact_x, x);
MVALUE_CHECKER(true_v, v, true);
MVALUE_CHECKER(true_z, z, true);
MVALUE_CHECKER(false_v, v, false);
MVALUE_CHECKER(one_v, v, 1);
MVALUE_CHECKER_STRICT(exact_v, v, 1);
METHOD_CHECKER(has_get, get, long, ());
METHOD_CHECKER(has_add, add, long, (1,2))
METHOD_CHECKER_ANY(any_get, get, ());
METHOD_CHECKER_STRICT_RET(int_get, get, int, ())
METHOD_CHECKER_STRICT_RET(long_get, get, long, ())

int main() {
#define CHECK(name, desc, ...) cout << endl; \
    cout << "One " << (name<One, ##__VA_ARGS__>() ? "has " : "does not have ") << desc << endl; \
    cout << "Two " << (name<Two, ##__VA_ARGS__>() ? "has " : "does not have ") << desc << endl; \
    cout << "Not " << (name<Not, ##__VA_ARGS__>() ? "has " : "does not have ") << desc << endl; \
    cout << "int " << (name<int, ##__VA_ARGS__>() ? "has " : "does not have ") << desc << endl
    string sep = string(60, '-');
    cout << sep;
    CHECK(any_type, "typedef type");
    CHECK(has_type, "typedef type convertible to long", long);
    CHECK(exact_type, "typedef type = int", int);
    CHECK(exact_type, "typedef type = long", long);

    cout << sep;
    CHECK(any_x, "var x");
    CHECK(has_x, "var x of type convertible to long", long);
    CHECK(exact_x, "var x of type int", int);
    CHECK(exact_x, "var x of type long", long);

    cout << sep;
    CHECK(true_v, "var v with value equal to true");
    CHECK(true_z, "var z with value equal to true");
    CHECK(false_v, "var v with value equal to false");
    CHECK(one_v, "var v with value equal to 1");
    CHECK(exact_v, "var v with value equal to 1 of type int");

    cout << sep;
    CHECK(has_get, "get()");
    CHECK(has_get, "get() with return type covertible to long");
    CHECK(has_add, "add() accepting two ints and returning ~ long");
    CHECK(int_get, "int get()");
    CHECK(long_get, "long get()");
}

Output

One has typedef type
Two has typedef type
Not does not have typedef type
int does not have typedef type

One has typedef type convertible to long
Two has typedef type convertible to long
Not does not have typedef type convertible to long
int does not have typedef type convertible to long

One has typedef type = int
Two has typedef type = int
Not does not have typedef type = int
int does not have typedef type = int

One does not have typedef type = long
Two does not have typedef type = long
Not does not have typedef type = long
int does not have typedef type = long
------------------------------------------------------------
One has var x
Two has var x
Not does not have var x
int does not have var x

One has var x of type convertible to long
Two has var x of type convertible to long
Not does not have var x of type convertible to long
int does not have var x of type convertible to long

One has var x of type int
Two has var x of type int
Not does not have var x of type int
int does not have var x of type int

One does not have var x of type long
Two does not have var x of type long
Not does not have var x of type long
int does not have var x of type long
------------------------------------------------------------
One has var v with value equal to true
Two has var v with value equal to true
Not does not have var v with value equal to true
int does not have var v with value equal to true

One does not have var z with value equal to true
Two does not have var z with value equal to true
Not does not have var z with value equal to true
int does not have var z with value equal to true

One does not have var v with value equal to false
Two does not have var v with value equal to false
Not does not have var v with value equal to false
int does not have var v with value equal to false

One has var v with value equal to 1
Two has var v with value equal to 1
Not does not have var v with value equal to 1
int does not have var v with value equal to 1

One does not have var v with value equal to 1 of type int
Two does not have var v with value equal to 1 of type int
Not does not have var v with value equal to 1 of type int
int does not have var v with value equal to 1 of type int
------------------------------------------------------------
One has get()
Two has get()
Not does not have get()
int does not have get()

One has get() with return type covertible to long
Two has get() with return type covertible to long
Not does not have get() with return type covertible to long
int does not have get() with return type covertible to long

One has add() accepting two ints and returning ~ long
Two has add() accepting two ints and returning ~ long
Not does not have add() accepting two ints and returning ~ long
int does not have add() accepting two ints and returning ~ long

One has int get()
Two has int get()
Not does not have int get()
int does not have int get()

One does not have long get()
Two does not have long get()
Not does not have long get()
int does not have long get()
firda
  • 3,268
  • 17
  • 30