如何使用模板将变量传递到具有任何签名的函数中?

时间:2015-08-30 13:18:26

标签: c++ templates compiler-errors c++14

我的问题如下。我想在包装器中调用任何自由函数,我想在包装器中自动传递它。

这个想法是通过GetArgument<float>()之类的函数调用传递参数。最终,我想从虚拟机中获取值,并将它们传递给我绑定的函数。我需要将正确类型的正确GetArgument<T>放在函数调用中的正确位置。

这是我尝试的最小工作示例。主:

#include "FunctionWrapper.h"
#include <iostream>

void say( int val ) {
    std::cout << "called with " << val << std::endl;
}

int main() {
    InvokeWithArguments( say );

    return 0;
}

这就是魔术发生的地方。我在源代码末尾遇到编译器错误:

#pragma once

#include <cstdlib>
#include <cstdint>
#include <tuple>
#include <type_traits>

/*
 * FUNCTION TRAITS
 */
template< typename F >
struct FunctionTraits;

template< typename R, typename... Args >
struct FunctionTraits< R( Args... ) > {
    using ReturnType = R;

    constexpr static const uint32_t arity = sizeof...( Args );

    template< std::size_t N >
    struct Argument {
        static_assert( N < arity, "FunctionTraits error: invalid argument count parameter" );
        using type = typename std::tuple_element< N, std::tuple< Args... > >::type;
    };
};

/*
 * ARGUMENT GETTER (for demonstration)
 **/
template< typename T >
T GetArgument() {}

template<>
float GetArgument() {
    return 3.3f;
}

template<>
int GetArgument() {
    return 5;
}

/*
 * AUTOMATIC INVOCATION
 **/
template< typename Function, std::size_t... index >
decltype( auto ) InvokeHelper( Function&& f, std::index_sequence<index...> ) {
    using Traits = FunctionTraits< decltype(f) >;
    // COMPILER FAILS HERE, EXPECTS ) BEFORE :: TOKEN
    return f( GetArgument< Traits::Argument<index>::type >()... );
}

template< typename Function >
decltype( auto ) InvokeWithArguments( Function&& f ) {
    constexpr auto Arity = FunctionTraits< decltype(f) >::arity;
    return InvokeHelper( std::forward<Function>( f ), std::make_index_sequence<Arity>{} );
}

我不明白为什么return f( GetArgument< Traits::Argument<index>::type >()... );失败了。据我所知,Traits::Argument<index>::type是一个类型,所以我不知道为什么编译器会期望在它中间关闭函数调用。

最后,一个小小的理智检查,因为我是这样的模板编程菜鸟。我希望在函数调用的括号之间有一个逗号分隔的GetArgument<T>调用列表。这甚至是我的代码在做什么?

1 个答案:

答案 0 :(得分:4)

您应该使用:

return f( GetArgument< typename Traits::template Argument<index>::type >()... );
//                     ^^^^^^^^         ^^^^^^^^^

在此之后,编译器会抱怨您的FunctionTraits<void(&)(int)>无法实例化,可以通过std::remove_reference删除函数类型中的引用来解决此问题。

这是因为void(int)void(&)(int)不同而void(*)(int)不同。第一个是函数类型,第二个是函数引用类型,后者是函数指针类型。

所有这些都将产生:

template< typename Function, std::size_t... index >
decltype( auto ) InvokeHelper( Function&& f, std::index_sequence<index...> ) {
    using Traits = FunctionTraits< typename std::remove_reference<decltype(f)>::type >;
    //                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^           ^^^^^^^
    return f( GetArgument< typename Traits::template Argument<index>::type >()... );
    //                     ^^^^^^^^         ^^^^^^^^^
}

template< typename Function >
decltype( auto ) InvokeWithArguments( Function&& f ) {
    constexpr auto Arity = FunctionTraits< typename std::remove_reference<decltype(f)>::type >::arity;
    //                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^           ^^^^^^^
    return InvokeHelper( std::forward<Function>( f ), std::make_index_sequence<Arity>{} );
}

Live demo