1

I'm using the code from another answer to obtain the types of a lambda function (return and arguments). Here is the relevant code in the answer:

template <typename T>
struct function_traits
    : public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
    enum { arity = sizeof...(Args) };
    // arity is the number of arguments.

    typedef ReturnType result_type;

    template <size_t i>
    struct arg
    {
        typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        // the i-th argument is equivalent to the i-th tuple element of a tuple
        // composed of those arguments.
    };
};

I want to create a list of arguments to automatically cast the lambda to a function pointer:

template<typename Func>
constexpr auto l2f(Func lambda) {
    typedef function_traits<Func> traits;
    return static_cast<traits::result_type(*)( ..all the args.. )(lambda);
}

Right now what I'm doing is, I added a function to the function_traits struct:

template <typename Func>
static auto convertToFunctionPointer(Func fn) {
    return static_cast<ReturnType(*)(Args...)>(fn);
}

It works, but what I really want to know is how to make available the content of the Args inside the function_traits to access it from outside, and how to "inline" multiple templates and then expand them. Something like this (does not work):

return static_cast<traits::result_type(*)(traits::arg<std::make_index_sequence<traits::arity>...>::type...)(lambda);
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    Your premise is wrong: not all lambdas can convert into function pointers! Also, those which can convert, will automatically/easily convert in a context which requires it, or you can trigger such decay trivially, e.g with a `+` sign: example: `+[]{}` converts into `void (*)()`. So you don't need a function to do that. – Nawaz Feb 28 '18 at 17:56
  • Thanks, I know that lambdas that have captures do not allow a conversion to function pointers. I tried `+[]{}` and did not work, I think it does not work in the MSVC compiler, or i did it the wrong way somehow. I know I was not clear, but the lambda conversion is more like an example for the real question. It's the issue I'm facing right now, but what I want to know is the template stuff, I'm very new to templates and I don't know how most of the things work. – Diogo Parreira Feb 28 '18 at 18:15

2 Answers2

1

As Nawaz pointed out, not all lambdas can be converted to pointers, only those without captures. For those without captures, they have an implicit conversion that can be forced by adding a + in front.

But to directly answer your question, just alias the signature within the trait

template<typename C, typename R, typename... Args>
struct function_traits<R (C::*)(Args...)>
{
    using signature = R(Args...);
};

template<typename F>
auto cast(F f)
{
    return static_cast<typename function_traits<F>::signature*>(f);
}
Passer By
  • 19,325
  • 6
  • 49
  • 96
0

but what I really want to know is how to make available the content of the Args inside the function_traits to access it from outside, and how to "inline" multiple templates and then expand them.

First of all, the simplest solution that come in my mind to solve you particular problem with l2f() is define a specific type as the signature suggested by PasserBy.

I propose a slightly different solution but the idea is the same: you can define

using func_type = std::function<ReturnType(Args...)>;

so l2f() simply become

template <typename Func>
constexpr auto l2f (Func lambda)
 { return static_cast<typename function_traits<Func>::func_type>(lambda); }

For the more general problem (" how to make available the content of the Args inside the function_traits to access it from outside, and how to "inline" multiple templates and then expand them") I suggest to avoid to define the template arg struct but simply define a tuple type inside function_traits

using tuple_type  = std::tuple<Args...>;

Next you have to call an helper function to transform the arity inside the function_traits in a std::index_sequence

template <typename Func>
constexpr auto l2f (Func lambda)
 { return l2f_helper(lambda,
      std::make_index_sequence<function_traits<Func>::arity>{});  }

and the helper function can be written as

template <typename Func, std::size_t ... Is>
constexpr auto l2f_helper (Func lambda,
                           std::index_sequence<Is...> const &)
 {
   return static_cast<std::function<
      typename function_traits<Func>::result_type(
         std::tuple_element_t<Is,
            typename function_traits<Func>::tuple_type> ...)>>(lambda);
 }

Ugly. I know.

The following is a full compilable example with both versions of l2f and a modified function_traits.

#include <tuple>
#include <iostream>
#include <functional>
#include <type_traits>

template <typename T>
struct function_traits
    : public function_traits<decltype(&T::operator())>
{ };

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
 {
   static constexpr std::size_t arity { sizeof...(Args) };

   using result_type = ReturnType;
   using func_type   = std::function<ReturnType(Args...)>;
   using tuple_type  = std::tuple<Args...>;
 };

template <typename Func>
constexpr auto l2f_v1 (Func lambda)
 { return static_cast<typename function_traits<Func>::func_type>(lambda); }

template <typename Func, std::size_t ... Is>
constexpr auto l2f_v2_helper (Func lambda,
                              std::index_sequence<Is...> const &)
 {
   return static_cast<std::function<
      typename function_traits<Func>::result_type(
         std::tuple_element_t<Is,
            typename function_traits<Func>::tuple_type> ...)>>(lambda);
 }

template <typename Func>
constexpr auto l2f_v2 (Func lambda)
 { return l2f_v2_helper(lambda,
      std::make_index_sequence<function_traits<Func>::arity>{});  }

int main ()
 {
   auto f1 = l2f_v1([](int, long){ std::cout << "lambda1!" << std::endl; });
   auto f2 = l2f_v2([](int, long){ std::cout << "lambda2!" << std::endl; });

   f1(1, 2L);
   f2(2, 3L);

   static_assert( std::is_same<decltype(f1), decltype(f2)>{}, "!" );
 }

A little improvement: if you also define the following using template inside function_traits

template <std::size_t I>
using a_type = std::tuple_element_t<I, tuple_type>;

the helper function can be simplified as follows

template <typename Func, std::size_t ... Is>
constexpr auto l2f_helper (Func lambda,
                              std::index_sequence<Is...> const &)
 {
   return static_cast<std::function<
      typename function_traits<Func>::result_type(
         typename function_traits<Func>::template a_type<Is>...)>>(lambda);
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • This is exactly what I was searching for, specially the second part, thank you very much max66. Indeed, it's quite ugly, and complex. Just out of curiosity, do you know if it is possible to have the std::make_index_sequence give the index_sequence in the same function? (instead of having to have a helper function to receive the indices) – Diogo Parreira Mar 01 '18 at 00:03
  • 1
    @DiogoParreira - not really complex but, yes: very ugly. And no: I don't know how pass from `std::make_index_sequence` to the corresponding `std::index_sequence` avoiding the helper function. If you find the way, please, say it to me: there are years that I'm looking for it. – max66 Mar 01 '18 at 02:08