I want to write a C++ metafunction is_callable<F, Arg>
that defines value
to be true
, if and only if the type F has the function call operator of the form SomeReturnType operator()(const Arg &)
. For example, in the following case
struct foo {
void operator(const int &) {}
};
I want is_callable<foo, int &>
to be false
and is_callable<foo, const int &>
to be true
. This is what I have so far :
#include <memory>
#include <iostream>
template<typename F, typename Arg>
struct is_callable {
private:
template<typename>
static char (&test(...))[2];
template<unsigned>
struct helper {
typedef void *type;
};
template<typename UVisitor>
static char test(
typename helper<
sizeof(std::declval<UVisitor>()(std::declval<Arg>()), 0)
>::type
);
public:
static const bool value = (sizeof(test<F>(0)) == sizeof(char));
};
struct foo {
void operator()(const int &) {}
};
using namespace std;
int main(void)
{
cout << is_callable<foo, int &>::value << "\n";
cout << is_callable<foo, const int &>::value << "\n";
return 0;
}
This prints 1
and 1
, but I want 0
and 1
because foo
only defines void operator()(const int &)
.
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.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 aspassed
.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...)
(with apologies to Kerrek for using his answer as a starting point)
EDIT: Updated to handle types without any
operator()
at all.Demo: http://ideone.com/T3Iry
Here is a possible solution that utilizes an extra test to see if your template is being instantiated with a
const T&
:Here's something I hacked up which may or may not be what you need; it does seem to give true (false) for
(const) int &
...Ran across this while doing something else, was able to adapt my code to fit. It has the same features (and limitations) as @Xeo, but does not need sizeof trick/enable_if. The default parameter takes the place of needing to do the enable_if to handle template functions. I tested it under g++ 4.7 and clang 3.2 using the same test code Xeo wrote up
Something like this maybe? It's a bit round about to make it work on VS2010.