1

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.

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
c1646091
  • 306
  • 1
  • 10
  • are you sure you want to check if there's a function of a specific signature, like `void SetPosition(float,float)`, or whether there's function `SetPosition` tak can take two values implicitly convertible to float ? – Piotr Skotnicki May 05 '16 at 15:22
  • A specific signature or an implicitly convertible version, either one would be fine for my needs. I can probably work better with a specific signature as I can macro template arguments, although I'm open to any solutions that will solve this particular problem. – c1646091 May 05 '16 at 15:31
  • [Here's](http://coliru.stacked-crooked.com/a/72426d6be71e55a5) a solution using the detection idiom. – Piotr Skotnicki May 05 '16 at 15:35
  • You should add it as an answer. Thank you Piotr. – c1646091 May 05 '16 at 15:38
  • And [here's](http://coliru.stacked-crooked.com/a/e806f6cf85675b1e) a modification of your C++03-like solution – Piotr Skotnicki May 05 '16 at 15:39
  • The C++03-like solution is much needed as I don't have C++14 support. It seems to throw a hissy fit on an inherited class member `&sf::Transformable::setPosition’ is not a valid template argument for type ‘void (sf::Text::*)(float, float)’ because it is of type ‘void (sf::Transformable::*)(float, float)’` Swapping Text for Transformable, it runs fine, however these functions will most likely involve inheritance. – c1646091 May 05 '16 at 16:02
  • you already use C++11, and you don't need C++14 here – Piotr Skotnicki May 05 '16 at 16:04
  • Okay. How is the error for classes that inherit a base function resolved? – c1646091 May 05 '16 at 16:15
  • I don't know your use-case, perhaps add more code or as a new question – Piotr Skotnicki May 05 '16 at 16:26

1 Answers1

2

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

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160