• 用户自定义类型


    userdata:

    userdata机制可以让我们在lua中使用c中的自定义数据类型。userdata表示一块动态分配的内存,这块内存就存储的自定义类型的数据,在lua脚本中使用userdata,并配合c提供的函数,就可以操作userdata了。

    定义一个player类型:

    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 
    ", 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代码:

    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标示类型:

    为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 ", 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 
    ", 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(); // 强制进行垃圾回收
  • 相关阅读:
    LeetCode 79. 单词搜索
    LeetCode 1143. 最长公共子序列
    LeetCode 55. 跳跃游戏
    LeetCode 48. 旋转图像
    LeetCode 93. 复原 IP 地址
    LeetCode 456. 132模式
    LeetCode 341. 扁平化嵌套列表迭代器
    LeetCode 73. 矩阵置零
    LeetCode 47. 全排列 II
    LeetCode 46. 全排列
  • 原文地址:https://www.cnblogs.com/iRidescent-ZONE/p/5664181.html
Copyright © 2020-2023  润新知