Liu Shouda coder

lua swig 和 embde example

2015-05-25
 

一直以来想知道lua和c++是如何通信的,也知道swig在这方面完成得很好。但是在网上一直没有找到swig与这方面比较相关的内容。今天安装了一下swig,到Example目录的lua里面看了几个例子。Examples对这些内容做了最好的诠释(难怪网上没有找到资料,因为不需要。。)。

首先simple就让人眼睛一亮。最简单的的c和lua的交互都在里面了。其次embde系列非常好的讲解了c和lua在实际使用中要做的事情。非常贴近实际。特别是示例三,很好地阐述引擎,事件机制。

1. simple

使用make,可以产生.so文件。而实际在工程上想生成cxx文件,则命令为:swig -lua -c++ example.i

2. embed

c-lua-c最简单的例子

  • 在c中要执行lua,调用lua_dostring, luaL_dostring(不同版本的api略有不同。这里做了简单的封装)
  • 在c中使用lua_State保存上下文环境。 使用luaopen_base(L)等来加载响应的库。如果这里想在lua里调用自定义的c函数,需要swig生成xx_warp.c文件。如这里luaopen_example即可
  • c中加载lua文件使用luaL_loadfile, lua_pcall.
  • 在lua中想调用swig后的c函数。需要指定模块。因为这里example.i里设置了module example。所有在使用的时候需要加上example。比如这里已经luaopen_example. 在lua中想调用example的gcd函数,需要example.gcd *【还未解决的问题】lua与c之间数据传输,数据类型统一问题

3. embed2

主要展示参数的传递。很重要的一个概念是c和lua通过栈传递对象。

top=lua_gettop(L);  /* for later */
lua_getglobal(L, "add");               /* function to be called */
if (!lua_isfunction(L,-1)) {
  printf("[C] error: cannot find function 'add'\n");
  lua_settop(L,top);
  return 0;
}
lua_pushnumber(L,a);
lua_pushnumber(L,b);
if (lua_pcall(L, 2, 1, 0) != 0)  /* call function with 2 arguments and 1 result */

其中可变参数call_va部分只不过是简单的字符串解析

为了让以上代码顺利运行,还需要在lua里将函数设定为全局

for k,v in pairs(example) do _G[k]=v end

4.embed3

引擎,事件,事件处理函数的。非常接近实际使用场景了

Event::STARTUP 给c++用的。在lua中被swig为Event_xxx(if e.mType==example.Event_STARTUP the),而且使用example进行引用即可。(注意这里的Event_STARTUP, 这是swig自动将类和其静态成员进行拼接生成的。在class Examples里,Shape_nshapes指的是Shape类的静态成员nShape)

而如果是函数,则调用方法不一样。pEngine:start(). 使用pEngine进行调用。这里与之前的教程调用不同主要在于,之前都只是普通函数。这里有类的概念。需要传入Engine实例才能对类函数进行调用。注意这里的函数调用使用的是”:”

那么传递给lua一个类的实例,是通过对象指针完成的。这里在传递的时候需要制定是否需要lua对该对象进行回收。最后这个参数为0表示不需要lua进行回收。push_pointer(L,&engine,"Engine *",0);

event机制的使用。

Event ev;
ev.mType = Event::STARTUP;
call_onEvent(L, ev);

在call_onEvent需要指定lua方面的对事件的解析函数。并且需要将event指针传到压栈,从而传递给lua

int call_onEvent(lua_State *L, Event e) {
  int top;
  /* ok, here we go:
  push a, push b, call 'add' check & return res
  */
  top = lua_gettop(L);  /* for later */
  lua_getglobal(L, "onEvent");               /* function to be called */
  if (!lua_isfunction(L, -1)) {
    printf("[C++] error: cannot find function 'OnEvent'\n");
    lua_settop(L, top);  // reset
    return 0;
  }
  // push the event object
  push_pointer(L, &e, "Event *", 0);
  if (lua_pcall(L, 1, 0, 0) != 0)  /* call function with 1 arguments and no result */
  {
    printf("[C++] error running function `OnEvent': %s\n", lua_tostring(L, -1));
    lua_settop(L, top);  // reset
    return 0;
  }
  lua_settop(L, top);  /* reset stack */
  return 1;   // ok
}

综上,简而言之,c++调用lua,还是使用push stack的方式。而封装内容,让lua调用c/c++,则使用的是swig

5. arrays

.i文件包含了几种封装方法。所有以上的所有前提是.i文件怎么写。不同的.i文件如何影响调用方式??

6. variables

lua操作c++中的变量.需要遵循变量定义。如果定义为只读,则不能进行修改。也可以在.i中将变量定义为只读%immutable;

可以在c++中封装分配内存函数。可以在lua中调用该函数进行操作。

int *new_int(int value) {
  int *ip = (int *) malloc(sizeof(int));
  *ip = value;
  return ip;
}

7. funtest

函数测试。可以使用返回值,也可以用参数来返回需要的值。add1和add5分别表示这两种情况。若需要额外的返回值,可以使用额外的参数列表。add3有两个返回值。add4通过y返回。

int add1(int x, int y)
void add2(int x, int *y, int *z)
int add3(int x, int y, int *z)
void add4(int x, int *y)
void add5(int x, int y, int *z) //add5是我自己加的
对应的.i文件的定义。注意这里的INPUT, OUTPUT, INOUT
extern int add1(int x, int y);
extern void add2(int x, int *INPUT, int *OUTPUT);
extern int add3(int x, int y, int *OUTPUT);    
extern void add4(int x, int *INOUT);
extern void add5(int x, int y, int* OUTPUT);

8. class

展示了lua使用普通c++类。

  • 直接调用构造函数进行初始化
  • 可以直接对成员赋值,直接调用函数
  • 不同之处在与调用collectgarbage()进行垃圾回收。
  • 静态成员使用”类名_变量”进行表示, 比如,Shape_nShape

Content