4

This is different from checking if a specific function is defined. Here, for this check to return true, the function has to be defined and passing the arguments of a certain type should result in a valid call.

Example: for a function f and an argument of type T &&, the check should return true if f is a valid function that accepts—either directly or through implicit conversion—an argument of type T &&.

void f(int &) {};

int main(int argc, char **av)
{
    isFunctionCallable<int>(f); // true because `int i; f(i);` is valid.
    isFunctionCallable<int &&>(f); // false because `int i; f(std::move(i));` is invalid.
    return 0;
}

Please note the distinction between “arguments” and “parameters” as explained in this answer.

Community
  • 1
  • 1
ChristopherC
  • 1,635
  • 16
  • 31
  • Does this answer your question? [How to determine function signature of a callable object?](https://stackoverflow.com/questions/5937485/how-to-determine-function-signature-of-a-callable-object) – ThibaultDC Oct 24 '22 at 21:10

2 Answers2

8

Making use of C++11, this can be done using a mixture of SFINAE, decltype and std::declval.

template<typename ...>
struct Bool
{ using type = bool; };

template<typename ... T_Dummies>
using BoolT = typename Bool<T_Dummies ...>::type;


template<typename T>
struct DeclvalType
{
    using type = typename std::conditional<
        std::is_rvalue_reference<T>::value,
        T,
        T &
    >::type;
};

template<typename T>
using DeclvalTypeT = typename DeclvalType<T>::type;


template<typename T>
struct ExtractFunction;

template<typename T_Return, typename ... T_Args>
struct ExtractFunction<T_Return(T_Args ...)>
{ using type = T_Return(T_Args ...); };

template<typename T_Return, typename ... T_Args>
struct ExtractFunction<T_Return(*)(T_Args ...)>
{ using type = T_Return(T_Args ...); };

template<typename T, typename T_Return, typename ... T_Args>
struct ExtractFunction<T_Return(T::*)(T_Args ...)>
{ using type = T_Return(T_Args ...); };

template<typename T, typename T_Return, typename ... T_Args>
struct ExtractFunction<T_Return(T::*)(T_Args ...) const>
{ using type = T_Return(T_Args ...); };

template<typename T>
using ExtractFunctionT = typename ExtractFunction<T>::type;


template<typename ... T, typename T_Function>
constexpr auto
impl(T_Function function) ->
    BoolT<decltype(
        std::declval<ExtractFunctionT<T_Function>>()
            (std::declval<DeclvalTypeT<T>>() ...)
    )>
{ return true; }

template<typename ... T>
constexpr bool
impl(...)
{ return false; }


template<typename ... T, typename T_Function>
constexpr bool
isFunctionCallable(T_Function function)
{ return impl<T ...>(function); }

With the help of some more code (available in this Gist), it is possible to output tables showing what type of arguments can be passed to what type of parameters.

using T = Default (empty struct with implicit constructors):

  +--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
  |                                |                                                                                                                                 |
  |        Function signature      |                                                          Argument type                                                          |
  |                                |                                                                                                                                 |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |                                |  T  |  const T  |   volatile T  |   const volatile T  |  T &  |  const T &  |   volatile T &  |   const volatile T &  |   T &&  |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T)                   |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const T)             |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(volatile T)          |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const volatile T)    |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T &)                 |  x  |           |               |                     |   x   |             |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const T &)           |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(volatile T &)        |  x  |           |       x       |                     |   x   |             |        x        |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const volatile T &)  |  x  |     x     |       x       |          x          |   x   |      x      |        x        |           x           |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T &&)                |     |           |               |                     |       |             |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+


using T = NonCopiable:

  +--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
  |                                |                                                                                                                                 |
  |        Function signature      |                                                          Argument type                                                          |
  |                                |                                                                                                                                 |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |                                |  T  |  const T  |   volatile T  |   const volatile T  |  T &  |  const T &  |   volatile T &  |   const volatile T &  |   T &&  |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T)                   |     |           |               |                     |       |             |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const T)             |     |           |               |                     |       |             |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(volatile T)          |     |           |               |                     |       |             |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const volatile T)    |     |           |               |                     |       |             |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T &)                 |  x  |           |               |                     |   x   |             |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const T &)           |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(volatile T &)        |  x  |           |       x       |                     |   x   |             |        x        |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const volatile T &)  |  x  |     x     |       x       |          x          |   x   |      x      |        x        |           x           |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T &&)                |     |           |               |                     |       |             |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+


