Lua调用userdata绑定C函数时传入元表致崩溃的原因咨询
LuaBridge绑定C++类时,传入元表而非userdata触发崩溃的原因解析
问题现象与复现步骤
- 将C++类以userdata形式暴露给Lua,通过LuaBridge绑定
__tostring元方法; - 在Lua中创建类实例,调用
getmetatable获取实例的元表并传入print函数; - 程序崩溃,调用栈显示LuaBridge的代理函数收到的是Lua表而非预期的userdata。
核心原因解析
问题本质是元表的元方法指向逻辑冲突:
- LuaBridge绑定C++类时,会为userdata创建专属元表,并将你绑定的
__tostring代理函数挂载到这个元表上; - 当执行
print(getmetatable(t))时,print实际处理的是这个元表本身(一个普通Lua表); - Lua在将表转为字符串时,会查找该表自身的
__tostring元方法——而这个元表的__tostring恰好就是你为userdata绑定的代理函数; - 该代理函数是为处理持有C++对象指针的userdata设计的,收到普通Lua表时会尝试读取userdata特有的指针结构,直接触发崩溃。
简单来说:你给userdata的元表加了专属的__tostring,但这个元表自己也是个Lua表,当你打印它时,Lua会调用它自己的__tostring,也就是那个只能处理userdata的函数,自然会崩溃。
示例代码(原问题提供)
// Some example class with a tostring method. class ExampleClass { public: ExampleClass() : a(0), b(0), c(0) { } string tostring() const { return "whatever"; } int a, b, c; }; lua_State* L = luaL_newstate(); luaL_openlibs(L); // Expose example class and add a __tostring metamethod luabridge::getGlobalNamespace(L) .beginClass<ExampleClass>("ExampleClass") .addConstructor<void(*) ()>() .addFunction("__tostring", &ExampleClass::tostring) .endClass() ; // Create an instance of the example class. // Call getmetatable on the instance and pass it to print. luaL_dostring(L, "local t = ExampleClass(); print(getmetatable(t));"); lua_close(L);
崩溃调用栈(原问题提供)
main.exe!luabridge::detail::Userdata::getPointer() Line 46 C++ main.exe!luabridge::detail::Userdata::get<ExampleClass>(lua_State * L, int index, bool canBeConst) Line 252 C++ main.exe!luabridge::detail::CFunc::CallConstMember<std::string (__cdecl ExampleClass::*)(void)const>::f(lua_State * L) Line 290 C++ main.exe!precallC(lua_State * L, StackValue * func, int nresults, int(*)(lua_State *) f) Line 506 C main.exe!luaD_precall(lua_State * L, StackValue * func, int nresults) Line 570 C main.exe!ccall(lua_State * L, StackValue * func, int nResults, int inc) Line 607 C main.exe!luaD_callnoyield(lua_State * L, StackValue * func, int nResults) Line 628 C main.exe!lua_callk(lua_State * L, int nargs, int nresults, __int64 ctx, int(*)(lua_State *, int, __int64) k) Line 1024 C main.exe!luaL_callmeta(lua_State * L, int obj, const char * event) Line 867 C main.exe!luaL_tolstring(lua_State * L, int idx, unsigned __int64 * len) Line 885 C main.exe!luaB_print(lua_State * L) Line 29 C
解决方案
要避免这个冲突,需确保元表自身的__tostring不会指向处理userdata的函数:
- 方法一:绑定元方法时,使用LuaBridge的
addMetaMethod(若支持)而非直接addFunction绑定__tostring,确保元表自身的元方法逻辑独立; - 方法二:绑定完成后手动重置元表的
__tostring为默认值或表专属处理函数:-- 在创建实例前执行 local mt = getmetatable(ExampleClass()) -- 重置为Lua默认表转字符串逻辑,或自定义表的打印内容 mt.__tostring = nil
内容的提问来源于stack exchange,提问作者SuperUser
相关产品推荐
相关产品推荐

