I want to write a C++ metafunction is_callable
that defines value
to be true
, if and only if the type F has the function cal
After hours of playing around and some serious discussions in the C++ chat room, we finally got a version that works for functors with possibly overloaded or inherited operator()
and for function pointers, based on @KerrekSB's and @BenVoigt's versions.
#include
#include
template
class Callable{
static int tester[1];
typedef char yes;
typedef yes (&no)[2];
template
static typename std::enable_if::value, char>::type
sfinae(decltype(std::declval()(std::declval()...)) (C::*pfn)(Brgs...));
template
static typename std::enable_if::value, char>::type
sfinae(decltype(std::declval()(std::declval()...)) (C::*pfn)(Brgs...) const);
template
static char sfinae(decltype(std::declval()(std::declval()...)) (G::*pfn)(Brgs...));
template
static char sfinae(decltype(std::declval()(std::declval()...)) (G::*pfn)(Brgs...) const);
template
static yes test(int (&a)[sizeof(sfinae(&G::operator()))]);
template
static no test(...);
public:
static bool const value = sizeof(test(tester)) == sizeof(yes);
};
template
struct Helper{ R operator()(Args...); };
template
class Callable
: public Callable, Args...>{};
Live example on Ideone. Note that the two failing tests are overloaded operator()
tests. This is a GCC bug with variadic templates, already fixed in GCC 4.7. Clang 3.1 also reports all tests as passed
.
If you want operator()
with default arguments to fail, there is a possible way to do that, however some other tests will start failing at that point and I found it as too much hassle to try and correct that.
Edit: As @Johannes correctly notes in the comment, we got a little inconsistency in here, namely that functors which define a conversion to function pointer will not be detected as "callable". This is, imho, pretty non-trivial to fix, as such I won't bother with it (for now). If you absolutely need this trait, well, leave a comment and I'll see what I can do.
Now that all this has been said, IMHO, the idea for this trait is stupid. Why whould you have such exact requirements? Why would the standard is_callable
not suffice?
(Yes, I think the idea is stupid. Yes, I still went and built this. Yes, it was fun, very much so. No, I'm not insane. Atleast that's what I believe...)