zoukankan      html  css  js  c++  java
  • lua-C++ userdata使用

    lua-C++ userdata使用

    所负责的产品使用非常灵活,可设置的参数上千个,而且还支持用户用lua进行流程控制,所以开发中要用到很多lua、C++混合编程。之前对这些也还是一知半解,只会依葫芦画瓢修改一些bug或者加些小小的新功能,而没有对这方面的知识进行系统性的学习和总结,蹭中秋假期,补充点这方面的知识。

    问题定义

    在开发过程中,我们经常会有这样的需求,可以分为一下几部完成:

    • 在lua中通过自定义C-API得到一个在C/C++中定义好的对象(数据结构);
    • lua通过C-API对该对象进行一系列操作。

    所以这里涉及到两个核心问题,一是如何把一个C/C++的对象传给lua;二是lua怎样把该对象传回C/C++,而且要提供一种C/C++能够确认该对象数据类型的机制。

    下面我们举一个例子,在C/C++中定义一个矩形(rectangle)的数据结构,提供C-API,能够使用lua读出这个矩形的信息。

    #ifdef __cplusplus
    extern "C" {
    #endif
        #include<lua.h>
        #include<lualib.h>
        #include<lauxlib.h>
    #ifdef __cplusplus
    }
    #endif
    
    typedef struct dt_rectangle_s
    {
        double left;
        double bottom;
        double right;
        double top;
    } dt_rectangle_t;
    
    typedef struct dt_line_s
    {
        double start;
        double end;
    } dt_line_t;
    
    /**
        调用lua_newuserdata新建一个rectangle对象
    */
    static int new_rectangle(lua_State *L)
    {
        dt_rectangle_t *p = (dt_rectangle_t*)
            lua_newuserdata(L, sizeof(dt_rectangle_t));
        p->left = 1;
        p->right = 2;
        p->bottom = 3;
        p->top = 4;
        return 1;
    }
    static int get_rect_left(lua_State *L)
    {
        dt_rectangle_t *p = (dt_rectangle_t*)
            lua_touserdata(L, -1);
        lua_pushnumber(L, p->left);
        return 1;
    }
    /**
        调用lua_newuserdata新建一个line对象
    */
    static int new_line(lua_State *L)
    {
        dt_line_t *p = (dt_line_t*)
            lua_newuserdata(L, sizeof(dt_line_t));
        p->start = 100;
        p->end = 200;
        return 1;
    }
    
    static luaL_Reg myfuncs[] = {
        {"new_rectangle", new_rectangle},
        {"get_rect_left", get_rect_left},
        {"new_line", new_line},
        {NULL, NULL}
    };
    
    extern "C" int luaopen_userdatatest(lua_State *L)
    {
        luaL_register(L, "userdatatest", myfuncs);
        return 1;
    }
    

    编译之后运行:

    $ lua
    Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
    > require("userdatatest")
    > rect = userdatatest.new_rectangle()
    > print(userdatatest.get_rect_left(rect))
    1
    

    看似可以工作了,但却存在一个问题,如:

    > line = userdatatest.new_line()
    > print(userdatatest.get_rect_left(line))
    100
    

    将一个line对象传给了get_rect_left函数,其返回了line的start,这种行为可以理解(在C++中只是得到了一个指针,我们返回的是指针指向第一个double类型),但是这回存在很多问题,比如访问越界之类的。

    利用metatable标记自定义数据

    metatable是lua中一个非常重要的概念,在lua程序中只能对table设置元表,而不能对其他的类型设置,但是在C/C++中却可以用lua_setmetatable对userdata设置元表,我们这是利用了这一点来标记userdata。

    前面的示例程序在new_rectangle、get_rect_left和luaopen_userdatatest函数稍作改变即可:

    static int new_rectangle(lua_State *L)
    {
        dt_rectangle_t *p = (dt_rectangle_t*)
            lua_newuserdata(L, sizeof(dt_rectangle_t));
        p->left = 1;
        p->right = 2;
        p->bottom = 3;
        p->top = 4;
        // 绑定元表
        luaL_getmetatable(L, "rectangle");
        lua_setmetatable(L, -2);
        return 1;
    }
    
    static int get_rect_left(lua_State *L)
    {
        //检查(L, -1)元表是否为rectangle
        void *p =  luaL_checkudata(L, -1, "rectangle");
        if(!p) {
            luaL_error(L, "p is NULL
    ");
        }
        lua_pushnumber(L, ((dt_rectangle_t*)p)->left);
        return 1;
    }
    
    extern "C" int luaopen_userdatatest(lua_State *L)
    {
        // 新建一个元表,挂到LUA_REGISTRYINDEX
        luaL_newmetatable(L, "rectangle");
        luaL_register(L, "userdatatest", myfuncs);
        return 1;
    }
    

    运行结果:

    $ lua
    Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
    > require("userdatatest")
    > rect = userdatatest.new_rectangle()
    > print(userdatatest.get_rect_left(rect))
    1
    > line = userdatatest.new_line()
    > print(userdatatest.get_rect_left(line))
    stdin:1: bad argument #-1 to 'get_rect_left' (rectangle expected, got userdata)
    stack traceback:
    	[C]: in function 'get_rect_left'
    	stdin:1: in main chunk
    	[C]: ?
    
    

    最近开发中遇到一个需求,要提供一个C-API判断lua对象是否属于某一指定的类型,如果是返回true,否则返回false。前面的例子中,luaL_checkudata判断不是该类型就报错返回,跟该需求有点不符,我们可以将luaL_checkudata源码稍作更改即可:

    static void *luaL_checkmydata (lua_State *L, int ud, const char *tname) {
        void *p = lua_touserdata(L, ud);
        if (p != NULL) {  /* value is a userdata? */
            if (lua_getmetatable(L, ud)) {  /* does it have a metatable? */
                lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get correct metatable */
                if (lua_rawequal(L, -1, -2)) {  /* does it have the correct mt? */
                    lua_pop(L, 2);  /* remove both metatables */
                    return p;
                }
            }
        }
        //注释掉这个报错返回
        //luaL_typerror(L, ud, tname);  /* else error */
        return NULL;  /* to avoid warnings */
    }
    
    $ lua
    Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
    > require("userdatatest")
    > line = userdatatest.new_line()
    > userdatatest.get_rect_left(line)
    stdin:1: p is NULL
    
    stack traceback:
    	[C]: in function 'get_rect_left'
    	stdin:1: in main chunk
    	[C]: ?
    > rect = userdatatest.new_rectangle()
    > print(userdatatest.get_rect_left(rect))
    1
    
    

    --http://www.jellythink.com/archives/587 Lua中的userdata

    --http://ju.outofmemory.cn/entry/103 lua metatable使用和源码分析(三)

  • 相关阅读:
    《Java4Android视频教程》学习笔记(二)
    漫画
    MyEclipse启动时报 Unable to acquire application service. Ensure that the org.eclips
    linux下关闭桌面模式使用命令行模式及其它模式
    nginx 开启fastcgi 可支持php、python、perl等多种语言
    linux下安装ImageMagick和Imagick扩展
    nginx 平滑升级到Tengine并编译concat
    centos 安装详解
    for xml path group by
    泛型对象Lists转xml
  • 原文地址:https://www.cnblogs.com/keviwu/p/5877679.html
Copyright © 2011-2022 走看看