Practical C++ Metaprogramming
Asked Answered
P

2

8

I just read the book "Practical C++ Metaprogramming" and it has the following example that I cannot compile. Can you help sort this out for me.

template <typename F>
struct make_tuple_of_params;

template <typename Ret, typename... Args>
struct make_tuple_of_params<Ret (Args...)>
{
   using type = std::tuple<Args...>;
};

template <typename F>
using make_tuple_of_params_t = typename make_tuple_of_params<F>::type;

template<typename F>
void some_magic_function(F callable)
{
   make_tuple_of_params_t<F> tuple;
   /*
    ... do something with arguments in tuple...
   */
}

int main()
{
   some_magic_function([] (int, double, float) {});
}

I get a compilation error saying: 'type' is not a member of any direct or indirect base class of 'make_tuple_of_params'. It seams like the SFINAE does not work as expected since the default struct is selected. How do I fix this?

Poaceous answered 29/11, 2016 at 14:21 Comment(3)
A lambda type is not a function type.Postern
Have in mind that lambdas in c++14 can have auto parameters which can obviously be perceived as templated functors. For this kind of lambdas what would you expect your tuple type look alike?Bilharziasis
There is no SFINAE above, just specialization template pattern matchingForayer
B
5

For lambdas if not including lambdas with auto parameters the workaround could look as follows:

#include <tuple>
#include <typeinfo>
#include <iostream>

template <class>
struct make_tuple_of_params;

template <class Res, class Type, class... Args>
struct make_tuple_of_params<Res (Type::*)(Args...) const> {
    using type = std::tuple<Args...>;
};

template <class F>
using make_tuple_of_params_t = typename make_tuple_of_params<F>::type;

template<typename F>
void some_magic_function(F callable)
{
   make_tuple_of_params_t<decltype(&F::operator())> tuple;
   std::cout << typeid(tuple).name() << std::endl;
}

int main()
{
   some_magic_function([] (int, double, float) {});
}

[live demo]

Bilharziasis answered 29/11, 2016 at 14:52 Comment(0)
M
6

The type of [] (int, double, float) {} is an unnamed class type local to main, called the closure type. It is most definitely not void (int, double, float); it is in fact not a function type at all. Therefore, the specialisation for function types doesn't apply, and the primary template is selected. (Note that no SFINAE is involved in your code).

As for how to fix this: I don't think there's a fully general solution. There could be a solution/workaround for a particular some_magic_function, but that would depend on what you need that function to do.

Manwell answered 29/11, 2016 at 14:24 Comment(2)
Regarding potential fixes: Turning the lambda into a function pointer with +[] (int, double, float) {} could be an option if nothing needs to be captured. Of course, make_tuple_of_params must match function pointers and function types then.Wyant
@Poaceous That is covered in the second sentence of Markus's comment.Forayer
B
5

For lambdas if not including lambdas with auto parameters the workaround could look as follows:

#include <tuple>
#include <typeinfo>
#include <iostream>

template <class>
struct make_tuple_of_params;

template <class Res, class Type, class... Args>
struct make_tuple_of_params<Res (Type::*)(Args...) const> {
    using type = std::tuple<Args...>;
};

template <class F>
using make_tuple_of_params_t = typename make_tuple_of_params<F>::type;

template<typename F>
void some_magic_function(F callable)
{
   make_tuple_of_params_t<decltype(&F::operator())> tuple;
   std::cout << typeid(tuple).name() << std::endl;
}

int main()
{
   some_magic_function([] (int, double, float) {});
}

[live demo]

Bilharziasis answered 29/11, 2016 at 14:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.