c1646091
c1646091

Reputation: 306

Checking if a class has a function that has parameters in it

Having read the question from Is it possible to write a C++ template to check for a function's existence?, and tested a few of the answers, I find it only works on detecting functions that take no parameters, EG void HelloWord(). Searching around for answers either just gives solutions to parameterless functions or eye-wateringly complex solutions that I can't make head nor tail of.

Here's my macro template code for constructing detectors:

#define MEMBERFUNCTIONTODETECT(CONTAINS_NAME,MEMBERFUNCTION) \
template <typename TemplateItem>\
class CONTAINS_NAME\
{\
    typedef char Yes;\
    typedef char No[2];\
\
    template <typename TemplateClass> static Yes &Test( decltype(&TemplateClass::MEMBERFUNCTION) );\
    template <typename TemplateClass> static No &Test(...);\
\
public:\
    enum { Value = sizeof(Test<TemplateItem>(0)) == sizeof(char) };\
};

How do I modify the above code in order to detect a member function in a class, that contains parameters, EG void SetPosition(float,float)?

I'm willing to accept solutions that are total rewrites, but if any solutions are more complex than the above, please try to explain what is happening in as much depth as possible so I (and presumably others) can understand how it works. Treat me like I have no idea what anything of what you wrote means.

Upvotes: 1

Views: 136

Answers (1)

Piotr Skotnicki
Piotr Skotnicki

Reputation: 48527

Since you want to check both, whether there's a member function of a specific signature, and also, whether a given function is callable with some arguments type, you could utilize the detection idiom for that:

#include <type_traits>
#include <utility>

template <typename...>
using void_t = void;

template <typename AlwaysVoid, template <typename...> class Operation, typename... Args>
struct detect_impl : std::false_type {};

template <template <typename...> class Operation, typename... Args>
struct detect_impl<void_t<Operation<Args...>>, Operation, Args...> : std::true_type {};

template <template <typename...> class Operation, typename... Args>
using detect = detect_impl<void_t<>, Operation, Args...>;

Now you just need to add some custom detectors:

// Check specific signature
template <typename T, typename Sig>
using has_SetPosition = decltype(static_cast<Sig>(&T::SetPosition));

// Check if the function can be called with Args...
template <typename T, typename... Args>
using can_call_SetPosition = decltype(std::declval<T>().SetPosition(std::declval<Args>()...));

Tests:

struct A
{
    void SetPosition(float, float) {}
};

struct B
{
    void SetPosition(int, int) {}
};

struct C
{
};

int main()
{
    static_assert(detect<has_SetPosition, A, void(A::*)(float, float)>{}, "!");
    static_assert(!detect<has_SetPosition, B, void(B::*)(float, float)>{}, "!");
    static_assert(!detect<has_SetPosition, C, void(C::*)(float, float)>{}, "!");

    static_assert(detect<can_call_SetPosition, A&, float, float>{}, "!");
    static_assert(detect<can_call_SetPosition, B&, float, float>{}, "!");
    static_assert(!detect<can_call_SetPosition, C&, float, float>{}, "!");
}

Note the difference with class B, while the first trait rejects this class, the second one evaluates to true.

DEMO

Upvotes: 2

Related Questions