Find out whether a C++ object is callable
Asked Answered
S

8

40

Is it possible to write a type trait, say is_callable<T> which tells if an object has an operator() defined? It is easy if the arguments to the call operator are known in advance, but not in the general case. I want the trait to return true if and only if there is at least one overloaded call operator defined.

This question is related and has a good answer, but it doesn't work on all types (only on int-convertible types). Also, std::is_function works, but only on proper C++ functions, not on functors. I'm looking for a more general solution.

Sketchbook answered 13/3, 2013 at 18:50 Comment(8)
This may be relevantChenab
Do you have a list of possible argument types? If so, that makes it definitely possible. Not quite sure, however, about a generic overload, though.Mullite
Why do you need this? I mean, why would you want to know if something is callable if you don't know any of the argument types? Handling things like overloaded operators won't be possible if you don't know them.Unscreened
@RichardJ.RossIII: yes C++11 is fine, and no I don't have a liste of argument types.Sketchbook
@mfontanini: basically it's to distinguish between "immediate" values vs "lazy" values such as callbacks, functors, lambda expression...Sketchbook
OP or @jrok, can you clarify whether this is about any variant of operator(), or just the parameter-less operator()?Sepulveda
@einpoklum: It's about operator()(...) and not operator()(), so yes any variant. That's what I meant in the 2nd sentence of the question.Sketchbook
See also this GitHub gist by Victor Robertson: gist.github.com/vmrob/f78c16cf2293786fbf03Septuplet
E
41

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
{ };
Elda answered 13/3, 2013 at 21:24 Comment(18)
Great! Good and short code, live demo and explanation. +1 for using liveworkspace.org, thanks.Sketchbook
Nice but this don't compile if the type is not inheritable from, like int. I'm not sure how to modify it so that it work in this case (and should return false I guess).Skep
This was really useful but the extension to make it work on non-class types was clearly untested — it doessn't actually work in practice. Also, the code for is_callable can fail if you give it a reference. But I was inspired by this answer to write my own version (elsewhere on this page) which handles these issues.Metamorphic
@MAristic I can't remember whether I tested it last year, but it does seem to work today :) Can you show an example where it doesn't work?Elda
please submit your solution to the boost type traits libraryUnconventional
Just don't get how this solution is better then the least scored here (except this one has issues with non-class type and final classes)Prier
@Prier You're the second person that said it doesn't work with non-class types. I showed that it does work (see the link in this comment). Is there something that I'm missing? The limitation with final classes can be sidestepped with expression SFINAE only if we know the number of arguments. Even OP says: "It is easy if the arguments to the call operator are known in advance, but not in the general case."Elda
In my opinion your solution (with all additional code) looks less graceful then I've mentioned. I don't say that your soultion is bad, I just doesn't understand what's wrong with the one I mentioned which I believe looks better.Prier
Add this specialization to make it work with function pointers: template<typename Ret, typename... Args> struct is_callable<Ret(*)(Args...)> : public std::true_type { };Beyrouth
@Prier "the least scored"?! =( After these years, which one is "the least scored" solution you refer to?Gradual
@jrok, I'm a newbie to template metaprogramming, and I'm struggling to understand how Check<void (Fallback::*)(), &U::operator()>*) works. What happens when T has a void T::operator()? Why not Check<Fallback, &U::operator()>*? I'd genuinely appreciate your help in understanding.Illbred
@Elda I understand that the Check template instantiation is being used to trigger SFINAE, but I'm not really confident that I'm understanding the cause of the Ambiguity correctly.Illbred
@Illbred First, template<typename U, U> struct Check; declares a template that takes a type and an instance of that type. Check<Fallback, &C::operator()> isn't valid because obviously a pointer- to-member isn't of type Fallback.Elda
@Illbred So, what happens when T doesn't have a member operator()? The compiler, when checking if second overload of testElda
@Illbred ...is viable, sees one Derived::operator() in Fallback base class of Derived, checks whether it's convertible to Fallback::operator() (it is) and happily selects the overload. So sizeof(test<Derived>(0)) == sizeof(yes) yields true.Elda
@Illbred On the other hand, when there is operator() in T (or more of them), then there's an ambiguity in look-up - the compiler doesn't know to whether Derived::operator() refers to T::operator() or Fallback::operator(). So substitution fails and yes test(...) is selected.Elda
@Illbred PS: there's a mistake in second comment - should be "yields false". Hope that helps, I'm not that good at explaining and I have nagging toddler sitting in my lap :)Elda
You've done a good job explaining: it has cleared up my confusion. I'm especially grateful that you took the time to do this. :)Illbred
M
11

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!

Metamorphic answered 16/9, 2014 at 0:36 Comment(0)
P
11

C++17 brings std::is_invocable and friends.

This answer also given a solution on how to emulate it with C++14.

Parthenos answered 22/4, 2019 at 23:14 Comment(0)
C
10

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");
}
Chenab answered 13/3, 2013 at 19:19 Comment(2)
Hum... The limitation on overload operators is really restricting, but your solution is very compact.Sketchbook
Did you cover function pointer here? Seems not to me. Reference types are not covered either.Parthenos
T
4

There are several other answers already, of course, and they are useful, but none of them seem to cover every use case AFAICT. Borrowing from those answers and this possible implementation of std::is_function, I created a version that covers every possible use case of which I could think. It's kind of lengthy, but very feature complete (Demo).

template<typename T, typename U = void>
struct is_callable
{
    static bool const constexpr value = std::conditional_t<
        std::is_class<std::remove_reference_t<T>>::value,
        is_callable<std::remove_reference_t<T>, int>, std::false_type>::value;
};

