零、前言
经过之前的文章分享,我们已经知道如何通过扩展函数来扩展 Lua 。但这里涉及到一个问题,Lua 脚本中如何使用 C/C++ 中的类型,如何像操作对象一样操作 C/C++ 类型的实例。解决这一问题就需要用到 userdata 类型(用户数据类型)。
userdata 类型,分为两种:
- 完全用户数据(full userdata)
- 轻量级用户数据(light userdata)
一、full userdata(完全用户数据)
full userdata 为 Lua 语言提供了可以用来存储任何数据的原始内存区域,没有预定义的操作。 使用完全用户数据的 C-API 为:lua_newuserdata
使用 full userdata 创建的内存不需要进行释放,Lua 会进行管理和释放。 而如果 full userdata 关联的对象需要释放,可以通过给 full userdata 设置元表,元表中设置 __gc
进行监听释放,调用关联的对象的释放方法进行释放,下面的第五小节会进行展示这一过程。
1、lua_newuserdata
#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1)
描述:
分配一块指定大小的内存,然后将该内存以 userdata 的类型进行压栈,并返回该内存的指针。
参数:
- 参数 L: Lua 状态的指针。
- 参数 size: 要分配的 userdata 对象的字节大小。
返回值:
返回指向该内存的指针,可以通过这个指针进行对象的操作。
2、举个例子
假设我们需要向 Lua 暴露一个 C++ 的类型:User ,在 Lua 脚本中可以创建该类型,并且调用该类型的方法。
第一步,定义一个 User 类。
class User {
private:
std::string name;
long long age;
public:
std::string introduce() {
std::stringstream info;
info << "大家好。我叫" << name << "。今年" << age << "岁。请关注我。";
return info.str();
}
void setName(std::string name) {
this->name = std::move(name);
}
void setAge(long long age) {
this->age = age;
}
std::string getName() {
return this->name;
}
long long getAge() {
return this->age;
}
};
第二步,定义对 User 操作相关的方法,并将这些方法用作库方法,向 Lua 暴露一个 user
库,可以通过该库创建 User 类型的 full userdata ,并操作背后真正 C++ 实例。
static int newUser(lua_State *L) {
std::string name = luaL_checkstring(L, 1);
long long age = luaL_checkinteger(L, 2);
// 生成一个 User Data 并压入栈中
auto *user = (User *) lua_newuserdata(L, sizeof(User));
user->setName(std::string(name));
user->setAge(age);
return 1;
}
static int introduce(lua_State *L) {
User *user = (User *) lua_touserdata(L, 1);
lua_pushstring(L, user->introduce().c_str());
return 1;
}
static int setName(lua_State *L) {
User *user = (User *) lua_touserdata(L, 1);
std::string name = luaL_checkstring(L, 2);
user->setName(std::string(name));
return 0;
}
static int setAge(lua_State *L) {
User *user = (User *) lua_touserdata(L, 1);
long long age = luaL_checkinteger(L, 2);
user->setAge(age);
retu