Find out whether a C++ object is callable
The answers here were helpful but I came here wanting something that could also spot whether something was callable regardless of whether it happened to be an object or a classic function. jrok's answer to this aspect of the problem, alas, didn't work because std::conditional
actually evaluates the types of both arms!
So, here's a solution:
// Note that std::is_function says that pointers to functions
// and references to functions aren't functions, so we'll make our
// own is_function_t that pulls off any pointer/reference first.
template<typename T>
using remove_ref_t = typename std::remove_reference<T>::type;
template<typename T>
using remove_refptr_t = typename std::remove_pointer<remove_ref_t<T>>::type;
template<typename T>
using is_function_t = typename std::is_function<remove_refptr_t<T>>::type;
// We can't use std::conditional because it (apparently) must determine
// the types of both arms of the condition, so we do it directly.
// Non-objects are callable only if they are functions.
template<bool isObject, typename T>
struct is_callable_impl : public is_function_t<T> {};
// Objects are callable if they have an operator(). We use a method check
// to find out.
template<typename T>
struct is_callable_impl<true, T> {
private:
struct Fallback { void operator()(); };
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
template<typename>
static std::true_type test(...);
template<typename C>
static std::false_type test(Check<void (Fallback::*)(), &C::operator()>*);
public:
typedef decltype(test<Derived>(nullptr)) type;
};
// Now we have our final version of is_callable_t. Again, we have to take
// care with references because std::is_class says "No" if we give it a
// reference to a class.
template<typename T>
using is_callable_t =
typename is_callable_impl<std::is_class<remove_ref_t<T>>::value,
remove_ref_t<T> >::type;
But in the end, for my application, I really wanted to just know whether you could say f() (i.e., call it with no arguments), so I instead went with something much simpler.
template <typename T>
constexpr bool noarg_callable_impl(
typename std::enable_if<bool(sizeof((std::declval<T>()(),0)))>::type*)
{
return true;
}
template<typename T>
constexpr bool noarg_callable_impl(...)
{
return false;
}
template<typename T>
constexpr bool is_noarg_callable()
{
return noarg_callable_impl<T>(nullptr);
}
In fact, I went even further. I knew the function was supposed to return an int
, so rather than just check that I could call it, I checked the return type, too, by changing the enable_if
to:
typename std::enable_if<std::is_convertible<decltype(std::declval<T>()()),
int>::value>::type*)
Hope this helps someone!
I think this trait does what you want. It detects operator()
with any kind of signature even if it's overloaded and also if it's templatized:
template<typename T>
struct is_callable {
private:
typedef char(&yes)[1];
typedef char(&no)[2];
struct Fallback { void operator()(); };
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
template<typename>
static yes test(...);
template<typename C>
static no test(Check<void (Fallback::*)(), &C::operator()>*);
public:
static const bool value = sizeof(test<Derived>(0)) == sizeof(yes);
};
The principle is based on Member Detector idiom. As it is, it will fail to compile if you pass it a non-class type, but that shouldn't be hard to fix, I just left it out for brevity. You can also extend it to report true for functions.
Of course it doesn't give you any info about the signature(s) of operator()
whatsoever, but I believe that's not what you asked for, right?
EDIT for Klaim:
It's simple enough to make it work (return false
) with non-class types. If you rename the above class to is_callable_impl
, you can write this, for example:
template<typename T>
struct is_callable
: std::conditional<
std::is_class<T>::value,
is_callable_impl<T>,
std::false_type
>::type
{ };
Here is a possible solution using C++11 that works without requiring to know the signature of the call operator for functors, but only as long the functor does not have more than one overload of operator ()
:
#include <type_traits>
template<typename T, typename = void>
struct is_callable : std::is_function<T> { };
template<typename T>
struct is_callable<T, typename std::enable_if<
std::is_same<decltype(void(&T::operator())), void>::value
>::type> : std::true_type { };
This is how you would use it:
struct C
{
void operator () () { }
};
struct NC { };
struct D
{
void operator () () { }
void operator () (int) { }
};
int main()
{
static_assert(is_callable<C>::value, "Error");
static_assert(is_callable<void()>::value, "Error");
auto l = [] () { };
static_assert(is_callable<decltype(l)>::value, "Error");
// Fires! (no operator())
static_assert(is_callable<NC>::value, "Error");
// Fires! (several overloads of operator ())
static_assert(is_callable<D>::value, "Error");
}