C++ metafunction to determine whether a type is callable
Solution 1:
The presence of a non-templated T::operator() for a given type T can be detected by:
template<typename C> // detect regular operator()
static char test(decltype(&C::operator()));
template<typename C> // worst match
static char (&test(...))[2];
static const bool value = (sizeof( test<T>(0) )
The presence of a templated operator can be detected by:
template<typename F, typename A> // detect 1-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0) ) ) = 0);
template<typename F, typename A, typename B> // detect 2-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0) ) ) = 0);
// ... detect N-arg operator()
template<typename F, typename ...Args> // worst match
static char (&test(...))[2];
static const bool value = (sizeof( test<T, int>(0) ) == 1) ||
(sizeof( test<T, int, int>(0) ) == 1); // etc...
However, these two do not play nicely together, as decltype(&C::operator()) will produce an error if C has a templated function call operator. The solution is to run the sequence of checks against a templated operator first, and check for a regular operator() if and only if a templated one can not be found. This is done by specializing the non-templated check to a no-op if a templated one was found.
template<bool, typename T>
struct has_regular_call_operator
{
template<typename C> // detect regular operator()
static char test(decltype(&C::operator()));
template<typename C> // worst match
static char (&test(...))[2];
static const bool value = (sizeof( test<T>(0) ) == 1);
};
template<typename T>
struct has_regular_call_operator<true,T>
{
static const bool value = true;
};
template<typename T>
struct has_call_operator
{
template<typename F, typename A> // detect 1-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0) ) ) = 0);
template<typename F, typename A, typename B> // detect 2-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0) ) ) = 0);
template<typename F, typename A, typename B, typename C> // detect 3-arg operator()
static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0), (*(C*)0) ) ) = 0);
template<typename F, typename ...Args> // worst match
static char (&test(...))[2];
static const bool OneArg = (sizeof( test<T, int>(0) ) == 1);
static const bool TwoArg = (sizeof( test<T, int, int>(0) ) == 1);
static const bool ThreeArg = (sizeof( test<T, int, int, int>(0) ) == 1);
static const bool HasTemplatedOperator = OneArg || TwoArg || ThreeArg;
static const bool value = has_regular_call_operator<HasTemplatedOperator, T>::value;
};
If the arity is always one, as discussed above, then the check should be simpler. I do not see the need for any additional type traits or library facilities for this to work.
Solution 2:
With the advent of our collective experience in c++11 (and beyond) it's probably time to revisit this question.
This little type trait seems to work for me:
#include <iostream>
#include <utility>
template<class F, class...Args>
struct is_callable
{
template<class U> static auto test(U* p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type());
template<class U> static auto test(...) -> decltype(std::false_type());
static constexpr bool value = decltype(test<F>(0))::value;
};
Which we can test thus:
template<class F, class...Args, typename std::enable_if<is_callable<F, Args&&...>::value>::type* = nullptr>
void test_call(F, Args&&...args)
{
std::cout << "callable" << std::endl;
}
template<class F, class...Args, typename std::enable_if<not is_callable<F, Args&&...>::value>::type* = nullptr>
void test_call(F, Args&&...args)
{
std::cout << "not callable" << std::endl;
}
extern void f3(int, const std::string&)
{
}
int main()
{
auto f1 = [](int, std::string) {};
test_call(f1, 0, "hello");
test_call(f1, "bad", "hello");
std::function<void(int, const std::string&)> f2;
test_call(f2, 0, "hello");
test_call(f2, "bad", "hello");
test_call(f3, 0, "hello");
test_call(f3, "bad", "hello");
}
expected output:
callable
not callable
callable
not callable
callable
not callable
Solution 3:
This is a really interesting question. I've been puzzled hard by it.
I think I managed to make a variation to Crazy Eddie's code that will allow any number of parameters, however, it does use variadic templates and it does require to specify the parameters you are expecting the "callable" object to be called with. Long story short, I got this running and working as expected on gcc 4.6.0:
EDIT: One could use the std::result_of utility as well, however it does not work because it requires a typename
to disambiguate the std::result_of<..>::type
which breaks the Sfinae.
#include <iostream>
#include <type_traits>
template < typename PotentiallyCallable, typename... Args>
struct is_callable
{
typedef char (&no) [1];
typedef char (&yes) [2];
template < typename T > struct dummy;
template < typename CheckType>
static yes check(dummy<decltype(std::declval<CheckType>()(std::declval<Args>()...))> *);
template < typename CheckType>
static no check(...);
enum { value = sizeof(check<PotentiallyCallable>(0)) == sizeof(yes) };
};
int f1(int,double) { return 0; };
typedef int(*f1_type)(int,double) ; //this is just to have a type to feed the template.
struct Foo { };
struct Bar {
template <typename T>
void operator()(T) { };
};
int main() {
if( is_callable<f1_type,int,double>::value )
std::cout << "f1 is callable!" << std::endl;
if( is_callable<Foo>::value )
std::cout << "Foo is callable!" << std::endl;
if( is_callable<Bar,int>::value )
std::cout << "Bar is callable with int!" << std::endl;
if( is_callable<Bar,double>::value )
std::cout << "Bar is callable with double!" << std::endl;
};
I hope this is what you are looking for because I don't think it is possible to do much more.
EDIT: For your use case, it is a partial solution, but it might help:
template <typename Predicate1, typename Predicate2>
struct and_predicate
{
template <typename ArgT>
enable_if<ice_and<is_callable<Predicate1,ArgT>::value,
is_callable<Predicate2,ArgT>::value>::value,
bool>::type operator()(const ArgT& arg)
{
return predicate1(arg) && predicate2(arg);
}
Predicate1 predicate1;
Predicate2 predicate2;
};
template <typename Predicate1, typename Predicate2>
enable_if<ice_and<is_callable< Predicate1, boost::any >::value,
is_callable< Predicate2, boost::any >::value>::value,
and_predicate<Predicate1, Predicate2>>::type
operator&&(Predicate1 predicate1, Predicate2 predicate2)
{
return and_predicate<Predicate1, Predicate2>{predicate1, predicate2};
}