Question

In Checking a member exists, possibly in a base class, C++11 version, we developed a C++11 version of the classical member-checking type-trait from SFINAE to check for inherited member functions that also works with C++11 final classes, but uses C++11 features (namely, decltype), too:

template<typename T>
class has_resize_method {
    struct Yes { char unused[1]; };
    struct No { char unused[2]; };
    static_assert(sizeof(Yes) != sizeof(No));

    template<class C>
    static decltype(std::declval<C>().resize(10), Yes()) test(int);
    template<class C>
    static No test(...);
public:
    static const bool value = (sizeof(test<T>(0)) == sizeof(Yes));
};

MSVC has had final as a non-standard extension named sealed since VS2005, but decltype has only been added in VS2010. That leaves VS2005 and 2008 where a class that is marked as sealed still breaks the classical type-trait and the C++11 version cannot be used.

So, is there a way to formulate has_resize_method such that it works on VC2005/08 sealed classes, too?

Obviously, just as using C++11-only features to work around a C++11-only problem (final) is fine, so would be using VS-only extensions to work around the VS2005/08-only problem of sealed classes, but if there's a solution that works for all three sets of compilers {C++11,{VS2005,VS2008},all others}, that would be cool, but probably too much to ask for :)

Was it helpful?

Solution

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.

OTHER TIPS

MSVC has a special statement __if_exists since vs2005. MSDN Link here. You can use it to check the member function name directly. And then check the signature. Following is a simple foo detection example:

template <typename T, typename U>
int8_t FooCheck( void(T::*)(U) )
{
    return 0;
}

template <typename T>
int16_t FooCheck( void(T::*)(double))
{
    return 0;
}

template <typename T>
int32_t FooCheck(void(T::*)(int))
{
    return 0;
}


template <typename T>
class Detector
{
public:

    __if_exists(T::foo)
    {    
       enum
       {
           value = sizeof(FooCheck(&T::foo))
       };
    }
    __if_not_exists(T::foo)
    {
       enum
       {
           value = 0
       };
   }

};


std::cout << Detector<Class>::value << std::endl;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top