3

There are other answers on this site using SFINAE but with non C++11 code, and there are others using C++11 code like decltypes to make this process easier. However, I am not sure how to check if a class has a function with a specific signature.

I want to check if a class has the function receive(const Event &) where Event is a class type that is specified when calling the check function.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
user975989
  • 2,578
  • 1
  • 20
  • 38
  • `There are other answers on this site using SFINAE but with non C++11 code:` Not true, take a look at [this](http://stackoverflow.com/a/264088/2352671) – 101010 Jul 26 '14 at 20:19
  • possible duplicate of [Is it possible to write a C++ template to check for a function's existence?](http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence) – 101010 Jul 26 '14 at 20:20
  • I don't think this properly answers my question, but it seems that the compiler complains if you try to create a function pointer to the function like so: `void(Reciever::*func)(const Event &) = &Reciever::recieve;` – user975989 Jul 26 '14 at 20:31
  • @user975989: Your question is really unclear. Your example [works for me](http://coliru.stacked-crooked.com/a/add6412d99cf9bda). What exactly doesn't work about the [answer](http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence/264088#264088) linked by 40two? – Mankarse Jul 26 '14 at 20:39
  • Nothing per say, I was just wondering if there was a less verbose way of doing it with decltypes and such. I didn't use it before because I didn't know of it, which is my fault. – user975989 Jul 26 '14 at 20:44

3 Answers3

6

The best way I know of is checking if you can actually call the function and if it returns the type you expect. Here's an example of how to detect if a class C has a receive method which takes const Event& as a parameter and "returns" void. The detection does not care whether the method is implemented in the class C directly or in some base class that C derives from, neither does it care whether there are further defaulted parameters. Adapt as needed.

template< typename C, typename Event, typename = void >
struct has_receive
  : std::false_type
{};

template< typename C, typename Event >
struct has_receive< C, Event, typename std::enable_if<
    std::is_same<
        decltype( std::declval<C>().receive( std::declval<const Event&>() ) ),
        void
    >::value
>::type >
  : std::true_type
{};
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
2

You may use the following to match exact signature:

template <typename U, typename Event>
class has_receive
{
private:
    template<typename T, T> struct helper;
    template<typename T>
    static std::uint8_t check(helper<void (T::*)(const Event &), &T::receive>*);
    template<typename T> static std::uint16_t check(...);
public:
    static
    constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t);
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I wonder how useful this actually is, since it rejects members like `void(T::*)(const Event&) const`, for example. I don't know why the OP needs this, but it seems to me that there is no reason to look for an exact signature. – R. Martinho Fernandes Jul 27 '14 at 11:52
1

There is help with macros:

#define DEFINE_METHOD_CHECKER(RETURN_TYPE, METHOD_NAME, PARAMETERS)     \
template<typename T>                                                    \
struct Is ## METHOD_NAME ## MemberFunctionExists                        \
{                                                                       \
private:                                                                \
    typedef char True;                                                  \
    typedef char (&False)[2];                                           \
    template<typename U, RETURN_TYPE (U::*)PARAMETERS = &U::METHOD_NAME>\
    struct Checker                                                      \
    {                                                                   \
        typedef True Type;                                              \
    };                                                                  \
    template<typename U>                                                \
    static typename Checker<U>::Type Tester(const U*);                  \
    static False Tester(...);                                           \
public:                                                                 \
    enum { value = (sizeof(Tester(static_cast<const T*>(0))) == sizeof(True)) }; \
}

// IsMethodMemberFunctionExists<T>::value
DEFINE_METHOD_CHECKER(int, Method, (bool));
// IsTestMemberFunctionExists<T>::value
DEFINE_METHOD_CHECKER(int*, Test, (int&, char));

#include <iostream>

class Exists
{
public:
    int Method(bool);
    int* Test(int&, char);

};

class NotExists
{
};

int main()
{
    std::cout << IsMethodMemberFunctionExists<Exists>::value << std::endl;
    std::cout << IsTestMemberFunctionExists<Exists>::value << std::endl;

    std::cout << IsMethodMemberFunctionExists<NotExists>::value << std::endl;
    std::cout << IsTestMemberFunctionExists<NotExists>::value << std::endl;
}

So, in your case you need to define a few checkers - one checker for one type of Event:

// void recieve(const Event1&)
DEFINE_METHOD_CHECKER(void, recieve, (const Event1&));

// void recieve(const Event2&)
DEFINE_METHOD_CHECKER(void, recieve, (const Event2&));
grisha
  • 1,247
  • 1
  • 14
  • 20