如何在c++中不绑定lua的情况下将成员函数注册到lua
How to register member function to lua without lua bind in c++
我在我的c++游戏项目中使用lua 5.1,但当我尝试注册一个c++成员函数时,我遇到了使用lua的麻烦。我想在lua中使用我的c++类成员函数,但是lua_register()函数的第三个形参只能接受c类型的普通函数指针或静态成员函数的指针。
我听说lua绑定库可以解决这个问题,但我不想使用lua绑定。很好,但是对我的项目来说太重了。有没有任何方法注册c++成员函数没有任何库?我该怎么做呢?我自己也有过同样的经历。
我知道基本上有两种好的解决方案。如果成员函数是针对一个类的,那么对于每个lua状态只有一个成员函数是好的。另一种方法更灵活,但更复杂、更慢。(我渴望学习其他方法/改进这些方法!)
我认为lua_bind使用了一些与方法1非常相似的模板,但使用了一些技巧使实现像方法2一样灵活。我认为这两种方法都比lua_bind更透明。
方法1
(1)对于要传递给lua的my_class
的每个成员函数,它应该取lua_State * L
并返回int
。
(2)在初始化lua的同时,在lua_extraspace
中存储一个指向my_class
的指针。
*static_cast<my_class**>(lua_getextraspace(L_)) = &instance;
(3)当你想传递成员函数给lua时,使用如下模板:
typedef int (my_class::*mem_func)(lua_State * L);
// This template wraps a member function into a C-style "free" function compatible with lua.
template <mem_func func>
int dispatch(lua_State * L) {
my_class * ptr = *static_cast<my_class**>(lua_getextraspace(L));
return ((*ptr).*func)(L);
}
然后像这样注册:
const luaL_Reg regs[] = {
{ "callback_1", &dispatch<&my_class::callback_1> },
{ "callback_2", &dispatch<&my_class::callback_2> },
{ "callback_3", &dispatch<&my_class::callback_3> },
{ NULL, NULL }
};
luaL_register(L, regs);
方法1的好处是它非常简单和非常快,我认为它将比lua绑定更快。因为get_extraspace
除了做一点指针运算外什么也没做。最有可能的是,一个好的编译器可以优化dispatch
模板,使它生成的函数是内联的,这样就不会有任何开销。
你可能想改变dispatch
模板,以便在额外空间有一个空指针检查,这取决于你的lua状态和my_class
的生存期是如何管理的。
潜在地,你也可以在额外空间中存储更复杂的东西,比如指向几个不同对象的指针,或者甚至像一个向量之类的东西(你可以在他们的文档中阅读如何配置lua额外空间)。或者您可以将内容存储在lua注册表中,然后分派函数可以从那里检索它们,但是额外空间更快——这取决于您。
方法2
在方法2中,你基本上使用通常的lua技术来将一个c++对象推入lua,但你这样做的对象是一个c++ std::function
,你重载_call
元函数,这样它就调用了这个函数。(如果你不是c++ 11,你可以使用boost::function
)
当你想把一个c++成员函数推入lua时,你可以使用std::bind
使它成为一个函数对象。
这个方法的缺点是,在lua中,"函数"的类型实际上是userdata
,但是因为你可以很好地调用它并将其作为函数使用,所以这并不重要。如果这对您不利,您可以做的一件事是使用相同的技巧,但也是在之后,创建一个将函数对象userdata作为upvalue的闭包,当调用闭包时,它只是将参数转发给函数对象并返回结果。那么闭包的类型在lua中将是function
,但它将做基本相同的事情。
typedef std::function<int(lua_State *)> lua_function;
char const * cpp_function = "CPP_Function";
static int intf_dispatcher ( lua_State* L )
{
//make a temporary copy, in case lua_remove(L,1) might cause lua to garbage collect and destroy it
lua_function f = * static_cast<lua_function *> (luaL_checkudata(L, 1, cpp_function));
// remove from the stack before executing, so that like all other callbacks, f finds only its intended arguments on the stack.
lua_remove(L,1);
int result = (f)(L);
return result;
}
static int intf_cleanup ( lua_State* L )
{
lua_function * d = static_cast< lua_function *> (luaL_testudata(L, 1, cpp_function));
if (d == NULL) {
std::cerr << "ERROR: intf_cleanup called on data of type: " << lua_typename( L, lua_type( L, 1 ) ) << std::endl;
lua_pushstring(L, "C++ function object garbage collection failure");
lua_error(L);
} else {
d->~lua_function();
}
return 0;
}
static int intf_tostring( lua_State* L )
{
lua_function * d = static_cast< lua_function *> (luaL_checkudata(L, 1, cpp_function));
// d is not null, if it was null then checkudata raised a lua error and a longjump was executed.
std::stringstream result;
result << "c++ function: " << std::hex << d;
lua_pushstring(L, result.str().c_str());
return 1;
}
void register_metatable ( lua_State* L )
{
luaL_newmetatable(L, cpp_function);
lua_pushcfunction(L, intf_dispatcher);
lua_setfield(L, -2, "__call");
lua_pushcfunction(L, intf_cleanup);
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, intf_tostring);
lua_setfield(L, -2, "__tostring");
lua_pushvalue(L, -1); //make a copy of this table, set it to be its own __index table
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
void push_function( lua_State* L, const lua_function & f )
{
void * p = lua_newuserdata(L, sizeof(lua_function));
luaL_setmetatable(L, cpp_function);
new (p) lua_function(f);
}
前面的答案是正确的。
就这个问题而言,这取决于当您从lua调用类成员时,是希望lua决定使用的对象,还是希望将其绑定到固定对象实例的成员。如果它是固定的,那么用std::bind创建一个函数对象来保存要调用的成员函数的指针,以及调用它的this指针,并将其注册到lua中是相当简单的。
http://en.cppreference.com/w/cpp/utility/functional/bind代码看起来像这样:
class C {
public:
void blah(lua_State* L);
};
C inst;
lua_pushcclosure(L, std::bind(&C::blah, &inst, std::placeholder::_1), 0);
lua_setglobal(L, "blah");
还有一个更自然的方法来绑定C/c++函数与lua,避免编写包装器,看看这里:
http://inspired-code.blogspot.com.au/p/automagical-language-bindings-using-c.html- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 在未初始化映射的情况下,将值插入到映射的映射中
- 是默认情况下分配给char数组常量的值
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 如何在不产生任何垃圾的情况下获得C中的像素
- 在已经使用Git的情况下减少编译时间
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 如何在没有信号的情况下从C++执行QML插槽
- 如何在不知道向量大小的情况下输入向量内部的向量?
- 为什么在某些情况下不写入此文件?
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 在没有Xcode的情况下在Mac捆绑包中嵌入框架
- UE4-如何在给定4个屏幕坐标的情况下缩放纹理或材质
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 在C++中如何在没有pow的情况下进行基础计算
- 松弛原子与无同步情况下的记忆连贯性
- C++-在没有自定义.lib文件的情况下从Lua C模块调用Lua函数
- 如何在不运行的情况下识别LUA脚本中的非初始化变量
- 如何在c++中不绑定lua的情况下将成员函数注册到lua