userdata:函数
userdata机制可让咱们在lua中使用c中的自定义数据类型。userdata表示一块动态分配的内存,这块内存就存储的自定义类型的数据,在lua脚本中使用userdata,并配合c提供的函数,就能够操做userdata了。lua
定义一个player类型:spa
typedef struct _Player { int id; char name[20]; int account; } Player;
定义player的全部操做:指针
static int _index = 1; static int player_new (lua_State* L) { const char* name = luaL_checkstring(L, 1); int len = strlen(name); Player* player = (Player*)lua_newuserdata(L, sizeof(Player)); // 使用lua_newuserdata建立userdata,并将其入栈 player->id = _index++; memcpy(player->name, name, len + 1); // 须要拷贝一份字符串,不然栈在弹出的时候,字符串会被销毁 player->account = 0; return 1; } static int player_print (lua_State* L) { Player* player = (Player*)lua_touserdata(L, 1); printf("player data: %d %s acount:%d \n", player->id, player->name, player->account); return 0; } static int player_charge (lua_State* L) { Player* player = (Player*)lua_touserdata(L, 1); int add = luaL_checkint(L, 2); player->account += add; return 0; }
lua代码:code
local player = Player.new("xiaoming") local player1 = Player.new("xiaoqiang") Player.charge(player1, 20) Player.charge(player, 101) Player.print(player) Player.print(player1)
元表:对象
1. 相同的元表表明相同的类型,所以,咱们也使用元表来为userdata标示类型:blog
为userdata设置元表:内存
const char* CLASS_NAME_PLAYER = "Player_Class";作用域
Player* player = lua_newuserdata(L, sizeof(Player));字符串
lua_newmetatable(L, CLASS_NAME_PLAYER); // 建立一个新的元表,名字为player_class,并入栈
lua_setmetatable(L, -2); // 为位置在-2的userdata,设置元表,元表出栈
如何使用元表来进行类型判断:
将
Player* player = (Player*)lua_touserdata(L, 1);
改成
Player* player = (Player*)lua_checkudata(L, 1, CLASS_NAME_PLAYER); 若是userdata的类型不匹配,将抛出错误
2. 在lua中,元表除了能够标示类型,更重要的是模拟面向对象,和普通lua对象同样,userdata一样可使用元表机制来模拟面向对象:
咱们首先建立一个元表,只须要把对象的方法放在元表上,最重要的是设置元表的__index元方法:
luaL_newmetatable(L, CLASS_NAME_PLAYER); // 建立一个新的元表,并入栈,该元表是存放在全局做用域中的
...... // 设置一些对象方法
lua_pushvalue(L, -1); // 复制元表
lua_setfield(L, -2, "__index"); // 将元表的__index元方法设置为本身
在建立新对象的时候,只须要将新对象的元表设置为已经建立好的元表:
luaL_getmetatable(L, CLASS_NAME_PLAYER); // 将元表入栈
lua_setmetatable(L, -2); // 设置元表,元表出栈
上面的例子改成:
c代码:
static int _index = 1; const char* CLASS_NAME_PLAYER = "PLAYER_CLASS"; static int player_new (lua_State* L) { dump(L); const char* name = luaL_checkstring(L, 1); int len = strlen(name); dump(L); Player* player = (Player*)lua_newuserdata(L, sizeof(Player)); dump(L); player->id = _index++; memcpy(player->name, name, len + 1); player->account = 0;
// 使用已经建立好的元表 luaL_getmetatable(L, CLASS_NAME_PLAYER); lua_setmetatable(L, -2); return 1; } static int player_print (lua_State* L) { Player* player = (Player*)luaL_checkudata(L, 1, CLASS_NAME_PLAYER); printf("player data: %d %s acount:%d \n", player->id, player->name, player->account); return 0; } static int player_charge (lua_State* L) { Player* player = (Player*)luaL_checkudata(L, 1, CLASS_NAME_PLAYER); int add = luaL_checkint(L, 2); player->account += add; return 0; } int libopen_player (lua_State* L) {
// 建立元表 luaL_newmetatable(L, CLASS_NAME_PLAYER); lua_pushcfunction(L, player_print); lua_setfield(L, -2, "print"); lua_pushcfunction(L, player_charge); lua_setfield(L, -2, "charge"); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_settop(L, 0);
// 模块只有一个new方法了 lua_newtable(L); lua_pushcfunction(L, player_new); lua_setfield(L, -2, "new"); lua_setglobal(L, "Player"); return 1; }
lua代码:
local player = Player.new("xiaoming") local player1 = Player.new("xiaoqiang") player1:charge(30) player:charge(20) player:print() player1:print();
轻量级的userdata:
对比彻底的userdta,轻量级的userdata只是c对象的一个指针,没有元表,就是一个普通的lua对象,就像number同样,所以轻量级的userdata不受lua垃圾回收机制的控制,必须本身管理内存。
c代码:
Player* player = nullptr; static int player_pointer (lua_State* L) { player = new Player(); player->id = 12; memcpy(player->name, "wulin", 6); player->account = 0; lua_pushlightuserdata(L, player); return 1; }
lua代码:
local player1 = Player.pointer(); local player2 = Player.pointer(); print(player1); print(player2);
userdata的内存回收:
userdata属于lua的内存管理机制,所以无须关系userdata的内存问题,但若是userdata使用了一些c内存中的对象,而且须要在userdata被删除的时候,同时删除这些对象,那么lua的内存回收机制就无能力为。这种状况下,lua为咱们提供了一个__gc元方法(只针对userdata),当userdata被删除时,会调用这个元方法,并将userdata做为参数传入,这样咱们就能够删除userdata中引用的c对象了。
在player中添加一个__gc的元方法:
static int player_delete (lua_State* L) { Player* player = (Player*)luaL_checkudata(L, 1, CLASS_NAME_PLAYER); printf("delete something not in lua memory... player name:%s \n", player->name); return 0; }
int libopen_player (lua_State* L) { luaL_newmetatable(L, CLASS_NAME_PLAYER); lua_pushcfunction(L, player_print); lua_setfield(L, -2, "print"); lua_pushcfunction(L, player_charge); lua_setfield(L, -2, "charge"); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, player_delete); lua_setfield(L, -2, "__gc"); // 添加__gc元方法 lua_settop(L, 0); lua_newtable(L); lua_pushcfunction(L, player_new); lua_setfield(L, -2, "new"); lua_pushcfunction(L, player_pointer); lua_setfield(L, -2, "pointer"); lua_setglobal(L, "Player"); return 1; }
lua代码:
local player = Player.new("xiaoming") player:charge(20) player:print() player = nil collectgarbage(); // 强制进行垃圾回收