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 (andnullptr
andnullptr_t
), the compiler errors due to ambiguous existence oftestExistence()
.
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
C++: Modifying Array Via Function
C++ - Initializing Variables in Header VS With Constructor
Is It a Good Idea to Typedef Pointers
Why Do People Say There Is Modulo Bias When Using a Random Number Generator
What Is a "Translation Unit" in C++
How Do Malloc() and Free() Work
Problems Importing Libraries to My C++ Project, How to Fix This
Reflection and Refraction Impossible Without Recursive Ray Tracing
How to Cast Const Uint8_T* to Char*
While Writting Unittests for a Function, Should I Mock the Internal Function Calls Made
Undefined, Unspecified and Implementation-Defined Behavior
What Uses Are There For "Placement New"
Why Can't Variables Be Declared in a Switch Statement
C++ Unordered_Map Using a Custom Class Type as the Key
Why Is Unsigned Integer Overflow Defined Behavior But Signed Integer Overflow Isn'T