10

Let's suppose I have a template function which takes an argument, which is a function (it can be a std::function, or a lambda, or actual function pointer). A silly example that illustrates the problem:

template<typename F,typename A,typename B = typename std::result_of<F(A)>::type>
B blabla(F &&f)
{
    return f(A())/3;
}

I can reference the return type of f with std::result_of::typename, given I have the type of A, but I would like the compiler to deduce type A from F's first argument. (If I write

template<typename A,typename B>
B blabla(const std::function<B(A)> &f)
{
    return f(A())/3;
}

the compiler have problems deducing A and B (especially if it's not an std::function but a lambda), so this is not the right way to do it.)

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Zsolt Szatmari
  • 1,219
  • 1
  • 12
  • 29
  • Does this describe your problem? https://stackoverflow.com/questions/27851111/why-cant-i-pass-a-lambda-to-this-function-which-takes-a-stdfunction – 5gon12eder Jan 10 '15 at 18:42
  • ... or this : https://stackoverflow.com/questions/26665152/compiler-does-not-deduce-template-parameters-map-stdvector-stdvector. – WhozCraig Jan 10 '15 at 19:12
  • Limited to the three cases you listed (`std::function`, C++11 (non-generic) lambda, and plain function pointer), this is doable, but impossible to generalize to arbitrary functors or generic lambdas. – T.C. Jan 10 '15 at 19:24
  • 5gon12eder, WhozCraig: Thanks, but not really, in these cases they didn't really want the argument(s) of the passed function. – Zsolt Szatmari Jan 10 '15 at 20:26
  • T.C.: Of course, it must get the type from *somewhere*. – Zsolt Szatmari Jan 10 '15 at 20:27

2 Answers2

16

This won't work for generic lambdas or arbitrary functors whose operator() is overloaded or is a template.

// primary template.
template<class T>
struct function_traits : function_traits<decltype(&T::operator())> {
};

// partial specialization for function type
template<class R, class... Args>
struct function_traits<R(Args...)> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
};

// partial specialization for function pointer
template<class R, class... Args>
struct function_traits<R (*)(Args...)> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
};

// partial specialization for std::function
template<class R, class... Args>
struct function_traits<std::function<R(Args...)>> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
};

// partial specialization for pointer-to-member-function (i.e., operator()'s)
template<class T, class R, class... Args>
struct function_traits<R (T::*)(Args...)> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
};

template<class T, class R, class... Args>
struct function_traits<R (T::*)(Args...) const> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
};

// additional cv-qualifier and ref-qualifier combinations omitted
// sprinkle with C-style variadics if desired

Then

template<class T>
using first_argument_type = typename std::tuple_element<0, typename function_traits<T>::argument_types>::type;

Replace 0 with the desired number as needed, or write a separate alias that also take an index. Demo.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • 1
    This approach is implemented in Boost.CallableTraits, see `args_t` in https://www.boost.org/doc/libs/1_68_0/libs/callable_traits/doc/html/index.html – olq_plo Aug 14 '18 at 13:41
  • @olq_plo, how to get N'th arg type of a callable using boost::callable_traits? – Sergei Krivonos Nov 28 '18 at 13:04
  • 1
    @Sergei args_t returns a `std::tuple`, so you use `std::tuple_element` to get the Nth arg. – olq_plo Nov 28 '18 at 20:27
3

#include <boost/type_traits.hpp>

boost::function_traits<decltype(function)>::arg2_type
Sergei Krivonos
  • 4,217
  • 3
  • 39
  • 54