Добавление разделителей в итерации по __VA_ARGS__ в макросе C / C ++ - PullRequest
0 голосов
/ 10 мая 2018

background

Я пытаюсь сделать автоматический генератор интерфейса Lua-C, используя макросы C.Самая большая проблема заключалась в том, чтобы сделать его общим для различного числа аргументов, которое я решил, используя __VA_ARGS__ с помощью этого ответа: Можно ли перебирать аргументы в вариационных макросах?

Более простое, почти рабочее решение

Это решение почти работает, но оно выдает несколько избыточных запятых (не выводится ,,,, на выходе)

// 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; }

Можно ли сделать его проще / приятнее?

ПРИМЕЧАНИЕ. - Для отладки я нашел очень полезным это Просмотр расширенных макросов C , в частности https://stackoverflow.com/a/31460434/1291544

1 Ответ

0 голосов
/ 11 мая 2018

Вам нужно подсчитать количество аргументов, которые у вас есть, а затем вызвать соответствующий макрос 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_N, 4 будет 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

Это та же самая техника, которая была показана в ответе, на который вы указали. Однако эта версия была немного сложнее, чем версия, которую я иллюстрирую для вас, так что, надеюсь, вы найдете это объяснение полезным.

...