用户自定义类型

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 \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代码:

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 \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(); // 强制进行垃圾回收
时间: 2024-08-08 20:36:18

用户自定义类型的相关文章

边做边学Rust之用户自定义类型

3 用户自定义类型 Rust自定义类型主要通过下面两个关键进行定义: struct:定义一个结构 enum:定义一个枚举 常量能以通过const和static关键字创建. 3.1 结构 有三种类型的结构("structs"),可以使用struct关键字来创建: 元组结构体,又名元组 传统C结构体 元结构体,无field,为了做成通用类型 // A unit struct struct Nil; // A tuple struct struct Pair(i32, f64); // A

C++的内置类型和用户自定义类型的互相转换

这两种之间的转换主要有两种方式第一就是内置类型转换为用户自定义类型,这里以int类型转换为类类型的对象为例,第二就是类类型转换为int类型 前者依靠转换构造函数,后者依靠自定义的类型转换函数 一.转换构造函数 二.类型转换构造函数 #include <iostream> using namespace std; //转换构造函数只有一个参数,但是有时候这种一个参数的构造函数不仅起到类型转换的作用也起到了构造函数的作用,但是只有一个参数的构造函数才有这种类似的双重身份,并且这种没有拷贝构造的情况

0914,异常语句,类类型(用户自定义类型),分割

异常语句try :尝试 try { //要包括起来的可能有错误的代码 } catch (Exception ex)//抓获错误 { throw ex;  //抛出错误异常 console.writeline ("错误内容"+ex ); } finally { console.write(""); } 类   类型 : class类型 : 用户自定义类型 String :   处理字符串 DateTime : 处理时间 Random :   生产随机数 Math : 

使用用户自定义类型作为map的key

有时候我们想把用户自定义类型作为std::map的键值.方法一)最简单的方法就是实现该自定义类型的<操作符,代码如下:class Foo{public:    Foo(int num_)        : num(num_)    {    }    bool operator < (const Foo & cmp) const    {        return num < cmp.num;    }      int num;   };之后就可以使用Foo作为map的key

Lua-UserData(用户自定义类型)

UserData(用户自定义类型) 意义:使用C语言编写的用于扩展Lua的新类型,方便使用脚本编写或者提高效率 userdata:提供了一块原始的内存区域,用于存储任何东西,在Lua中userdata没有任何预定义操作 生成:void *lua_newuserdata(L,size) 根据指定大小分配一块内存,并将userdata压入栈中,最后返回这个内存块的地址 例子: Lua require "array" a = array.new(1000) print(a); print(a

使用用户自定义类型 CLR UDT

? ? 一些复合类型进行范式分解是没有必要的,尤其是一些统一模型的情况下 ? ? DECLARE @i TimeBalance SET @i = CAST('0/102' AS TimeBalance) SELECT @i SELECT @i.ToString() SELECT @i.ToTimeString() SELECT @i.Distance() SELECT @i.Distance2('mi') ? ? SET @i = CAST('2015-1-1/2016-8-21' AS Tim

0914 练习题,类类型(用户自定义类型)

一,输入您的身份证号,识别是哪一年几月几日出生,并计算出你的年龄 Console.Write("请输入您的身份证号:"); // 输出一串字符“请输入您的身份证号” string s = Console.ReadLine(); // 把身份证这串字符定义在 s 这个变量里面  等待你输入(录入) string n = s.Substring(6,4); // 定义 n 等于 s里面从第6位开始数 后面的 4位数字 string y = s.Substring(10,2); // 定义

YTUOJ-计算该日在本年中是第几天(用户自定义类型)

题目描述 定义一个结构体变量(包括年.月.日).编写一个函数days,由主函数将年.月.日传递给函数days,计算出该日在本年中是第几天并将结果传回主函数输出. 输入 年月日 输出 当年第几天 样例输入 2000 12 31 样例输出 366 提示 主函数已给定如下,提交时不需要包含下述主函数 /* C代码 */ int main() { y_m_d date; int days(y_m_d); int day_sum; scanf("%d%d%d",&date.year,&a

YTUOJ-学生成绩输入和输出(用户自定义类型)

题目描述 编写一个函数print,打印一个学生的成绩数组,该数组中有5个学生的数据,每个学生的数据包括num(学号).name(姓名).score[3](3门课的成绩).编写一个函数input,用来输入5个学生的数据. 输入 5个学生的学号,姓名,3门课的成绩 输出 5个学生的学号,姓名,3门课的成绩 样例输入 1001 zhangsan 100 90 86 1002 lisi 90 20 80 1003 wangwu 90 90 89 1004 yanping 100 100 100 1005