简体   繁体   中英

stuck with C++ metaprogramming

I'm trying to get into TMP with a book called Practical C++ Metaprogramming and I'm stuck at trying to derive one of the first examples...

My objective is to make a templated function call taking a function with multiple parameters of unrelated type and then, to return the computation of that function with the parameters.

What seems to be the root of the problem is that I'm unable to instantiate a struct (the good specialization of makeTupleOfParams ) with the appropriate parameter types that would allow me to define the type of the input function parameters, tupleOfParamTypes_t in my code.

I don't understand why this is the problem because the Function signature in the build output seems equivocal enough to identify the types of the arguments with the specialization <Return(Args...)> . I don't get why this struct would not be generated with the correct member using type = std::tuple<Args...>;which seems to be the key to all this.

Here's the whole thing:

#include <tuple>

// base template
template<typename Function>
struct makeTupleOfParams;

// template specialization to identify parameters
template <typename Return, typename... Params>
struct makeTupleOfParams<Return(Params...)>
{
    using type = std::tuple<Params...>;
};

// shortcut to identify type of parameters
template <typename Function>
using tupleOfParamTypes_t = typename makeTupleOfParams<Function>::type;

// function to unroll a tuple of parameters on a function
template <typename Function, typename Params, size_t... Index>
auto dispatchParams(Function f, Params p, std::index_sequence<Index...>)
{
    return f(std::get<Index>(p)...);
}

template <typename Function, typename... Params>
auto call(Function f, Params... p)
{
    // getting size of Params and argument types of Function
    constexpr size_t paramsCount = sizeof...(Params);
    tupleOfParamTypes_t<Function> params;

    return dispatchParams(f, params, std::make_index_sequence<paramsCount>());
}

auto f(int i, float j) -> decltype(i+j)
{
    return i + j;
};

void main()
{
    call(f, 1, 2.0);
}

And the build output:

1 > ------Build started : Project: TMP, Configuration : Debug Win32------
1 > main.cpp
1 > d:\git\meta - cpp - sandbox\src\main.cpp(40) : warning C4326 : return type of 'main' should be 'int' instead of 'void'
1 > d:\git\meta - cpp - sandbox\src\main.cpp(16) : error C2794 : 'type' : is not a member of any direct or indirect base class of 'makeTupleOfParams<Function>'
1 >     with
1 >     [
1 >         Function = float(__cdecl *)(int, float)
1 >     ]
1 > d:\git\meta - cpp - sandbox\src\main.cpp(28) : note: see reference to alias template instantiation 'tupleOfParamTypes_t<float(__cdecl *)(int,float)>' being compiled
1 > d:\git\meta - cpp - sandbox\src\main.cpp(41) : note: see reference to function template instantiation 'auto call<float(__cdecl *)(int,float),int,double>(Function,int,double)' being compiled
1 >     with
1 >     [
1 >         Function = float(__cdecl *)(int, float)
1 >     ]
1 > d:\git\meta - cpp - sandbox\src\main.cpp(26) : error C2938 : 'tupleOfParamTypes_t' : Failed to specialize alias template
1 > d:\git\meta - cpp - sandbox\src\main.cpp(31) : error C2672 : 'dispatchParams' : no matching overloaded function found
1 > d:\git\meta - cpp - sandbox\src\main.cpp(26) : error C2893 : Failed to specialize function template 'auto dispatchParams(Function,Params,std::integer_sequence<unsigned int,_Ix...>)'
1 > d:\git\meta - cpp - sandbox\src\main.cpp(26) : note: With the following template arguments :
1 > d:\git\meta - cpp - sandbox\src\main.cpp(26) : note: 'Function=float (__cdecl *)(int,float)'
1 > d:\git\meta - cpp - sandbox\src\main.cpp(26) : note: 'Params=unknown-type'
1 > d:\git\meta - cpp - sandbox\src\main.cpp(26) : note: 'Index={0, 1}'
1 > Done building project "TMP.vcxproj" --FAILED.
========== Build: 0 succeeded, 1 failed, 0 up - to - date, 0 skipped ==========

When you call your call function, you pass the function f as an argument. However c++ implicitly converts the argument to a pointer-to-function. Therefore, when the type-alias is being constructed, the template parameter is actually int(*)(int,float) , not int(int,float) . Since this does not meet the requirements of the partially specialised template, the compiler attempts to construct the type-alias from the non-specialised template for makeTupleOfParams. However the non-sepcialised template does not contain the type-alias "type", which results in a compile-error.

To solve, modify the partially-specialised template to:

template<typename Return, typename... Params>
struct makeTupleOfParams<Return(*)(Params...)> //pointer-to-function specialisation
{
    using type = std::tuple<Params...>;
};

Another solution is to use type_traits to remove the pointer trait within the call function, like so:

#include<type_traits>

template <typename Function, typename... Params>
auto call(Function f, Params... p)
{
    // getting size of Params and argument types of Function
    constexpr size_t paramsCount = sizeof...(Params);
    // modify function type to remove pointer trait
    tupleOfParamTypes_t<std::remove_pointer_t<Function>> params;

    return dispatchParams(f, params, std::make_index_sequence<paramsCount>());
}

In this case, the template parameter passed to tupleOfParamTypes_t will be int(int,float) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM