在 C 中使用默认参数包装 C++ 函数

问题描述

假设我在 C++ 头文件中定义了一个这样的函数

namespace foo {
    void bar(int a,int b = 1);
}

我想在 C 代码中使用这个函数一个明显的解决方案是定义两个这样的函数

void foo_bar_1(int a)
{ foo::bar(a,1); }

void foo_bar_2(int a,int b)
{ foo::bar(a,b); }

这些可以很容易地包含在 C 代码中。但是,对于多个认参数,这会变得很丑陋,拥有单个包装函数会更好。我想过做这样的事情:

#define _test_foo_numargs(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))

#define test_foo(...) do { \
  if (_test_foo_numargs(__VA_ARGS__) == 1) \
    test_foo_1(__VA_ARGS__); \
  else if (_test_foo_numargs(__VA_ARGS__) == 2) \
    test_foo_2(__VA_ARGS__); \
} while (0)

但这不起作用,因为对 test_foo_1test_foo_2调用都必须有效才能编译。

有没有更好的方法来做到这一点?

解决方法

我会在这里提供我自己的解决方案,以防没有人有更好的解决方案,并且将来有人遇到同样的问题:

#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>

int _test_foo_1(int a)
{ return a; }

int _test_foo_2(int a,int b)
{ return a + b; }

int _test_foo_va(size_t num_args,...)
{
  va_list args;
  va_start(args,num_args);

  switch (num_args) {
  case 1:
    return _test_foo_1(va_arg(args,int));
  case 2:
    return _test_foo_2(va_arg(args,int),va_arg(args,int));
  }

  va_end(args);
}

#define _test_foo_numargs(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))

#define test_foo(...) _test_foo_va(_test_foo_numargs(__VA_ARGS__),__VA_ARGS__)

int main()
{
  printf("%d\n",test_foo(1));
  printf("%d\n",test_foo(1,2));
}

这当然很不安全,因为如果传递的参数太少或太多,它都会编译。

,

您可以执行以下操作:

#define TAKE_9(_1,_2,_3,_4,_5,_6,_7,_8,_9,...) _9
#define COUNT(...) TAKE_9(__VA_ARGS__,8,7,6,5,4,3,2,1)

#define CONCAT_(A,B) A ## B
#define CONCAT(A,B) CONCAT_(A,B)

#define SELECT(NAME,...) CONCAT(NAME,COUNT(__VA_ARGS__))(__VA_ARGS__)

#define foo_bar(...) SELECT(foo_bar_,__VA_ARGS__)

Demo

COUNT 可以升级为处理 0 参数,如果您的编译器支持它

#define COUNT(...) TAKE_9(__VA_ARGS__ __VA_OPT__(,) 8,1,0)

Demo

为了避免不得不写 foo_bar_N,你可以这样做:

// ...
#define FUNC_WITH_DEFAULT_ARG_2(func,DEFAULT,...) TAKE_3(__VA_ARGS__ __VA_OPT__(,) \
                                                 func(__VA_ARGS__),\
                                                 func(__VA_ARGS__,TAKE_2 DEFAULT),\
                                                 func(TAKE_1 DEFAULT,TAKE_2 DEFAULT) )

#define FUNC_WITH_DEFAULT_ARG_3(func,...) TAKE_4(__VA_ARGS__ __VA_OPT__(,TAKE_2 DEFAULT,TAKE_3 DEFAULT),TAKE_3 DEFAULT) )

// Choose on or other,error message for misuse might differ
// RequiredParameter is just a name for "better" error message when not enough parameter are given
#define foo_bar(...) FUNC_WITH_DEFAULT_ARG_2(foo_bar_impl,(RequiredParameter,1),__VA_ARGS__)
#define foo_bar2(_1,...) FUNC_WITH_DEFAULT_ARG_3(foo_bar_impl,(_1,0),__VA_ARGS__)

void foo_bar_impl(int a,int b)
{ foo::bar(a,b); }

Demo

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...