Is it possible to write a C++(0x) metafunction that determines whether a type is callable?
By callable type I mean a function type, function pointer type, function reference type (these are detected by boost::function_types::is_callable_builtin
), lambda types, and any class with an overloaded operator()
(and maybe any class with an implicit conversion operator to one of these, but that's not absolutely necessary).
EDIT: The metafunction should detect the presence of an operator()
with any signature, including a templated operator()
. I believe this is the difficult part.
EDIT: Here is a use case:
template <typename Predicate1, typename Predicate2> struct and_predicate { template <typename ArgT> bool 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>::value, is_callable<Predicate2>::value>::value, and_predicate<Predicate1, Predicate2>>::type operator&&(Predicate1 predicate1, Predicate2 predicate2) { return and_predicate<Predicate1, Predicate2>{predicate1, predicate2}; }
is_callable
is what I would like to implement.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With