23

是否有标准方法来获取函数参数的类型并将这些类型作为模板参数包传递?我知道这在 C++ 中是可能的,因为它之前已经完成

我希望使用 C++14 或即将推出的 C++1z,这里会有一种惯用的方式来实现arg_types<F>...

template <typename ...Params>
void some_function(); // Params = const char* and const char*

FILE* fopen(const char* restrict filename, const char* restrict mode);

int main(){
    some_function<arg_types<fopen>...>();
}

需要明确的是,声称没有标准方法可以做到这一点的答案不是答案。如果没有答案,我希望这个问题一直没有答案,直到解决方案添加到 C++500 或直到宇宙的热寂,以较早发生的为准:)

编辑:已删除的答案指出我可以PRETTY_FUNCTION用来获取参数类型的名称。但是,我想要实际的类型。不是这些类型的名称。

4

4 回答 4

24

这种语法略有不同。

首先,因为类型比包含包的类型更容易使用。using type=types;只是节省了我在生成的代码中的工作types

template<class...>struct types{using type=types;};

这里是主力。它接受一个签名,并生成一个types<?...>包含签名参数的包。3 个步骤,我们可以获得干净的 C++14esque 语法:

template<class Sig> struct args;
template<class R, class...Args>
struct args<R(Args...)>:types<Args...>{};
template<class Sig> using args_t=typename args<Sig>::type;

这是语法差异。我们不是直接取Params...,而是取一个types<Params...>. 这类似于“标签调度”模式,我们利用模板函数类型推导将参数移动到类型列表中:

template <class...Params>
void some_function(types<Params...>) {
}

fopen的不同,因为我不想打扰#include

void* fopen(const char* filename, const char* mode);

并且语法不是基于的,fopen而是基于的类型fopen如果你有一个指针,你需要做decltype(*func_ptr)或类似的事情。或者我们可以增加顶部以R(*)(Args...)方便使用:

int main(){
  some_function(args_t<decltype(fopen)>{});
}

活生生的例子

请注意,这不适用于重载函数,也不适用于函数对象。

一般来说,这种事情是个坏主意,因为通常你知道你是如何与一个对象交互的。

如果您想获取一个函数(或函数指针)并将一些参数从某个堆栈中弹出某个位置并根据预期的参数或类似的参数调用它,上述内容才会有用。

于 2015-02-13T22:05:44.297 回答
4

使用Boost.FunctionTypesstd::index_sequence. 下面是一个打印函数参数类型的示例func。您可以更改doit静态函数以执行您想要的操作。在这里查看它的实际应用。

template <typename FuncType>
using Arity = boost::function_types::function_arity<FuncType>;

template <typename FuncType>
using ResultType = typename boost::function_types::result_type<FuncType>::type;

template <typename FuncType, size_t ArgIndex>
using ArgType = typename boost::mpl::at_c<boost::function_types::parameter_types<FuncType>, ArgIndex>::type;

void func(int, char, double) {}

template <typename Func, typename IndexSeq>
struct ArgPrintHelper;

template <typename Func, size_t... Inds>
struct ArgPrintHelper<Func, integer_sequence<size_t, Inds...> >
{
  static void doit()
  {
    string typeNames[] = {typeid(ResultType<Arg>).name(), typeid(ArgType<Func, Inds>).name()...};
    for (auto const& name : typeNames)
      cout << name << " ";
    cout << endl;
  }
};

template <typename Func>
void ArgPrinter(Func f)
{
  ArgPrintHelper<Func, make_index_sequence<Arity<Func>::value> >::doit();
}

int main()
{
  ArgPrinter(func);
  return 0;
}

标头(移到此处以减少上述代码片段中的噪音):

#include <boost/function_types/function_type.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/function_types/function_arity.hpp>

#include <algorithm>
#include <iostream>
#include <string>
#include <type_traits>
#include <typeinfo>
#include <tuple>
#include <utility>
using namespace std;
于 2015-02-13T22:38:38.980 回答
1

对于boost用户,#include <boost/type_traits.hpp>

boost::function_traits<decltype(function)>::arg1_type
boost::function_traits<decltype(function)>::arg2_type
// boost::function_traits<decltype(function)>::argN_type

using FopenArg1 = boost::function_traits<decltype(fopen)>::arg1_type;
using FopenArg2 = boost::function_traits<decltype(fopen)>::arg2_type;
void some_function(FopenArg1, FopenArg2);

升压文件

于 2021-10-11T04:52:38.527 回答
0

受@Yakk 启发,这里有一个稍微简化的版本:

  1. 首先,我们定义辅助元函数以将函数参数类型存储为元组。

    
    template<typename Sig>
    struct signature;
    template<typename Ret, typename...Args>{
        using type=tuple<Args...>;
    };
    
  2. 我们使用概念来限制输入作为函数

    
    template
    concept is_fun=is_function_v<Fun>;
    
  3. 这是我们用于检索输入参数类型的函数“参数”。取决于输入参数,我们重载“参数”函数以接受引用和非引用。(自由函数总是通过引用传递。我们甚至不必有函数体,只要返回类型就足够了,因为这是元函数。

    
    template<is_fun T> 
    auto arguments(const T& t)->signature<T>::type;
    
  4. 这是测试:

      
    void foo(const string&, int, double){}
    static_assert(is_same_v<decltype(arguments(foo)),tuple<const string&, int, double>>);
    

我的完整版本在这里,它还支持 lambda、仿函数、成员函数指针

于 2022-02-02T11:21:06.550 回答