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\n");
}
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使用和源码分析(三)

lua-C++ userdata使用的更多相关文章

  1. Lua 之 userdata

    Lua 之 userdata 在Lua中可以通过自定义类型(user data)与C语言代码更高效.更灵活的交互,从而扩展Lua能够表达的类型. full userdata full userdata ...

  2. Lua继承userdata

    http://blog.csdn.net/mywcyfl/article/details/37765751 http://blog.csdn.net/teng_ontheway/article/det ...

  3. Lua中的userdata

    [话从这里说起] 在我发表<Lua中的类型与值>这篇文章时,就有读者给我留言了,说:你应该好好总结一下Lua中的function和userdata类型.现在是时候总结了.对于functio ...

  4. Lua.LearningLua.7-userdata

    Learning Lua: 7 - userdata 1. Userdata basic "There are eight basic types in Lua: nil, boolean, ...

  5. lua: Learning Official Doc notes

    dynamically typed vars: basic types: nil, boolean, number, string, function, userdata, thread & ...

  6. [lua] mac上如何编译snapshot(检测Lua中的内存泄露)

    最近我们的unity手游频繁闪退,只要进入战斗场景,之后一段时间就会闪退,如果是在unity编辑器中则会报出not enough memory的错误!猜测应该是有内存泄漏: 由于我们使用了tolua, ...

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

    前言 在看了uwa之前发布的<Unity项目常见Lua解决方案性能比较>,决定动手写一篇关于lua+unity方案的性能优化文. 整合lua是目前最强大的unity热更新方案,毕竟这是唯一 ...

  8. Lua基础语法讲解

    Lua 是什么? Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能. Lua 是巴西里约热内卢天主教大学( ...

  9. 浅析C++绑定到Lua的方法

    注:原文也在公司内部论坛上发了  概述       尽管将C++对象绑定到Lua已经有tolua++(Cocos2d-x 3.0用的就是这个).LuaBridge(我们游戏client对这个库进行了改 ...

  10. Step By Step(userdata)

    Step By Step(userdata) 在Lua中可以通过自定义类型的方式与C语言代码更高效.更灵活的交互.这里我们通过一个简单完整的示例来学习一下Lua中userdata的使用方式.需要说明的 ...

随机推荐

  1. 嵌入式Linux基础知识

    一.构建嵌入式开发环境 1.编译bootloader并烧写到板子中---uboot, 可以自己定制bootloader and logo 2.编译file system 并烧写--内嵌APP 3.编译 ...

  2. 移动web中的流式布局和viewport知识介绍

    1   流式布局 其实  流式布局  就是百分比布局,通过盒子的宽度设置成百分比来根据屏幕的宽度来进行伸缩,不受固定像素的限制,内容向两侧填充. 这样的布局方式  就是移动web开发使用的常用布局方式 ...

  3. [原创]c语言中const与指针的用法

    最近一直在准备笔试,补补大一大二欠下的课.复习c语言时碰见这么个题:   1 2 3 4 5 int a=248, b=4; int const c=21; const int *d=&a;  ...

  4. c# ExecuteScalar()

    ExecuteScalar这个方法是从数据库中检索单个值返回值是object类型,必须用与它在数据库里存放的类型相同类型或者可以转换成的类型,比如数据是nchar类型值为 "123" ...

  5. DRF lazy Serializer

    class LazySerializer: def __init__(self, cls_name, **kwargs): self.cls_name = cls_name self.kwargs = ...

  6. vscode代码格式化 空格的配置

    一个项目中同事使用webstorm,看我代码的时候说我传上去的会多出空格, VSCode 编辑 Setting.json文件,列出一些可以配置的项目   "javascript.format ...

  7. 嵌入式平台选择:树莓派 or BeagleBone Black(BBB)

    原文链接: Michael Leonard 翻译: 极客范- 小道空空 译文链接: http://www.geekfan.net/5246/ 嵌入式平台选择:树莓派 or BeagleBone Bla ...

  8. tomcat 映射虚拟路径

    编辑server.xml   在  <Host></Host>中添加 <Context path="/renbao/img/" docBase=&qu ...

  9. python 递归算阶乘 (转载)

    Python 递归函数 递归函数在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数.举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n,用函数 ...

  10. python中return和print的区别(详细)

    huskiesir最近在研究python哈,今天纠结一个问题,那就是return和print的区别,都是可以输出结果的,到底有啥区别呀?二话不多说,看下面的例子. #代码1: def break_wo ...