获取C++函数参数的类型

cpjpxq1n  于 2023-05-20  发布在  其他
关注(0)|答案(6)|浏览(191)

有没有一种标准的方法来获取函数参数的类型,并将这些类型作为模板参数包传递?我知道这在C中是可能的,因为it has been done before
我希望在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来获取参数类型的名称。但是,我想要实际的类型。不是那些类型的名字。

gpnt7bae

gpnt7bae1#

此语法略有不同。
首先,因为类型比包更容易使用,包是一个包含包的类型。using type=types;只是在生成types的代码中节省了我的工作:

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

这是一匹马。它接受一个签名,并生成一个包含签名参数的types<?...>包。3个步骤,这样我们就可以得到干净的C++14风格的语法:

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,而是基于fopentype。如果你有一个指针,你需要做decltype(*func_ptr)或类似的事情。或者我们可以增加顶部来处理R(*)(Args...),以便于用途:

template<class Sig>
struct args<Sig*>:args<Sig>{}; // R(*)(Args...) case
template<class Sig>
struct args<Sig&>:args<Sig>{}; // R(&)(Args...) case

测试代码:

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

live example
注意,这不适用于重载函数,也不适用于函数对象。
总的来说,这种事情是一个坏主意,因为通常你知道你是如何与对象交互的。
只有当你想获取一个函数(或函数指针),并从某个堆栈中弹出一些参数,然后根据它所期望的参数调用它,或者类似的事情时,上面的方法才有用。

koaltpgm

koaltpgm2#

受@Yakk的启发,这里有一个稍微简化的版本:
1.首先,我们定义了一个帮助 meta函数来将函数的类型存储为元组。

template<typename Sig>
struct signature;

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

1.我们使用概念来限制输入作为函数

template<typename F>
concept is_fun = std::is_function_v<F>;

1.下面是我们的函数“arguments”,用于检索输入的参数类型。依赖于输入参数,我们重载“arguments”函数来接受引用和非引用。(自由函数总是通过引用传递。我们甚至不需要函数体,只要返回类型就足够了,因为这是 meta函数。

template<is_fun F>
auto arguments(const F &) -> typename signature<F>::type;

1.下面是测试:

void foo(const string &, int, double)
{}

static_assert(std::is_same_v<decltype (arguments(foo)), 
                             std::tuple<const string &, int, double>>);

我的完整版本是here which also supports lambda, functor, member function pointer

jv4diomz

jv4diomz3#

使用Boost.FunctionTypesstd::index_sequence。下面是一个打印函数func的参数类型的示例。您可以更改doit静态函数来执行您想要的操作。在行动中看到它here

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;
xu3bshqb

xu3bshqb4#

对于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);

Boost Document

tvz2xvvm

tvz2xvvm5#

使用符合C++17(或更高版本)的编译器,您可以使用以下命令:

#include<iostream>
    template<typename type, typename...args>
    void getFuncInfo(type(*func)(args...))
    {
        // some code here...
        // here my example:
        ((std::cout << typeid(args).name() << "\n"),...);
    }

    // every Augments you can imagines...
    void someRandomFunction(int a, float b, double c, const char* d, int e[], std::pair<int, const char*> f)
    {
    
    }

    // test out in main.
    int main()
    {
        getFuncInfo(someRandomFunction);
    
        std::cin.get();
    }
vs91vp4v

vs91vp4v6#

几年后,但看到我的完整解决方案here(生产级,完全文档化)。例如,想要某个函数“F”的第二个参数(第二个模板参数是从零开始的):

using Arg2Type_t = ArgType_t<F, 1>;

希望其用户友好的名称为字符串(std::basic_string_view):

constexpr auto Arg2TypeName = ArgTypeName_v<F, 1>;

根据你最初的问题想要它的所有(非变量)参数(尽管通常很少有人需要直接访问它)。还有一个函数可以循环遍历它们,并在每个arg类型上调用您自己的functor(请参阅上面链接中的“循环遍历所有函数参数”):

using ArgTypes = ArgTypes_t<F>;

其中包括参数计数、返回类型、非静态成员函数上的cv限定符等。
请注意,“F”可以是任何原始C函数类型,函数类型指针,函数类型引用(不包括对C中不法律的的非静态成员函数的引用),函数类型指针引用和仿函数类型(包括lambda)。

相关问题