using T = NonMovable:

  +--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
  |                                |                                                                                                                                 |
  |        Function signature      |                                                          Argument type                                                          |
  |                                |                                                                                                                                 |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |                                |  T  |  const T  |   volatile T  |   const volatile T  |  T &  |  const T &  |   volatile T &  |   const volatile T &  |   T &&  |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T)                   |  x  |     x     |               |                     |   x   |      x      |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const T)             |  x  |     x     |               |                     |   x   |      x      |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(volatile T)          |  x  |     x     |               |                     |   x   |      x      |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const volatile T)    |  x  |     x     |               |                     |   x   |      x      |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T &)                 |  x  |           |               |                     |   x   |             |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const T &)           |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(volatile T &)        |  x  |           |       x       |                     |   x   |             |        x        |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const volatile T &)  |  x  |     x     |       x       |          x          |   x   |      x      |        x        |           x           |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T &&)                |     |           |               |                     |       |             |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+


using T = NonCopiableNonMovable:

  +--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
  |                                |                                                                                                                                 |
  |        Function signature      |                                                          Argument type                                                          |
  |                                |                                                                                                                                 |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |                                |  T  |  const T  |   volatile T  |   const volatile T  |  T &  |  const T &  |   volatile T &  |   const volatile T &  |   T &&  |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T)                   |     |           |               |                     |       |             |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const T)             |     |           |               |                     |       |             |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(volatile T)          |     |           |               |                     |       |             |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const volatile T)    |     |           |               |                     |       |             |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T &)                 |  x  |           |               |                     |   x   |             |                 |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const T &)           |  x  |     x     |               |                     |   x   |      x      |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(volatile T &)        |  x  |           |       x       |                     |   x   |             |        x        |                       |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(const volatile T &)  |  x  |     x     |       x       |          x          |   x   |      x      |        x        |           x           |         |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
  |  function(T &&)                |     |           |               |                     |       |             |                 |                       |    x    |
  +--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+

We can for example deduce from these tables that an argument of type T can't be passed to a function that takes T && as parameter. Or that function(T &&) only accepts arguments of type T &&.

Note how deleting the copy and/or the move constructor reduce the possibilities since the arguments can't be converted implicitly anymore.

Edit:

Added support for member functions, thanks to @hvd.

ChristopherC
  • 1,635
  • 16
  • 31
  • Just a warning: this cannot be made to work if your function is overloaded. –  Dec 21 '14 at 16:07
  • True, thanks for pointing that out! That's also why in the Gist code I had to wrap the functions within a struct. On top of that it won't work on member functions neither. I can't think of a way to fix those 2 flaws though, any idea? – ChristopherC Dec 21 '14 at 16:42
  • 1
    You could deal with member functions by turning them into regular functions: use a type trait that extracts `Ret (Args...)` from `Ret (T::*) (Args...)`, and then you can use what you already have. But I don't think there's any way to get something working for overloaded functions. –  Dec 21 '14 at 16:53
  • I think I got a type trait working like you suggested (http://pastebin.com/2vCdKs4u) but I can't figure out how to integrate it within my code—too many new concepts at once for me! – ChristopherC Dec 22 '14 at 03:41
  • You can then check `decltype(std::declval>()(std::declval>() ...))` in SFINAE context, very similar to what you're already doing for non-member functions. Or even simply call `isFunctionCallable((ExtractFunctionT *) nullptr)`. –  Dec 22 '14 at 06:16
  • The syntax is getting a bit tricky, I was missing the empty parenthesis from your SFINAE snippet. That being said, there seems to be an error in the SFINAE expression since it always picks the default overload which returns `false`, see [http://ideone.com/Abj1lT](http://ideone.com/Abj1lT). I'm not even sure how to get meaningful debug messages from this kind of expressions? – ChristopherC Dec 23 '14 at 01:33
  • 1
    `isFunctionCallable(f)` deduces `T_Function` as `void(*)(int&)`, not `void(int&)`, and your implementation of `ExtractFunction` doesn't deal with pointers-to-functions yet. Adding a specialisation for that, I get `1` as the output. –  Dec 23 '14 at 09:41
  • I'm obviously far from being comfortable with all this new code but it seems to work now. I've edited the answer to include it, thanks for your help! – ChristopherC Dec 24 '14 at 01:02
3
#define overload_set(F)\
  struct { auto operator()(auto&&...args)const\
    ->decltype(F(std::forward<decltype(args)>(args)...))\
     { return (F(std::forward<decltype(args)>(args)...)); }\
  }

this takes a token F and generates an overload set type for F.

It is not quite perfect: it only does perfect forwarding with SFINAE tests. But it is close.

We then use this:

template<class T,class=void>struct can_invoke:std::false_type{};
template<class F,class...Args>
struct can_invoke<F(Args...),
  decltype(void(
    std::declval<F>()(std::declval<Args>()...)
  ))
>:std::true_type{};

Mixing them we get:

typedef overload_set(Foo) Foo_overloads;
std::cout << can_invoke<Foo_overloads(int, int) >::value<<"\n";

will print 1 if Foo(3,2) works. As noted, this is limited by failures of perfect forwarding.

You can also pass Foo_overloads{} to functions expecting a function object, and it will dispatch at the call site instead of doing it when you pass the function object.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524