概述

最近在学习 云风的skynet库,想自定义一个模块,目标是使用lua调用C/C++所编写的库。

当我们需要在Lua里面调用c/c++函数时,所有的函数都必须满足以下函数签名:

typedef int (*lua_CFunction) (lua_State *L);

换句话说,所有的函数必须接收一个lua_State作为参数,同时返回一个整数值。因为这个函数使用Lua栈作为参数,所以它可以从栈里面读取任意数量和任意类型的参数。而这个函数的返回值则表示函数返回时有多少返回值被压入Lua栈。(因为Lua的函数是可以返回多个值的)。

lua调用C/C++的动态库

lua调用C动态库有如下几个步骤:

  • 写C动态库
  • lua调用C动态库

C动态库的重要部分 :定义一个 luaopen_* 函数,并调用 luaL_newlib 函数,这个函数相当于作为此动态库的main函数。需要注意:

  • luaopen_是此函数的前缀,不可修改。后面的内容是我们在lua中使用 require 引用此库时的字符串名称(假如名称中带有下划线,在使用require需要将下划线替换为点,例如:“mylib_test”->“mylib.test”)。
  • 在Lua5.0中调用的是luaL_openlib,但是在Lua5.3中,则是使用luaL_newlib

示例

看一个示例:

//snpccfg.cc
#ifdef __cplusplus
extern "C" {
#endif
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
#ifdef __cplusplus
}
#endif
#include <stdio.h>

static int test(lua_State *L)
{
    size_t len = 0;
	int num = luaL_checkinteger(L, 1);
	const char* str = luaL_checklstring(L, 2, &len);
	printf("come from test: num = %d,str = %s,len = %d\n", num, str, len);
	return 0;
}

extern "C" int luaopen_snpccfg(lua_State *L)
{
    luaL_Reg l[] = {
        {"test",test},	// key-val,相当于{"(lua调用时使用的函数名)",C定义函数名(函数指针)}
        {NULL,NULL}		// 必不可少
    };
    luaL_checkversion(L);
    luaL_newlib(L,l);
    return 1;
}

luaL_checkinteger() 和 luaL_checklstring()是用来获取参数的。
生成动态库:

gcc -fPIC -shared -o snpccfg.so snpccfg.cc -I./lua -L./lua -llua

需要通过-I和-L指定lua头文件和liblua.a库的路径

--test.lua
package.cpath = "./?.so"  --库路径
local snpccfg = require  "snpccfg"
snpccfg.test(1, "12312")

执行脚本

$ lua test.lua
come from test: num = 1 str = 12312     len = 5

遇到的问题

relocation R_X86_64_32S against luaT_typenames_’ can not be used when making a shared object; recompile with -fPIC

解决办法:重新编译liblua.a库,Makefile:CFLAGS=加上-fPIC ,而后再编译安装

$ make linux && make install

需要注意的几个点

  1. 在C++中编译成动态库,需要在函数 luaopen_snpccfg前面增加 extern “C”,否则在使用该动态库时会有如下错误:
lua: error loading module 'snpccfg' from file './snpccfg.so':
        ./snpccfg.so: undefined symbol: luaopen_snpccfg
stack traceback:
        [C]: in ?
        [C]: in function 'require'
        test.lua:2: in main chunk
        [C]: in
  1. 在C中则使用extern来修饰函数luaopen_snpccfg, 引用书中的话:
  • extern是计算机语言中的一个关键字,可置于变量或者函数前,以表示变量或者函数的定义在别的文件中。提示编译器遇到此变量或函数时,在其它模块中寻找其定义,另外,extern也可用来进行链接指定。
  • extern 后面修饰的只能是一个全局变量。

--完--