Templated Check For the Existence of a Class Member Function

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.

Use SFINAE to detect the existence of a templated member function

First of all, showing you a shortened version of your original code:

template <typename T>
struct has_method_hello {

static constexpr auto test(int) -> decltype(std::declval<T&>().hello(), std::true_type());

static constexpr std::false_type test(...);

using result_type = decltype(test(0));
static const bool value = result_type::value;

};

struct Foo {
void hello() {}
};

Now making it work for a template parameter, easy, an example:

template <typename T>
struct has_method_hello {

static constexpr auto test(int) -> decltype(std::declval<T&>().hello(std::declval<int&>()), std::true_type());

static constexpr std::false_type test(...);

using result_type = decltype(test(0));
static const bool value = result_type::value;

};

struct Foo {
template <typename T>
void hello(T& v) {}
};

Note that, I have hardcoded int type here. You can make that part of has_method_hello template too.

Test if a not class member function exists (SFINAE) [duplicate]

You may use something like this:

template <class T>
static auto hasPrintMethod(int)
->std::integral_constant<bool, std::is_class<decltype(print(T()))>::value>;

template <class>
static auto hasPrintMethod(...)->std::false_type;

template <typename T>
struct HasPrintMethod : decltype(hasPrintMethod<T>(0)) {
};

Here the decltype(print(T())) is std::string for classes for which your non-member function is defined, and an erroneous type for other classes. So, according to SFINAE concept, HasPrintMethod<A>::value is equal to true for class A with print function defined and is equal to false otherwise.

Check if templated member function exists SFINAE

but unfortunately the static_assert is triggered for init (as the specialization is probably evaluated at a later time as the object is being instantiated in main())

Not exactly.

The problem is that init() is a template method, so when you write

decltype(testSignature(&MemberPointerType::FUNCTION))

no pointer is selected because the compiler can't select the right method.

You can try with

decltype(testSignature(&MemberPointerType::template FUNCTION<__VA_ARGS__>))

but now doesn't works for exec() that isn't a template method

To works with both template and non-template method... not simple passing trough a variadic macro because the variadic part can't be empty... but I propose something as follows

template <typename...>
struct wrap
{ };

#define CHECK4_MEMBER_FUNC(RETTYPE,FUN,...) \
template <class ClassType> \
class CIfCheck_##FUN \
{\
private: \
template <typename MPT> \
static auto testSig (wrap<void>) \
-> std::is_same<decltype(std::declval<MPT>().FUN()),\
RETTYPE>; \
\
template <typename MPT, typename ... As> \
static auto testSig (wrap<As...>) \
-> std::is_same<decltype(std::declval<MPT>().FUN(std::declval<As>()...)), \
RETTYPE>; \
\
template <typename...> \
static std::false_type testSig (...);\
\
public: \
using type = decltype(testSig<ClassType>(wrap<__VA_ARGS__>{}));\
static const bool value = type::value; \
};

Observe that I've added a wrap structure to wrap template parameter types; usually is used std::tuple but, in this case, we need wrap<void> because std::tuple<void> gives error.

Observe also that my solution is different from another point of view (and can be better or worse, according your specific needs): your solution check if is present a method with the exactly signature; my solution check if is present a method that is callable with a given list of arguments.

Concrete example: suppose that there is a Bla::foo() method that accept a long value

    void foo (long)
{ }

With your solution, if you check for an int parameter

CHECK4_MEMBER_FUNC(void, foo, int);

static_assert( false == CIfCheck_foo<Bla>::value, "no foo with int");

you get a false value from CIfCheck_foo because there isn't in Bla a method foo of type void(&BLA::*)(int) (there is a void(&BLA::*)(long) that is different).

With my method you get a true value from CIfCheck_foo because the foo(long) accept also a int value (and the returned type is void).


std::false_type testExistence(...);

Why exactly do I have to pass an argument here? If I remove the variadic argument ... option (and nullptr and nullptr_t), the compiler errors due to ambiguous existence of testExistence().

That testExistence(), as

    template <typename...> \
static std::false_type testSig (...);\

is the second choice.

I mean... when you macro call testExistence() inside decltype()

decltype(testExistence<ClassType>(nullptr));

or my macro call testSig() inside decltype()

decltype(testSig<ClassType>(wrap<__VA_ARGS__>{}));

call that functions with an argument (nullptr or wrap<__VA_ARGS__>{}).

When the first choice is available (when is present a RETTYPE (MemberPointerType::*)(__VA_ARGS__) in your case, when is callable a method with the required arguments in my example), the compiler choose that version and return std::true_type (or std::is_same in my code).

But when the first choice isn't available?

Second choices, the versions returning std::false, are required. But the call is with an argument. The ellipsis here is the old C-style variadic argument list and accept zero or more arguments, so accept also one argument.

If you remove the ellipsis (...), the second choices can't accept an argument anymore (become zero arguments functions) and you get a compilation error because the compiler doesn't find a second choice function compatible with an argument.

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>());
}

Using SFINAE to check if member exists in class based on a template

The template parameters of the template template parameter can not be used in the body.

template <typename T, typename = void>
struct has_psl : std::false_type
{};

template <template <typename> typename EntryType, typename KeyType>
struct has_psl<EntryType<KeyType>, std::void_t<decltype(EntryType<KeyType>::psl)>> : std::true_type
{};

template <template <typename> typename EntryType, typename KeyType, std::enable_if<has_psl<EntryType<KeyType>>::value>>
bool f(EntryType<KeyType> *buffer, size_t offset, EntryType<KeyType> *new_entry) {
return true;
}

And then,

static_assert(has_psl<int>::value == false);
static_assert(has_psl<RawBytesEntry<int>>::value == true);

Online Demo



Related Topics



Leave a reply



Submit