template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(*)(Args...), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(&)(Args...), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(*)(Args......), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(&)(Args......), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)volatile, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const volatile, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)volatile, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const volatile, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)&, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)volatile&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const volatile&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)&, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)volatile&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const volatile&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)volatile&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const volatile&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)volatile&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const volatile&&, U> : std::true_type{};

template<typename T>
struct is_callable<T, int>
{
private:
    using YesType = char(&)[1];
    using NoType = char(&)[2];

    struct Fallback { void operator()(); };

    struct Derived : T, Fallback {};

    template<typename U, U>
    struct Check;

    template<typename>
    static YesType Test(...);

    template<typename C>
    static NoType Test(Check<void (Fallback::*)(), &C::operator()>*);

public:
    static bool const constexpr value = sizeof(Test<Derived>(0)) == sizeof(YesType);
};

This works correctly with non-class types (returns false, of course), function types (<T()>), function pointer types, function reference types, functor class types, bind expressions, lambda types, etc. This works correctly even if the class constructor is private and/or non-defaulted, and even if operator() is overloaded. This returns false for member function pointers by design because they are not callable, but you can use bind to create a callable expression.

Tarttan answered 3/3, 2016 at 3:12 Comment(2)
Note: Visual Studio 2015 seems to choke on the "variadic function" overloads ("Args......"), and I'll confess I'm not entirely sure what that means or why it has that syntax.Tarttan
Actually, I did some further testing, and I think I understand. If a function such as template<typename ...Args> void bar(Args &&...args, ...); were defined (although why you would mix a variadic function template with the unsafe variadic ... is beyond me!), then is_callable<decltype(bar<>)> will refer to the Args...... overload. Because the Args parameter pack may be empty, you can't type Args..., ... as the parameter list, and if it isn't empty then it will automatically insert the necessary comma for you (except, apparently, in Visual Studio 2015).Tarttan
T
3

Note: These assume that the default constructor is valid for the type your checking. Not sure offhand how to get around that.

The following seems to work if it's callable with 0 arguments. Is there something in is_function's implementation that might help to extend this to 1 or more argument callables?:

template <typename T>
struct is_callable {
    // Types "yes" and "no" are guaranteed to have different sizes,
    // specifically sizeof(yes) == 1 and sizeof(no) == 2.
    typedef char yes[1];
    typedef char no[2];

    template <typename C>
    static yes& test(decltype(C()())*);

    template <typename>
    static no& test(...);

    // If the "sizeof" the result of calling test<T>(0) would be equal to the     sizeof(yes),
    // the first overload worked and T has a nested type named foobar.
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};   

If you know the type of the argument (even if it's a template parameter), the following would work for 1 argument, and I imagine one could extend pretty easily from there:

template <typename T, typename T2>
struct is_callable_1 {
    // Types "yes" and "no" are guaranteed to have different sizes,
    // specifically sizeof(yes) == 1 and sizeof(no) == 2.
    typedef char yes[1];
    typedef char no[2];

    template <typename C>
    static yes& test(decltype(C()(T2()))*);

    template <typename, typename>
    static no& test(...);

    // If the "sizeof" the result of calling test<T>(0) would be equal to the     sizeof(yes),
    // the first overload worked and T has a nested type named foobar.
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

Edit here is a modification that handles the case where default constructor isn't available.

Tacitus answered 13/3, 2013 at 19:19 Comment(2)
Re These assume that the default constructor is valid for the type your checking. Not sure offhand how to get around that. Take a look at std::declval.Elda
@Elda Thanks, I hadn't seen that one yet. in the pastebin I attached, I just used a helper struct that had the necessary conversion operator defined, but I supposed I could replace it with declval.Tacitus
S
3

This is a neat and short trick for finding if T is callable. It goes along the lines originally proposed by Walter E. Brown at CPPCON'14 in his talk on modern template metaprogramming.

template <class... >
using void_t = void;

template <class T>
using has_opr_t = decltype(&T::operator());

template <class T, class = void>
struct is_callable : std::false_type { };

template <class T>
struct is_callable<T, void_t<has_opr_t<typename std::decay<T>::type>>> : std::true_type { };
Stenography answered 4/3, 2019 at 10:9 Comment(1)
I believe this will break if operator() is overloaded or templated.Tennison
T
1

Here is another implementation.

It makes use of std::is_function template for free functions.

For classes, it uses something similar to the Member Detector Idiom. If T has a call operator, callable_2 will contain more than one operator(). This will cause the first can_call function to be discarded (through SFINAE) due to ambiguity failure in decltype(&callable_2<T>::operator()) and the second can_call function will return true. If T does not have a call operator, the first can_call function will be used (due to overload resolution rules).

namespace impl
{
struct callable_1 { void operator()(); };
template<typename T> struct callable_2 : T, callable_1 { };

template<typename T>
static constexpr bool can_call(decltype(&callable_2<T>::operator())*) noexcept { return false; }

template<typename>
static constexpr bool can_call(...) noexcept { return true; }

template<bool is_class, typename T>
struct is_callable : public std::is_function<T> { };

template<typename T> struct is_callable<false, T*> : public is_callable<false, T> { };
template<typename T> struct is_callable<false, T* const> : public is_callable<false, T> { };
template<typename T> struct is_callable<false, T* volatile> : public is_callable<false, T> { };
template<typename T> struct is_callable<false, T* const volatile> : public is_callable<false, T> { };

template<typename T>
struct is_callable<true, T> : public std::integral_constant<bool, can_call<T>(0)> { };
}

template<typename T>
using is_callable = impl::is_callable<std::is_class<std::remove_reference_t<T>>::value,
                                    std::remove_reference_t<T>>;
Torsi answered 31/7, 2016 at 23:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.