lua userdata

userdata类型是为了方便C/C++对Lua进行扩展,因为在用C/C++扩展时,我们经常会自定义数据类型,如:

typedef struct epoll_fd
{ 
    int epfd;
    size_t size;
    struct epoll_event *events;
}epoll_fd_t;

我们要想在Lua中使用此类型的对象,就必须使用userdata类型来表示。

对于Lua而言,所有C/C++自定义的类型都是userdata类型,无法区分,那么在实际的应用中userdata都会设置metatable,通过metattable来区分不同的userdata类型。

metatable其实也是table,metatable中的方法则称为“元方法”。对于userdata的metatable,当我们访问userdata对象的成员时,lua会调用metatable中的__index()元方法,如果__index也是table,则从__index table中查找对应的成员。当lua要回收userdata对象时,会调用metatable中的__gc()元方法。还有很多元方法,可以查看文档说明。

  1. userdata常用的Lua C API

1) 创建userdata for lua

void *lua_newuserdata (lua_State *L, size_t size);
    This function allocates a new block of memory with the given size, pushes onto the stack a new full userdata with the block address, and returns this address.
    Userdata represent C values in Lua. A full userdata represents a block of memory. It is an object (like a table): you must create it, it can have its own metatable, and you can detect when it is being collected. A full userdata is only equal to itself (under raw equality).
    When Lua collects a full userdata with a gc metamethod, Lua calls the metamethod and marks the userdata as finalized. When this userdata is collected again then Lua frees its corresponding memory.

从上面的说明,可以知道返回的就是内存地址,我们只需要强制转换为我们的自定义类型即可使用。

2) 创建metatable:

int luaL_newmetatable (lua_State *L, const char *tname);
    If the registry already has the key tname, returns 0. Otherwise, creates a new table to be used as a metatable for userdata, adds it to the registry with key tname, and returns 1.
In both cases pushes onto the stack the final value associated with tname in the registry.

新创建的metatable会存放于registry中,其实registry也是一个table.

要从registry中获取指定的metatable:

void luaL_getmetatable (lua_State *L, const char *tname);
Pushes onto the stack the metatable associated with name tname in the registry

3) 设置metatable:

int lua_setmetatable (lua_State *L, int index);
    Pops a table from the stack and sets it as the new metatable for the value at the given acceptable index.

metatable其实也是table,所以所有对table操作的方法,均可用在metatable上,包括为metatable设置元方法等。

4) 从栈中获取userdata:

void *luaL_checkudata (lua_State *L, int narg, const char *tname);
Checks whether the function argument narg is a userdata of the type tname.
It will throws an error when the given value is not a userdata of the expected type.

2. 对lua-epoll模块进行简单的修改,

在上一篇博文的基础上,对其源码进行修改如下:

// lua_f_epoll.c
#include "lua_f_base.h"
#include "epoll_fd.h"

#define EPOLL_METATABLE_NAME "Epoll.MT"

#define check_epoll_fd(L) luaL_checkudata(L, 1, EPOLL_METATABLE_NAME);

static int lua_f_epoll_close(lua_State *L)
{
    epoll_fd_t *self = (epoll_fd_t *)check_epoll_fd(L);
    epoll_fd_release(self);
    return 0;
}

static int lua_f_epoll_register(lua_State *L)
{
    if(lua_gettop(L) < 3){
        RETERR("invalid args");
    }
    
    epoll_fd_t *self = (epoll_fd_t *)check_epoll_fd(L);
    int fd = luaL_checkint(L, 2);
    int event = luaL_checkint(L, 3);

    if(fd < 0){
        RETERR("invalid fd");
    }

    if(epoll_fd_add(self, fd, event) < 0){
        RETERR("epoll_fd_add fail");
    }

    lua_pushboolean(L, 1);
    return 1;
}

static int lua_f_epoll_modify(lua_State *L)
{
    if(lua_gettop(L) < 3){
        RETERR("invalid args");
    }
    
    epoll_fd_t *self = (epoll_fd_t *)check_epoll_fd(L);
    int fd = luaL_checkint(L, 2);
    int event = luaL_checkint(L, 3);

    if(fd < 0){
        RETERR("invalid fd");
    }

    if(epoll_fd_mod(self, fd, event) < 0){
        RETERR("epoll_fd_mod fail");
    }

    lua_pushboolean(L, 1);
    return 1;
}

