如何将 C++ class 构造函数注册到 Lua 用户数据并默认使用它
How to register C++ class constructor to Lua userdata and use it by default
使用 Lua C API,我注册了一个简单的 Object
class 到 Lua,像这样:
// My C++ Object class
class Object {
private:
double x;
public:
Object(double x) : x(x){}
};
// Create and return instance of Object class to Lua
int object_new(lua_State* L)
{
double x = luaL_checknumber(L, 1);
*reinterpret_cast<Object**>(lua_newuserdata(L, sizeof(Object*))) = new Object(x);
luaL_setmetatable(L, "Object");
return 1;
}
// Functions to register to Lua
const luaL_Reg functions[] =
{
{"new", object_new},
{nullptr, nullptr}
};
// Register the Object class to Lua
luaL_newmetatable(L, "Object");
luaL_setfuncs(L, functions, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
在我的 Lua 脚本中,以下工作正常:
// Works!
my_object = Object.new(42)
但是,我希望能够做到这一点(即省略 .new
部分):
// Fail :(
my_object = Object(42)
但是当我执行 Lua 脚本时,我得到这个错误:
...attempt to call a table value (global 'Object').
有没有办法注册一个 C++ class,如果我们不提供函数名,构造函数就会被调用?我错过了什么来完成这项工作?这对临时对象特别有用。
谢谢!
您应该检查 luaL_newmetatable 的 return 来注册您的元方法一次。
您可以将luaL_setmetatable
替换为luaL_newmetatable
,因此您的代码兼容Lua 5.1,您可以将元表注册嵌入构造函数 并且它的工作原理相同(除了额外的 lua_setmetatable
)。
对于构造函数,只需注册一个函数即可。元表应该管理一个实例,而不是他的创建。
不要忘记添加解构函数 (__gc) 以释放您分配的 C++ class 实例。
最后,您只需注册函数 Object
,该函数会在他第一次调用时创建元表。
#define LUA_META_OBJECT "Object"
class Object {
private:
double x;
public:
Object(double x) : x(x){}
};
static int object_free(lua_State* L)
{
delete *static_cast<Object**>(luaL_checkudata(L, 1, LUA_META_OBJECT));
return 0;
}
static int object_new(lua_State* L)
{
const lua_Number x = luaL_checknumber(L, 1);
*static_cast<Object**>(lua_newuserdata(L, sizeof(Object*))) = new Object(x);
if(luaL_newmetatable(L, LUA_META_OBJECT)){
static const luaL_Reg functions[] =
{
{"__gc", object_free},
{nullptr, nullptr}
};
luaL_setfuncs(L, functions, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
}
lua_setmetatable(L, -2);
return 1;
}
...
lua_register(L, "Object", object_new);
...
我非常喜欢 Youka 的回答,但我想以不同的方式实现它。这里的大部分代码都是借用了Youka的回答。
Youka 还做了一个重要说明,您 需要 制作一个 __gc
元方法,这样当您的 Object 实例超出范围时就不会发生内存泄漏
在下面的代码中,我创建了一个单独的函数,将元表推送到堆栈并在它不存在时创建它。元表本身有一个元表,该元表具有调用对象创建的 __call
元方法。此行为应该允许您使用 o = Object(42)
来创建新对象。我还包含了使用 o = Object.new(42)
仍然具有旧功能的代码。
在代码的底部,元表被推送一次以最初创建元表。
#define LUA_META_OBJECT "Object"
class Object {
private:
double x;
public:
Object(double x) : x(x){}
};
// declaration so we can use this in object_new function
int push_object_metatable(lua_State* L);
static int object_free(lua_State* L)
{
delete *static_cast<Object**>(luaL_checkudata(L, 1, LUA_META_OBJECT));
return 0;
}
static int object_new(lua_State* L)
{
const lua_Number x = luaL_checknumber(L, 1);
*static_cast<Object**>(lua_newuserdata(L, sizeof(Object*))) = new Object(x);
push_object_metatable(L);
lua_setmetatable(L, -2);
return 1;
}
static int call_object_new(lua_State* L)
{
lua_remove(L, 1);
object_new(L);
return 1;
}
// Pushes the metatable for Object and creates if it doesnt exist yet
int push_object_metatable(lua_State* L)
{
if(luaL_newmetatable(L, LUA_META_OBJECT)){
static const luaL_Reg functions[] =
{
{"new", object_new},
{"__gc", object_free},
{nullptr, nullptr}
};
luaL_setfuncs(L, functions, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
// Set a metatable for the metatable :D
// This allows using Object(42) to make new objects
lua_newtable(L);
lua_pushcfunction(L, call_object_new);
lua_setfield(L, -2, "__call");
lua_setmetatable(L, -2);
}
return 1;
}
...
// Register Object metatable for lua (Create and push it)
push_object_metatable(L);
lua_setglobal(L, LUA_META_OBJECT);
...
使用 Lua C API,我注册了一个简单的 Object
class 到 Lua,像这样:
// My C++ Object class
class Object {
private:
double x;
public:
Object(double x) : x(x){}
};
// Create and return instance of Object class to Lua
int object_new(lua_State* L)
{
double x = luaL_checknumber(L, 1);
*reinterpret_cast<Object**>(lua_newuserdata(L, sizeof(Object*))) = new Object(x);
luaL_setmetatable(L, "Object");
return 1;
}
// Functions to register to Lua
const luaL_Reg functions[] =
{
{"new", object_new},
{nullptr, nullptr}
};
// Register the Object class to Lua
luaL_newmetatable(L, "Object");
luaL_setfuncs(L, functions, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
在我的 Lua 脚本中,以下工作正常:
// Works!
my_object = Object.new(42)
但是,我希望能够做到这一点(即省略 .new
部分):
// Fail :(
my_object = Object(42)
但是当我执行 Lua 脚本时,我得到这个错误:
...attempt to call a table value (global 'Object').
有没有办法注册一个 C++ class,如果我们不提供函数名,构造函数就会被调用?我错过了什么来完成这项工作?这对临时对象特别有用。
谢谢!
您应该检查 luaL_newmetatable 的 return 来注册您的元方法一次。
您可以将luaL_setmetatable
替换为luaL_newmetatable
,因此您的代码兼容Lua 5.1,您可以将元表注册嵌入构造函数 并且它的工作原理相同(除了额外的 lua_setmetatable
)。
对于构造函数,只需注册一个函数即可。元表应该管理一个实例,而不是他的创建。
不要忘记添加解构函数 (__gc) 以释放您分配的 C++ class 实例。
最后,您只需注册函数 Object
,该函数会在他第一次调用时创建元表。
#define LUA_META_OBJECT "Object"
class Object {
private:
double x;
public:
Object(double x) : x(x){}
};
static int object_free(lua_State* L)
{
delete *static_cast<Object**>(luaL_checkudata(L, 1, LUA_META_OBJECT));
return 0;
}
static int object_new(lua_State* L)
{
const lua_Number x = luaL_checknumber(L, 1);
*static_cast<Object**>(lua_newuserdata(L, sizeof(Object*))) = new Object(x);
if(luaL_newmetatable(L, LUA_META_OBJECT)){
static const luaL_Reg functions[] =
{
{"__gc", object_free},
{nullptr, nullptr}
};
luaL_setfuncs(L, functions, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
}
lua_setmetatable(L, -2);
return 1;
}
...
lua_register(L, "Object", object_new);
...
我非常喜欢 Youka 的回答,但我想以不同的方式实现它。这里的大部分代码都是借用了Youka的回答。
Youka 还做了一个重要说明,您 需要 制作一个 __gc
元方法,这样当您的 Object 实例超出范围时就不会发生内存泄漏
在下面的代码中,我创建了一个单独的函数,将元表推送到堆栈并在它不存在时创建它。元表本身有一个元表,该元表具有调用对象创建的 __call
元方法。此行为应该允许您使用 o = Object(42)
来创建新对象。我还包含了使用 o = Object.new(42)
仍然具有旧功能的代码。
在代码的底部,元表被推送一次以最初创建元表。
#define LUA_META_OBJECT "Object"
class Object {
private:
double x;
public:
Object(double x) : x(x){}
};
// declaration so we can use this in object_new function
int push_object_metatable(lua_State* L);
static int object_free(lua_State* L)
{
delete *static_cast<Object**>(luaL_checkudata(L, 1, LUA_META_OBJECT));
return 0;
}
static int object_new(lua_State* L)
{
const lua_Number x = luaL_checknumber(L, 1);
*static_cast<Object**>(lua_newuserdata(L, sizeof(Object*))) = new Object(x);
push_object_metatable(L);
lua_setmetatable(L, -2);
return 1;
}
static int call_object_new(lua_State* L)
{
lua_remove(L, 1);
object_new(L);
return 1;
}
// Pushes the metatable for Object and creates if it doesnt exist yet
int push_object_metatable(lua_State* L)
{
if(luaL_newmetatable(L, LUA_META_OBJECT)){
static const luaL_Reg functions[] =
{
{"new", object_new},
{"__gc", object_free},
{nullptr, nullptr}
};
luaL_setfuncs(L, functions, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
// Set a metatable for the metatable :D
// This allows using Object(42) to make new objects
lua_newtable(L);
lua_pushcfunction(L, call_object_new);
lua_setfield(L, -2, "__call");
lua_setmetatable(L, -2);
}
return 1;
}
...
// Register Object metatable for lua (Create and push it)
push_object_metatable(L);
lua_setglobal(L, LUA_META_OBJECT);
...