4

If and how is it possible to deduce the signature type from any callable in C++17? Snippet:

template <typename T>
struct MyFunction;

template <typename R, typename... Args>
struct MyFunction<R(Args...)>
{
};

template <typename Callable>
auto deduceSignature(Callable&& c)
{
    // using Signature = ???;
    // return MyFunction<Signature>{ ???};}
}

I would like to use use class template argument deduction in the return statement. In client code I want to write this then:

std::int8_t freeFunction(std::int8_t x)
{
    return x;
}

auto c1 = deduceSignature(&freeFunction);
auto c2 = deduceSignature([](std::int8_t x){
    return x;
});
Juergen
  • 3,489
  • 6
  • 35
  • 59
  • 1
    `std::function` does this automatically. The easiest is probably to use that in an undeduced context. Something like `decltype(std::function{std::declval()})` – super Nov 27 '20 at 08:17
  • Can you provide an example? If I put this line into deduceSignature, then I get "error: invalid use of incomplete type ‘struct utils::memoize::MyFunction >’" – Juergen Nov 27 '20 at 08:59

1 Answers1

5

std::function can be constructed from any callable, and can deduce the signature (since c++17). We can use this to make a type trait that extracts the signature.

#include <functional>

template <typename T>
struct get_signature;

template <typename R, typename... Args>
struct get_signature<std::function<R(Args...)>> {
    using type = R(Args...);
};

template <typename Callable>
auto deduceSignature(Callable&& c)
{
    using Signature = typename get_signature<decltype(std::function{c})>::type;
}

int main() {
    deduceSignature([](int a){return 5;});
}
super
  • 12,335
  • 2
  • 19
  • 29
  • Notice that the callable should not have overloaded `operator()` or templated one. (else anyway signature would be ambiguous). – Jarod42 Nov 27 '20 at 09:35
  • Great thx.Just one question: what makes this special for C++17? Does this not work with C++14? – Juergen Nov 27 '20 at 09:58
  • 1
    @Juergen, [CTAD](https://en.cppreference.com/w/cpp/language/class_template_argument_deduction), I guess. – Evg Nov 27 '20 at 10:04
  • @Juergen CTAD as Evg mentioned. You can make it work in c++11 as well, but you need more boilerplate. Usually the approach there is to take the signature of the objects `operator()` which works on functors and lambdas. Then you need a separate specialization to deal with function pointers. – super Nov 27 '20 at 11:13