static int lua_f_epoll_unregister(lua_State *L)
{
    if(lua_gettop(L) < 2){
        RETERR("invalid args");
    }
    
    epoll_fd_t *self = (epoll_fd_t *)check_epoll_fd(L);
    int fd = luaL_checkint(L, 2);

    if(fd < 0){
        RETERR("invalid fd");
    }

    if(epoll_fd_del(self, fd) < 0){
        RETERR("epoll_fd_del fail");
    }

    lua_pushboolean(L, 1);
    return 1;
}

static int lua_f_epoll_wait(lua_State *L)
{
    epoll_fd_t *self = (epoll_fd_t *)check_epoll_fd(L);
    int timeout = luaL_optint(L, 2, -1);

    int n = epoll_fd_wait(self, timeout);
    if(n < 0){
        RETERR("epoll_fd_wait fail");
    }

    lua_newtable(L);
    int i;
    for(i = 0; i < n; ++i){
        lua_pushinteger(L, self->events[i].events);
        lua_rawseti(L, -2, self->events[i].data.fd);
    }

    return 1;
}

static const struct luaL_Reg lua_f_epoll_func[] = {
    {"close", lua_f_epoll_close},
    {"register", lua_f_epoll_register},
    {"modify", lua_f_epoll_modify},
    {"unregister", lua_f_epoll_unregister},
    {"wait", lua_f_epoll_wait},
    {NULL, NULL},
};

static int lua_f_epoll_create(lua_State *L)
{
    size_t size = luaL_optint(L, 1, EPOLL_DEFAULT_SIZE);
    int epfd = epoll_create(size);
    if(epfd < 0){
        RETERR("epoll_create fail");
    }

    epoll_fd_t *self = (epoll_fd_t*)lua_newuserdata(L, sizeof(epoll_fd_t));
    if(epoll_fd_init(self, epfd, size) < 0){
        RETERR("epoll_fd_init fail");
    }

    luaL_getmetatable(L, EPOLL_METATABLE_NAME);
    lua_setmetatable(L, -2);
    return 1;
}

static int lua_f_epoll_version(lua_State *L)
{
    const char *ver = "Lua-Epoll V0.0.1 by wenhao.ye";
    lua_pushstring(L, ver);
    return 1;
}

static const struct luaL_Reg lua_f_epoll_mod[] = {
    {"create", lua_f_epoll_create},
    {"version", lua_f_epoll_version},
    {NULL, NULL},
};

/*
  mt = {
    __gc = lua_f_epoll_close,
    __index = {
        close = lua_f_epoll_close,
        register = lua_f_epoll_register,
        modify = lua_f_epoll_modify,
        unregister = lua_f_epoll_unregister,
        wait = lua_f_epoll_wait,
    },
  };

  epoll = {
    create = lua_f_epoll_create,
    version = lua_f_epoll_version,
    EPOLLIN = EPOLLIN,
	EPOLLPRI = EPOLLPRI,
	EPOLLOUT = EPOLLOUT,
	EPOLLRDNORM = EPOLLRDNORM,
	EPOLLRDBAND = EPOLLRDBAND,
	EPOLLWRNORM = EPOLLWRNORM,
	EPOLLWRBAND = EPOLLWRBAND,
	EPOLLMSG = EPOLLMSG,
	EPOLLERR = EPOLLERR,
	EPOLLHUP = EPOLLHUP,
	EPOLLRDHUP = EPOLLRDHUP,
	EPOLLONESHOT = EPOLLONESHOT,
	EPOLLET = EPOLLET,
  };
  
*/
int luaopen_epoll(lua_State *L)
{
    luaL_newmetatable(L, EPOLL_METATABLE_NAME);
    LTABLE_ADD_CFUNC(L, -1, "__gc", lua_f_epoll_close);
    lua_newtable(L);
    luaL_register(L, NULL, lua_f_epoll_func);
    lua_setfield(L, -2, "__index");
    lua_pop(L, 1);

    luaL_register(L, "epoll", lua_f_epoll_mod);

#define SETCONST(EVENT)     lua_pushnumber(L, EVENT);     lua_setfield(L, -2, #EVENT)

    // push const values into epoll table.
    SETCONST(EPOLLIN);
    SETCONST(EPOLLPRI);
    SETCONST(EPOLLOUT);
    SETCONST(EPOLLRDNORM);
    SETCONST(EPOLLRDBAND);
    SETCONST(EPOLLWRNORM);
    SETCONST(EPOLLWRBAND);
    SETCONST(EPOLLMSG);
    SETCONST(EPOLLERR);
    SETCONST(EPOLLHUP);
    SETCONST(EPOLLRDHUP);
    SETCONST(EPOLLONESHOT);
    SETCONST(EPOLLET);

    return 1;
}

