在为游戏Bitfighter实现Lua界面时,我正在使用x-macros来减少重复次数和代码重复。以下代码工作正常:
// Fn name Valid param profiles Profile count
# define TELEPORTER_LUA_METHOD_TABLE \
TELEPORTER_LUA_METHOD_ITEM(addDest, ARRAYDEF({{ PT, END }}), 1 ) \
TELEPORTER_LUA_METHOD_ITEM(delDest, ARRAYDEF({{ INT, END }}), 1 ) \
TELEPORTER_LUA_METHOD_ITEM(clearDests, ARRAYDEF({{ END }}), 1 ) \
// BLOCK A Start
const luaL_reg Teleporter::luaMethods[] =
{
# define TELEPORTER_LUA_METHOD_ITEM(name, b, c) { #name, luaW_doMethod<Teleporter, &Teleporter::name > },
TELEPORTER_LUA_METHOD_TABLE
# undef TELEPORTER_LUA_METHOD_ITEM
{ NULL, NULL }
};
// BLOCK A End
/* Generates the following:
const luaL_reg Teleporter::luaMethods[] =
{
{ "addDest", luaW_doMethod<Teleporter, &Teleporter::addDest > }
{ "delDest", luaW_doMethod<Teleporter, &Teleporter::delDest > }
{ "clearDests", luaW_doMethod<Teleporter, &Teleporter::clearDests > }
{ NULL, NULL }
};
*/
// BLOCK B Start
const LuaFunctionProfile Teleporter::functionArgs[] =
{
# define TELEPORTER_LUA_METHOD_ITEM(name, profiles, profileCount) { #name, profiles, profileCount },
TELEPORTER_LUA_METHOD_TABLE
# undef TELEPORTER_LUA_METHOD_ITEM
{ NULL, { }, 0 }
};
// BLOCK B End
/* Generates the following:
const LuaFunctionProfile Teleporter::functionArgs[] =
{
{ "addDest", {{ PT, END }}, 1 }
{ "delDest", {{ INT, END }}, 1 }
{ "clearDests", {{ END }}, 1 }
{ NULL, { }, 0 }
};
*/
#undef TELEPORTER_LUA_METHOD_TABLE
到目前为止,非常好。
除了我在几十个课程中基本上做同样的事情。我真正想做的是在每个类中定义方法表(可以调用任何东西),然后定义两个可以像这样调用的宏:
GENERATE_LUA_METHODS(Teleporter, TELEPORTER_LUA_METHOD_TABLE)
GENERATE_FUNCTION_PROFILE(Teleporter, TELEPORTER_LUA_METHOD_TABLE)
避免上面A&amp; A中所有重复的代码。 B.显而易见的方法是使用嵌套宏,但不幸的是,这是非法的。
有更好的方法吗?
解
当我发布这个问题时,我很确定答案是“无法完成”。相反,我有两种方法,其中一种正是我想要的。还有一个很好的讨论宏的缺陷(有很多),建议采用一些替代方法。我根据接受的答案开发的实现是干净且易于理解的,脏的宏观内容很方便。
在一个隐蔽的地方:
#define ARRAYDEF(...) __VA_ARGS__ // Don't confuse the preprocessor with array defs
////////////////////////////////////////
////////////////////////////////////////
//
// Some ugly macro defs that will make our Lua classes sleek and beautiful
//
////////////////////////////////////////
////////////////////////////////////////
//
// See discussion of this code here:
// http://stackoverflow.com/questions/11413663/reducing-code-repetition-in-c
//
// Start with a definition like the following:
// #define LUA_METHODS(CLASS, METHOD) \
// METHOD(CLASS, addDest, ARRAYDEF({{ PT, END }}), 1 ) \
// METHOD(CLASS, delDest, ARRAYDEF({{ INT, END }}), 1 ) \
// METHOD(CLASS, clearDests, ARRAYDEF({{ END }}), 1 ) \
//
#define LUA_METHOD_ITEM(class_, name, b, c) \
{ #name, luaW_doMethod<class_, &class_::name > },
#define GENERATE_LUA_METHODS_TABLE(class_, table_) \
const luaL_reg class_::luaMethods[] = \
{ \
table_(class_, LUA_METHOD_ITEM) \
{ NULL, NULL } \
}
// Generates something like the following:
// const luaL_reg Teleporter::luaMethods[] =
// {
// { "addDest", luaW_doMethod<Teleporter, &Teleporter::addDest > }
// { "delDest", luaW_doMethod<Teleporter, &Teleporter::delDest > }
// { "clearDests", luaW_doMethod<Teleporter, &Teleporter::clearDests > }
// { NULL, NULL }
// };
////////////////////////////////////////
#define LUA_FUNARGS_ITEM(class_, name, profiles, profileCount) \
{ #name, profiles, profileCount },
#define GENERATE_LUA_FUNARGS_TABLE(class_, table_) \
const LuaFunctionProfile class_::functionArgs[] = \
{ \
table_(class_, LUA_FUNARGS_ITEM) \
{ NULL, { }, 0 } \
}
// Generates something like the following:
// const LuaFunctionProfile Teleporter::functionArgs[] =
// {
// { "addDest", {{ PT, END }}, 1 }
// { "delDest", {{ INT, END }}, 1 }
// { "clearDests", {{ END }}, 1 }
// { NULL, { }, 0 }
// };
////////////////////////////////////////
////////////////////////////////////////
在每个班级档案中:
// Fn name Param profiles Profile count
#define LUA_METHODS(CLASS, METHOD) \
METHOD(CLASS, addDest, ARRAYDEF({{ PT, END }}), 1 ) \
METHOD(CLASS, delDest, ARRAYDEF({{ INT, END }}), 1 ) \
METHOD(CLASS, clearDests, ARRAYDEF({{ END }}), 1 ) \
GENERATE_LUA_METHODS_TABLE(Teleporter, LUA_METHODS);
GENERATE_LUA_FUNARGS_TABLE(Teleporter, LUA_METHODS);
#undef LUA_METHODS
答案 0 :(得分:2)
功能方法可以解决你的许多困境,但你应该意识到大量使用预处理器会导致代码难以调试。每当生成的代码中出现语法错误时,您就会花费大量时间格式化代码(当宏使用量增长时,您必然会遇到这种情况);当你需要使用gdb或其他类似的东西时,它也会影响你的心情。
以下显然只是一个草图,可以给你一个想法。
# define TELEPORTER_LUA_METHOD_TABLE(class_, item) \
item(class_, addDest, ARRAYDEF({{ PT, END }}), 1 ) \
item(class_, delDest, ARRAYDEF({{ INT, END }}), 1 ) \
item(class_, clearDests, ARRAYDEF({{ END }}), 1 ) \
# define LUA_METHOD_ITEM(class_, name, b, c) \
{ #name, luaW_doMethod<class_, &class_::name > },
# define LUA_FUNARGS_ITEM(class_, name, profiles, profileCount) \
{ #name, profiles, profileCount },
#define LUA_METHODS_TABLE(class_, table) \
const luaL_reg class_::luaMethods[] = \
{ \
table(class_, LUA_METHOD_ITEM) \
{ NULL, NULL } \
};
#define LUA_FUNARGS_TABLE(class_, table) \
const LuaFunctionProfile class_::functionArgs[] = \
{ \
table(class_, LUA_FUNARGS_ITEM) \
{ NULL, { }, 0 } \
};
LUA_METHODS_TABLE(Teleporter, TELEPORTER_LUA_METHOD_TABLE)
LUA_FUNARGS_TABLE(Teleporter, TELEPORTER_LUA_METHOD_TABLE)
#undef TELEPORTER_LUA_METHOD_TABLE
编辑从评论中回答Watusimoto的问题。
Watusimoto建议这样的事情:
#define LUA_METHODS_TABLE(class_) \
const luaL_reg class_::luaMethods[] = \
{ \
LUA_METHOD_TABLE(class_, LUA_METHOD_ITEM) \
{ NULL, NULL } \
};
#define LUA_FUNARGS_TABLE(class_, table) \
const LuaFunctionProfile class_::functionArgs[] = \
{ \
LUA_METHOD_TABLE(class_, LUA_FUNARGS_ITEM) \
{ NULL, { }, 0 } \
};
#ifdef LUA_METHOD_TABLE
# undef LUA_METHOD_TABLE
#endif
# define LUA_METHOD_TABLE(class_, item) \
... class-specific definition ...
LUA_METHODS_TABLE(Teleporter)
LUA_FUNARGS_TABLE(Teleporter)
这样做的缺点是,不清楚LUA_METHOD_TABLE与后面的两个宏调用有什么关系。这就好像sprintf(3)没有接受参数,而是期望特定名称的全局变量中的数据。从可理解性的角度来看,任何一段代码都应该明确它的直接输入,它的工作原理和它的用途之间的不同。但是全局表宏也在可组合性方面失去了:全局宏不能一次性生成多个类定义,例如。与BPP或类似。
答案 1 :(得分:1)
这是一个相当极端的预处理程序hackery,但你可以使用几个不同的文件来完成。
teleporter.cpp:
#define LMT_CLASS_NAME Teleporter
#define LMT_TABLE_FILE "Teleporter.lmt"
#include "lua_method_table.h"
lua_method_table.h:
#define LMT_METHOD_ITEM(name, b, c) { #name, luaW_doMethod<LMT_CLASS_NAME, &LMT_CLASS_NAME::name > },
const luaL_reg LMT_CLASS_NAME::luaMethods[] =
#include LMT_TABLE_FILE
{ NULL, NULL }
};
#undef LMT_METHOD_ITEM
#define LMT_METHOD_ITEM(name, profiles, profileCount) { #name, profiles, profileCount },
const LuaFunctionProfile LMT_CLASS_NAME::functionArgs[] =
{
#include LMT_TABLE_FILE
{ NULL, { }, 0 }
};
#undef LMT_METHOD_ITEM
最后是teleporter.lmt:
LMT_METHOD_ITEM(addDest, ARRAYDEF({{ PT, END }}), 1 )
LMT_METHOD_ITEM(delDest, ARRAYDEF({{ INT, END }}), 1 )
LMT_METHOD_ITEM(clearDests, ARRAYDEF({{ END }}), 1 )
它不是使用宏来定义方法表,而是列在文件teleporter.lmt
中,该文件包含两次LMT_METHOD_ITEM
的不同定义。那里没有头部防护装置,因此可以根据需要多次包含。如果需要,可以将lua_method_table.h
拆分为两个文件,分别处理这两个部分。只需在CPP文件中包含这两个文件。