Sfinae to Check For Inherited Member Functions

SFINAE to check for inherited member functions

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

Check if a class has a member function of a given signature

I'm not sure if I understand you correctly, but you may exploit SFINAE to detect function presence at compile-time. Example from my code (tests if class has member function size_t used_memory() const).

template<typename T>
struct HasUsedMemoryMethod
{
template<typename U, size_t (U::*)() const> struct SFINAE {};
template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
template<typename U> static int Test(...);
static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};

template<typename TMap>
void ReportMemUsage(const TMap& m, std::true_type)
{
// We may call used_memory() on m here.
}
template<typename TMap>
void ReportMemUsage(const TMap&, std::false_type)
{
}
template<typename TMap>
void ReportMemUsage(const TMap& m)
{
ReportMemUsage(m,
std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
}

Is it possible to check if a member function is defined for a class even if the member is inherited from an unknown base class

Unfortunately it wouldn't be possible at least in C++03 and I doubt in C++11 also.

Few important points:

  1. The proposed SFINAE works only if the method is public
  2. Even if the SFINAE would have worked for base methods, the point (1)
    applies; because for private and protected inheritance the SFINAE
    may end up useless
  3. Assuming you may want to deal only with public method/inheritance,
    the code HasFoo::test<> can be enhanced for taking multiple
    parameters where a base class also can be passed;
    std::is_base_of<> can be used for further validation of the
    base/derived relationship; then apply the same logic for base class
    also

Templated check for the existence of a class member function?

Yes, with SFINAE you can check if a given class does provide a certain method. Here's the working code:

#include <iostream>

struct Hello
{
int helloworld() { return 0; }
};

struct Generic {};

// SFINAE test
template <typename T>
class has_helloworld
{
typedef char one;
struct two { char x[2]; };

template <typename C> static one test( decltype(&C::helloworld) ) ;
template <typename C> static two test(...);

public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
std::cout << has_helloworld<Hello>::value << std::endl;
std::cout << has_helloworld<Generic>::value << std::endl;
return 0;
}

I've just tested it with Linux and gcc 4.1/4.3. I don't know if it's portable to other platforms running different compilers.

Check for function signature also for inherited functions

The following works:

    /// "Stores a type"
template<typename T> struct Type2Type{
typedef T type;
};

/// Evil hackery to put an expression into a type
template<typename T>
Type2Type<T> operator,(T, Type2Type<void>);

template<typename T>
T declval();

template<class T>
struct EraseReturnsIterator
{
typedef typename T::iterator iterator;
typedef BOOST_TYPEOF_TPL((declval<T>().erase(declval<iterator>()), Type2Type<void>())) result;

static CONSTEXPR bool value = boost::is_same<iterator, typename result::type>::value;
};

Basicly we just call the function whose return type we need. If no function of this type ever returned void than BOOST_TYPEOF_TPL would already work. Unfortunately erase CAN return void which breaks the implementation as it tries to pass "void&" somewhere down the stack.

So to avoid the void (no pun intended) we put it in a type, that holds a type. To be able to do this for expressions, we overload the comma operator. This way result equals "Type2Type" which we can easily read. Done!

Idea for the comma-overload: https://stackoverflow.com/a/5553460/1930508

Checking a member exists, possibly in a base class, C++11 version

Actually, things got much easier in C++11 thanks to the decltype and late return bindings machinery.

Now, it's just simpler to use methods to test this:

// Culled by SFINAE if reserve does not exist or is not accessible
template <typename T>
constexpr auto has_reserve_method(T& t) -> decltype(t.reserve(0), bool()) {
return true;
}

// Used as fallback when SFINAE culls the template method
constexpr bool has_reserve_method(...) { return false; }

You can then use this in a class for example:

template <typename T, bool b>
struct Reserver {
static void apply(T& t, size_t n) { t.reserve(n); }
};

template <typename T>
struct Reserver <T, false> {
static void apply(T& t, size_t n) {}
};

And you use it so:

template <typename T>
bool reserve(T& t, size_t n) {
Reserver<T, has_reserve_method(t)>::apply(t, n);
return has_reserve_method(t);
}

Or you can choose a enable_if method:

template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<has_reserve_method(t), bool>::type {
t.reserve(n);
return true;
}

template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<not has_reserve_method(t), bool>::type {
return false;
}

Note that this switching things is actually not so easy. In general, it's much easier when just SFINAE exist -- and you just want to enable_if one method and not provide any fallback:

template <typename T>
auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) {
t.reserve(n);
}

If substitution fails, this method is removed from the list of possible overloads.

Note: thanks to the semantics of , (the comma operator) you can chain multiple expressions in decltype and only the last actually decides the type. Handy to check multiple operations.

Checking for template parent class in C++ using SFINAE

You might do

template <typename T>
std::true_type is_my_parent_impl(const MyParent<T>*);

std::false_type is_my_parent_impl(const void*);

template <typename T>
using is_my_parent = decltype(is_my_parent_impl(std::declval<T*>()));

Demo

Checking a member exists, possibly in a base class, VS2005/08 version

I was able to come up with a solution that works in all major compilers. Sadly, there is a preprocessor check for MSVC because it complains about the solution for other compilers. The main difference is that MSVC does not accept function pointers inside sizeof() and conversely, GCC does not seem to accept (&C::resize == 0) in the check. Clang happily accepts both.

#include <iostream>

class Base {
public:
void resize(int, int, int) { }
};

class Derived : public Base {

};

class Unrelated { };

template<typename T>
class has_resize_method {
struct Yes { char unused[1]; };
struct No { char unused[2]; };

#ifdef _MSC_VER
template <class C>
static Yes test(char (*)[(&C::resize == 0) + 1]);
#else
template <class C>
static Yes test(char (*)[sizeof(&C::resize) + 1]);
#endif
template<class C>
static No test(...);
public:
static const bool value = (sizeof(test<T>(0)) == sizeof(Yes));
};

int main() {
std::cout << (has_resize_method<Base>::value ? "Base has method resize" : "Base has NO method resize") << std::endl;
std::cout << (has_resize_method<Derived>::value ? "Derived has method resize" : "Derived has NO method resize") << std::endl;
std::cout << (has_resize_method<Unrelated>::value ? "Unrelated has method resize" : "Unrelated has NO method resize") << std::endl;
return 0;
}

Output:

Base has method resize
Derived has method resize
Unrelated has NO method resize

Tested on GCC 4.5.3, GCC 4.3.4, Clang 3.0, Visual C++ 2008 and Visual C++ 2010. I don't have access to Visual C++ 2005 but I think it will work there, too. It also compiles on Comeau Online but I cannot guarantee it produces a correct output there.

Works with both final and __sealed classes.

Note though that it checks not only for member functions but for member pointers in general. You might want to add additional checks such as boost::is_member_function_pointer if this behavior is unwanted. Similarly, you might want to add checks for number of arguments/argument types/result types - again, boost will be very helpful here, esp. boost type decomposition.



Related Topics



Leave a reply



Submit