上述代码用到的关于epoll的操作,我封装在epoll_fd,h文件中:

// epoll_fd.h
#ifndef _EPOLL_FD_H_
#define _EPOLL_FD_H_

#ifdef __cplusplus
extern "C"{
#endif

#include "def.h"
#include "io_tools.h" // for fd_set_nonblock()
#include <fcntl.h>
#include <sys/epoll.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#define EPOLL_DEFAULT_SIZE 1024 

typedef struct epoll_fd
{ 
    int epfd;
    size_t size;
    struct epoll_event *events;
}epoll_fd_t;

inline int epoll_fd_init(epoll_fd_t *self, int epfd, size_t size)
{
    self->epfd = epfd;
    self->size = size;
    self->events = (struct epoll_event *)MALLOC(sizeof(struct epoll_event) * size);
    if(self->events == NULL)
        return -1;

    return 0;
}

inline int epoll_fd_release(epoll_fd_t *self)
{
    safe_close(self->epfd);
    safe_free(self->events);
    return 0;
}

inline int epoll_fd_add(epoll_fd_t *self, int fd, int event)
{
    if(fd_set_nonblock(fd) < 0){
        ERR("fd_set_nonblock fail");
        return -1;
    }

    struct epoll_event ev;
    ev.data.fd = fd;
    ev.events = event;
    if(epoll_ctl(self->epfd, EPOLL_CTL_ADD, fd, &ev) < 0){
        ERR("epoll_ctl_add fail");
        return -1;
    }

    return 0;
}

inline int epoll_fd_mod(epoll_fd_t *self, int fd, int event)
{
    struct epoll_event ev;
    ev.data.fd = fd;
    ev.events = event;
    if(epoll_ctl(self->epfd, EPOLL_CTL_MOD, fd, &ev) < 0){
        ERR("epoll_ctl_mod fail");
        return -1;
    }

    return 0;
}

inline int epoll_fd_del(epoll_fd_t *self, int fd)
{
    if(epoll_ctl(self->epfd, EPOLL_CTL_DEL, fd, NULL) < 0){
        ERR("epoll_ctl_del fail");
        return -1;
    }

    return 0;
}

inline int epoll_fd_wait(epoll_fd_t *self, int timeout)
{
    return epoll_wait(self->epfd, self->events, self->size, timeout);
}

#ifdef __cplusplus
}
#endif

#endif

代码很简单,也就不过多说明了,上述代码如果直接拿来用会编译不过,因为这些代码是在我的整个工程中,会用到其他的一些宏或者函数,工程代码地址: https://git.oschina.net/yewenhao/libutils

如果不想拿整个工程,只需要把DBG(), ERR(), 换成简单的打印函数printf即可,其他的函数如下:

#define RETERR(_e)     DBG("err: %s", (_e));    lua_pushnil(L);     lua_pushfstring(L, "%s: %s", strerror(errno), (_e));     return 2
    
#define LTABLE_ADD_CFUNC(L, index, name, func)     lua_pushcfunction(L, (func));    lua_setfield(L, (index)-1, (name));
    
int fd_set_nonblock(int fd)
{
    int val;
    if((val = fcntl(fd, F_GETFL, 0)) == -1){
        perror("fcntl get");
        return -1;
    }

    val |= O_NONBLOCK;
    if(fcntl(fd, F_SETFL, val) == -1){
        perror("fcntl set");
        return -1;
    }
    
    return 0;
}

修改后的epoll模块的使用参考:

local epoll = require(‘epoll‘)
print(epoll.version())

local epfd = epoll.create()
epfd:register(fd, epoll.EPOLLIN)

while true do
    local events, err= epfd:wait()
    if not err then
        -- handle events
    end
    -- ...
end

epfd:unregister(fd)
epfd:close()
print(‘done‘)
时间: 2024-11-07 10:33:58

lua userdata的相关文章

用好lua+unity,让性能飞起来——lua与c#交互篇

