c - 在 C/C++ 宏中通过 __VA_ARGS__ 迭代添加分隔符

标签 c lua metaprogramming boilerplate variadic-macros

背景

我正在尝试使用 C 宏自动生成 Lua-C 接口(interface)。最大的问题是让它对不同数量的参数通用,我在这个答案的帮助下使用 __VA_ARGS__ 解决了这个问题:Is it possible to iterate over arguments in variadic macros?

更简单几乎可行的解决方案

这个解决方案几乎可以工作,但它会产生一些多余的逗号(注意输出中的 ,,,,)

// helper macros for iteration over __VA_ARGS__
#define ARG1(WHAT,X,...) WHAT(X)ARG2(WHAT,__VA_ARGS__)
#define ARG2(WHAT,X,...) WHAT(X)ARG3(WHAT,__VA_ARGS__)
#define ARG3(WHAT,X,...) WHAT(X)ARG4(WHAT,__VA_ARGS__)
#define ARG4(WHAT,X,...) WHAT(X)ARG5(WHAT,__VA_ARGS__)
#define ARG5(WHAT,X,...) WHAT(X)ARG6(WHAT,__VA_ARGS__)
#define ARG6(WHAT,X,...) WHAT(X)//ARG2(__VA_ARGS__)

// macros dispatch propper type of Lua::get
#define LUA_GET_int(i)     Lua::getInt(L,i)
#define LUA_GET_long(i)    Lua::getInt(L,i)
#define LUA_GET_float(i)   (float)Lua::getDouble(L,i)
#define LUA_GET_double(i)  Lua::getDouble(L,i)
#define LUA_GET_string(i)  Lua::getString(L,i)

#define LUA_PUSH_int(a)    lua_pushnumber(L,a)
#define LUA_PUSH_float(a)  lua_pushnumber(L,a)
#define LUA_PUSH_double(a) lua_pushnumber(L,a)
#define LUA_PUSH_float(a)  lua_pushstring(L,a)

#define LUA_GET_(T)
#define LUA_GET(T) ,LUA_GET_##T(i++) // commas come from here
#define MAKE_LUA_FUNC(TR,fname,T1,...) int l_##fname(lua_State * L){ int i=0; LUA_PUSH_##TR( fname( LUA_GET_##T1(i++) ARG1(LUA_GET,__VA_ARGS__) ) ); return 1; }

// interface for function:
// double add3(float, int, double );
MAKE_LUA_FUNC( double, add3, float, int, double )
// output:
// 'int l_add3(lua_State * L){ int i=0; lua_pushnumber(L,add3((float)Lua::getDouble(L,i++) ,Lua::getInt(L,i++),Lua::getDouble(L,i++),,,, )); return 1; }'

可行但不太好的解决方案

我必须复制 LUA_GET_ 宏,以防它在参数列表中位于第一个(没有逗号),否则(前面有逗号)

// begin of argument list => no commas
#define LUA_GET_int(i)     Lua::getInt(L,i)
#define LUA_GET_long(i)    Lua::getInt(L,i)
#define LUA_GET_float(i)   (float)Lua::getDouble(L,i)
#define LUA_GET_double(i)  Lua::getDouble(L,i)
#define LUA_GET_string(i)  Lua::getString(L,i)

// rest of argument list => with commas
#define LUA_GET__int(i)     ,Lua::getInt(L,i)
#define LUA_GET__long(i)    ,Lua::getInt(L,i)
#define LUA_GET__float(i)   ,(float)Lua::getDouble(L,i)
#define LUA_GET__double(i)  ,Lua::getDouble(L,i)
#define LUA_GET__string(i)  ,Lua::getString(L,i)

#define LUA_PUSH_int(a)    lua_pushnumber(L,a)
#define LUA_PUSH_float(a)  lua_pushnumber(L,a)
#define LUA_PUSH_double(a) lua_pushnumber(L,a)
#define LUA_PUSH_float(a)  lua_pushstring(L,a)

#define LUA_GET_(T)
#define LUA_GET__(T)
#define LUA_GET(T) LUA_GET__##T(i++)
#define MAKE_LUA_FUNC(TR,fname,T1,...) int l_##fname(lua_State * L){ int i=0; LUA_PUSH_##TR( fname( LUA_GET_##T1(i++) ARG1(LUA_GET,__VA_ARGS__) ) ); return 1; }

// MAKE_LUA_FUNC( double, add3, float, int, double )
// output:
// int l_add3(lua_State * L){ int i=0; lua_pushnumber(L,add3( (float)Lua::getDouble(L,i++) ,Lua::getInt(L,i++),Lua::getDouble(L,i++) )); return 1; }

是否可以让它更简单/更好?

注意 - 对于调试,我发现这个 Seeing expanded C macros 非常有用特别是https://stackoverflow.com/a/31460434/1291544

最佳答案

你需要统计你有多少个参数,然后调用相应的ARG#宏。

#define ARGS_N(M,...) \
ARGS_N__(__VA_ARGS__, 6, 5, 4, 3, 2, 1)(M, __VA_ARGS__)

#define ARGS_N__(_1, _2, _3, _4, _5, _6, X, ...) ARGS_##X

#define ARGS_1(M, X)      M(X)
#define ARGS_2(M, X, ...) M(X)ARGS_1(M, __VA_ARGS__)
#define ARGS_3(M, X, ...) M(X)ARGS_2(M, __VA_ARGS__)
#define ARGS_4(M, X, ...) M(X)ARGS_3(M, __VA_ARGS__)
#define ARGS_5(M, X, ...) M(X)ARGS_4(M, __VA_ARGS__)
#define ARGS_6(M, X, ...) M(X)ARGS_5(M, __VA_ARGS__)

现在,将 MAKE_LUA_FUNC 更改为调用 ARGS_N 而不是您的 ARG1


计数技术的工作方式是 ARGS_N 使用可变参数调用帮助器 ARGS_N__,然后用其他参数填充调用。 ARGS_N__ 通过始终使用第 7 个参数进行计数。因此,如果 ARGS_N 在第一个参数之后提供了 4 个变量参数,ARGS_N__ 将生成 ARGS_4,因为在这种情况下,在由ARGS_N4 将是第 7 个参数。

ARGS_N__(__VA_ARGS__, 6, 5, 4, 3, 2, 1)(M, __VA_ARGS__)
         .                  .
        /|\                /|\
         |                  |
         If this has 4 arguments
                            |
                            This would be the 7th argument

这与您指向的答案中显示的技术相同。但是,该版本比我为您说明的版本稍微复杂一些,因此希望您会发现此解释对您有所帮助。

关于c - 在 C/C++ 宏中通过 __VA_ARGS__ 迭代添加分隔符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50280326/

相关文章:

c - 是否有必要在 select 系统调用之间重置 fd_set?

Lua内存不足

ruby - 需要帮助来理解这段代码

c - gdb 脚本根据变量值隔离线程

c - 如何制作 fscanf,扫描单个单词而不是整行

c - C中的线程,如何使用它们来移动玩家?

Nginx lua日志位置

使用 require 和 dofile 将 lua5.2 文件编译成一个文件

c++ - 我可以使用 C/C++ 预处理器添加数字吗?

ruby - 在 ruby​​ 上,为什么 include 是私有(private)的而 extend 是公共(public)的?