zoukankan      html  css  js  c++  java
  • 用户自定义类型

    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(); // 强制进行垃圾回收
  • 相关阅读:
    洛谷mNOIP模拟赛Day2-星空
    洛谷mNOIP模拟赛Day2-将军令
    洛谷mNOIP模拟赛Day2-入阵曲
    洛谷mNOIP模拟赛Day1-斐波那契
    洛谷mNOIP模拟赛Day1-数颜色
    计蒜客NOIP2017提高组模拟赛(五)day1-展览
    HDU1700Points on Cycle(圆心半径)
    HDU1086You can Solve a Geometry Problem too (斜率问题)
    湖南省第十届大学生计算机程序设计竞赛1503: 点到圆弧的距离(atan()函数的应用)
    HDU献给杭电五十周年校庆的礼物 (切蛋糕,线段划分区域)
  • 原文地址:https://www.cnblogs.com/iRidescent-ZONE/p/5664181.html
Copyright © 2011-2022 走看看