前言 在看了uwa之前发布的<Unity项目常见Lua解决方案性能比较>,决定动手写一篇关于lua+unity方案的性能优化文. 整合lua是目前最强大的unity热更新方案,毕竟这是唯一可以支持ios热更新的办法.然而作为一个重度ulua用户,我们踩过了很多的坑才将ulua上升到一个可以在项目中大规模使用的状态.事实上即使到现在lua+unity的方案仍不能轻易的说可以肆意使用,要用好,你需要知道很多. 因此,这篇文章是从一堆简单的优化建议里头,逐步挖掘出背后的原因.只有理解了原因,才能很清

用好Lua+Unity,让万金6.0平台搭建性能飞起来——Lua与C#交互篇

前言万金6.0平台搭建论坛:haozbbs.com Q1446595067 在看了uwa之前发布的<Unity项目常见Lua解决方案性能比较>,决定动手写一篇关于lua+unity方案的性能优化文.整合lua是目前最强大的unity热更新方案,毕竟这是唯一可以支持ios热更新的办法.然而作为一个重度ulua用户,我们踩过了很多的坑才将ulua上升到一个可以在项目中大规模使用的状态.事实上即使到现在lua+unity的方案仍不能轻易的说可以肆意使用,要用好,你需要知道很多.因此,这篇文章是从一堆

lua绑定C++对象系列一——基础知识

本文主要介绍lua绑定C++对象的原理和方法,并能在C/C++定义类和方法,在lua中创建C++类的句柄实例,像面向对象一样去使用C++类实例.为了便于大家理解,系列文章会从基础知识讲解,并通过多个版本的进化,一步步完成从基础到多版本实践的完美结合和深入,彻底理解lua绑定C++对象的原理方法.在阅读本系列文章前,需要具备一定的lua开发经验以及lua与C/C++相互调用操作的知识. 1.基础C/C++和Lua的相互引用调用 我们知道C和lua相互调用,是通过虚拟栈进行数据传递通信的,基础介绍介

lua笔记之userdata

1.一直使用框架里封装好的c库,想着自己一点一点的写些例子,学习下,以后需要c库,可以自己写了. 下边是一个简单的userdata的例子--数组操作. newarray.c #include "lua.h" #include "lauxlib.h" #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> typedef

Lua 与C/C++ 交互系列:Light userdata翻译

利用零碎的时间,先把以后用的知识点提前准备好.最近比较忙,正在准备一篇绑定C++对象到Lua中.但是,不想轻易下手,希望做足准备. 这篇翻译来自于lua-users.org   ,原文地址. Light User Data Light userdata, like heavy userdata, are a form of userdata, which is one of the basic data types in Lua .Light userdata are characterized

快速掌握Lua 5.3 —— userdata (1)

Q:什么是"userdata"? A:"userdata"分为两类,"full userdata"和"light userdata".Lua使用他们来表示C中一些特殊的类型.前面的章节中,我们看到了如何通过C编写新的函数来扩展Lua:使用"userdata",我们将可以通过C编写新的类新来扩展Lua. Q:两种"userdata"的区别? A: \ "full userdata

快速掌握Lua 5.3 —— userdata (2)

Q:如何使用"userdata"的"metamethods"? A:我们继续来修改上一节中的例子,这次我们的目标是使用面向对象的方式调用"userdata"的方法.这个目标既可以在Lua中实现,也可以在C库中实现,我们先来看一个比较简单的方式,在Lua中实现."mylib.c"中代码无需更改,只需要修改"a.lua"中的代码, local array = require "mylib"

Lua中的userdata

[话从这里说起] 在我发表<Lua中的类型与值>这篇文章时,就有读者给我留言了,说:你应该好好总结一下Lua中的function和userdata类型.现在是时候总结了.对于function,我在<Lua中的函数>这篇文章中进行了总结,而这篇文章将会对Lua中的userdata进行仔细的总结.对于文章,大家如果有任何疑议,都可以在文章的下方给我留言,也可以关注我的新浪微博与我互动.学习,就要分享,我期待你的加入. [userdata是啥?] userdata是啥?简单直译就是用户数

Lua中Userdata类型源码实现

1.概述 Lua中userdata分两种,一种是轻量级userdata(light userdata),轻量级userdata是一种表示C指针的值,对Lua虚拟机来说,这种数据类型不需要GC(垃圾回收),其指向的内存由用户分配和释放,其实现就是一个void *p指针:后一种userdata类型完全userdata(full userdata),内存是由Lua虚拟机分配,并有GC机制负责处理.下面将通过Lua 5.2.1的源码来看后一种userdata的实现. 2.源码实现 userdata内存存