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. H5自带进度条&滑块

    一.H5自带进度条 <div id="d1"> <p id="pgv">进度:0%</p> <progress id= ...

  2. 解决有关null闪退及json解析数据中null的问题

    程序在获取某些数据之后莫名崩溃.其实很早就发现了原因: 由于服务器的数据库中有些字段为空, 然后以Json形式返回给客户端时就会出现这样的数据: "somevalue":null ...

  3. Flex tree展开节点问题!

    问题: 使用 for each(var item:XML in menuTree.dataProvider) {     menuTree.expandChildrenOf(item,true);   ...

  4. 使用命令:ssh-add 时,出现 “Could not open a connection to your authentication agent.”

    为 GitHub 账号设置 SSH Key时, 使用命令:ssh-add,出现“Could not open a connection to your authentication agent”,解决 ...

  5. [读书笔记] Python 数据分析 (十二)高级NumPy

    da array: 一个快速而灵活的同构多维大数据集容器,可以利用这种数组对整块的数据进行一些数学运算 数据指针,系统内存的一部分 数据类型 data type/dtype 指示数据大小的元组 str ...

  6. s5k4ba摄像头驱动分析

    注释: 本驱动是基于S5PV310的,但是全天下的摄像头驱动都是采用V4L2,因此驱动框架流程基本差不多.其中fimc_init_camera()函数会回调.init函数,该函数主要就是通过IIC总线 ...

  7. JAVA 多线程知识总结(一)

    一,线程的生命周期以及五种基本状态 关于JAVA线程的生命周期,首先看一下下面这张图 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌握了. Ja ...

  8. git pull 跟 fetch的区别

    今天在公司碰到个问题,公司不使用master分支作为主分支,而使用release分支作为主分支,这就碰到了个问题,也就是当clone一个项目下来的时候,如果master跟release分支有冲突,就不 ...

  9. 自备LocalDateTime工具类

    package cn.zytao.taosir.common.utils; import java.time.Instant; import java.time.LocalDate; import j ...

  10. LeetCode 11. Container With Most Water 单调队列

    题意 Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai ...