为具有可变数量参数的不同函数编写通用包装器

Posted

技术标签:

【中文标题】为具有可变数量参数的不同函数编写通用包装器【英文标题】:Writing a generalized wrapper for different functions with variable number of arguments 【发布时间】:2012-07-21 06:32:45 【问题描述】:

我想为一堆功能相似的函数编写一个 C++ 包装器。但他们都有不同数量的论点。例如(假设 typeA、typeB 等是不同的 typedef 类型):

typeA func1 (typeB b, typeC c);

typeA func2 (typeB b, typeD d, typeE e);

这些是我想要为其编写包装器的函数(请注意,它们都有返回类型 typeA 和类型 B 的第一个参数)。所以我打算创建一个通用包装类并在构造函数中存储一个参数列表。我希望能够在下面调用一个看起来像“func”的函数:

class wrapper 
  public:
    wrapper (/*args - a list of arguments, including 
               a pointer to the actual function, and 
               the remaining arguments depending 
               on the function. */);
    void func (typeB b, typeA &a);

所以,我想以这样一种方式编写 func,它可以调用任何函数(func1 或 func2),并将参数传递给构造函数。

我想知道是否有一种方法可以将 func1 或 func2 视为具有可变数量的参数。或者至少,如果在 C++ 中有一种方法,我可以输入一个函数和一个参数列表并获取 eval (function, argument_list)。

另一种方法是为每个函数编写一个包装器,我想避免但不反对。 (另外,我不介意解决方案是否涉及我不使用包装器类而是包装器函数)

【问题讨论】:

你熟悉 std::function,什么编译器和版本?提升可用吗? 你想用你的包装器完成什么?我不明白为什么要包装任何东西。 @Zac,我查看了 std::function。似乎我仍然需要指定确切数量的输入参数才能将其与另一个函数一起使用。我在 ubuntu 上工作。我在终端上运行了 g++ --version,它告诉我 4.6.1。是的。升压可用。我听说它可能对我想做的事情有用。 @Adam,我基本上有一堆以不同方式过滤点云的函数。我想用通用包装器将它们全部包装起来,这样它们在调用时看起来都一样。 (有代码要求以特定格式调用它们)。 目前还不清楚你想要什么。您提到的函数采用截然不同的参数,但您需要一个看似采用这些参数的子集并为两个函数提供通用接口的包装器。您描述的类似 Python 的 eval 在像 C++ 这样的编译语言中是不可能的,但绝对可以编写您想要的包装器。但我不清楚这是否值得,因为在我看来,包装器就像例程本身一样。 【参考方案1】:

这是我在 C 中所做的,用于包装任何函数。我希望它能帮助/启发你。 主要限制是您只能包装用户定义的函数,因为您必须修改函数的签名。

我的解决方案使用 va_arg 结构。

#include <stdarg.h>
void * super_wrapper( void * (*fn)(), void * ret, int n, ...)

  va_list = my_list;
  va_start(my_list, n);
  ret = (*fn)(ret, n, my_list);
  va_end(my_list);
  return ret

包装器的签名接受指向您要执行的函数的指针,给函数以检索您的返回值的指针,您调用函数的参数数量,然后是函数的参数你想打电话。

va_list 是一个参数列表。 你用 va_start 初始化它。 va_start 的第一个参数是要初始化的 va_list。第二个参数是当前函数的最后一个已知参数(在本例中为“n”)。

现在,您将放置一个参数 va_list,而不是用户定义函数中的省略号。

我有两个例子:

double sum (int n, ...)

  int i;
  double res = 0;
  double tmp;
  va_list = my_list;
  va_start(my_list, n);
  for(i=0; i<n; i++)
  
    tmp = va_arg(my_list, double);
    res += tmp;
  
  va_end(my_list);
  return res;


void my_print(int n, ...)

  int i;
  va_list my_list;
  va_start(my_list, n);
  char * tmp;
  for(i=0; i<n; i++)
  
    tmp = va_arg(my_list, char *);
    printf("%s ", tmp);
  
  printf("\n");
  va_end(my_list);

我设法使用相同的包装器来调用这两个不同的函数,并在签名和函数体中进行了一些修改。 问题是在参数和返回中,我只给出指针。所以我必须取消引用指针。

void * sum (void * ret, int n, va_list my_list)

  int i;
  double res = 0;
  double *tmp;
  for(i=0; i<n; i++)
  
    tmp = va_arg(my_list, double);
    res += *tmp;
  
  ret = &res;
  return ret;


void* my_print(int n, ...)

  int i;
  char ** tmp;
  for(i=0; i<n; i++)
  
    tmp = va_arg(my_list, char **);
    printf("%s ", *tmp);
  
  printf("\n");
  return ret;

现在我可以在我的 main 中使用相同的包装函数来调用这两个函数:

void main()

  double two = 2.0;
  double three = 3.0;
  double fifteen = 15.0;

  char *I, *am, *a, *great, *wrapper;
  I = malloc(sizeof(char) * 2);
  am = malloc(sizeof(char) * 3);
  a = malloc(sizeof(char)*2);
  great = malloc(sizeof(char) * 6;
  wrapper = malloc(sizeof(char) * 8);

  I = strcpy(I, "I\0");
  am = strcpy(am, "am\0");
  a = strcpy(a,"a\0");
  great = strcpy(great, "great\0");
  wrapper = strcpy(wrapper, "wrapper\0");

  void * ret;

  printf("Sum = %f\n", *( (double*)super_wrapper(sum, ret, 3, &two, &three, &fifteen) ) );
  super_wrapper(my_print, ret, 5, &I, &am, &a, &great, &wrapper);

【讨论】:

以上是关于为具有可变数量参数的不同函数编写通用包装器的主要内容,如果未能解决你的问题,请参考以下文章

如何将 std::function 包装器转换为可变参数函数?

透明地通过带有可变参数列表的函数

传递给通用函数包装器(如 _.debounce)的函数的类型推断

可变参数宏包装器,扩展为使用与参数数量相对应的字符格式化字符串

使用可变参数模板函数围绕类实现基于 pImpl 的包装器

如何创建一个可变的通用lambda?