Member check exists, possibly in base class, version VS2005 / 08
In Member validation exists, possibly in the base class, version C ++ 11 , we developed a version of the classic C ++ 11 member validation type -trait from SFINAE to validate inherited member functions , which also works with C ++ 11 classes final
, 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 final
a non-standard extension named sealed
from VS2005, but decltype
only added in VS2010. This leaves VS2005 and 2008 where the class marked as sealed
still breaks the classic type-character and the C ++ 11 version cannot be used.
So, is there a way to formulate has_resize_method
so that it works with VC2005 / 08 classes too sealed
?
Obviously using C ++ 11 features to work with C ++ 11 ( final
) only problem is fine, so using VS-only extensions to work with VS2005 / 08 is only a class issue sealed
, but if there is a solution that works for all three sets compilers {C ++ 11, {VS2005, VS2008}, everyone else}, that would be cool, but probably too many to ask :)
source to share
I was able to find a solution that works in all major compilers. Unfortunately there is a preprocessor check for MSVC because it complains about a 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 check. Klang 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 too. It also compiles on Comeau Online , but I cannot guarantee that it produces correct output there.
Works with both final and __- class.
Note that it checks not only member functions, but member pointers in general. You might want to add additional checks, such as boost::is_member_function_pointer
if this behavior is undesirable. Likewise, you can add checks for the number of arguments / argument types / result types - again, boost will be very useful here, especially. decomposition of type boost .
source to share
MSVC has a special instruction __ if_exists with vs2005. MSDN link here . You can use it to directly control the name of a member function. And then check the signature. Below is a simple example of detecting foo:
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;
source to share