作者:糖果

在Lua中,可以使用loadlib的方式直接的加载C语言写的库,如同加载.lua文件一样。C写的模块可以做一些对效率要求相对比较高的模块,或是一些底层操作。下面举例

说明:

第一步:创建C模块文件。

foo.h头文件


#ifndef foo_h__

#define foot_h__

extern void foo(lua_State* L);

#endif

foo.c实现文件

#include <stdio.h>
#include "lauxlib.h"

void foo(lua_State* L)
{
    puts("Hello, I'm a shared library");
}

第二步:创建.o文件。

gcc -c -Wall -Werror -fpic foo.c -I/usr/include/lua5.1

注意一下的是.h文件中包含了"lauxlib.h"文件,所以要在编译的时候加上-I选项,后面追加.h文件的路径。

第三步:创建.so文件。

gcc -shared -o libfoo.so foo.o

如此操作后,”.so“文件就完成了生成,在使用libfoo.so动态库的时候,有以下几种方式让Lua找到库文件。

a). 设置LD_LIBRARY_PATH环境变量。

export LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH

b). 复制库文件到系统目录。

sudo cp /home/coding/workspace/libfoo.so /usr/lib
ldconfig 

用ldconfig更新一下缓冲,然后看是否生效。

ldconfig -p | grep foo

(也可以看一下.so文件是否关联到其他的库。ldd XXX{XXX为非lua文件,可以含有main函数的C程序。}

c). 把.so文件放到当前目录。

第四步:在lua中加载.so库。

test.lua文件。

f = package.loadlib("libfoo.so", "foo")
f()

以上,就是如何创建.so共享库,然后在Lua加载调用的过程,使用Lua版本是Lua5.15, 开发环境是在coding.net的WEB IDE的terminal终端环境。

之前so库,是没有传递参数的,下面我们用一个简单传参的例子来说明问题,然后以Makefile的形式编译共享程序。

首先要定义就是.h文件,定义最常见的接口add, sub。

#ifndef __tangguo_h__
#define __tangguo_h__
#include "lua.h"
#include "lualib.h" 
#include "lauxlib.h"

extern int add(lua_State* L);
extern int sub(lua_State* L);
static luaL_Reg libtangguo[] = {
    {"add", add},
    {"sub", sub},
    {NULL, NULL}
};  

#endif 

luaL_Reg 这个结构体相对很重要,下面是引用这个结构全的原型:

typedef struct luaL_Reg {
  const char *name;
  lua_CFunction func;
} luaL_Reg;

主要的元素:一个函数名字符串,另外一个是lua_CFunction的函数指针。
在定义时地函数最后要用两个NULL,作业结构体数据的结尾。

Type for arrays of functions to be registered byluaL_register.name is the function name and func is a pointer to the function.Any array of luaL_Reg must end with an sentinel entry in which both name and func are NULL.

lua_CFunction函数指针原型定义,如下:

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

下面是函数体的实体部分,所有函数的接口定义都是遵循lua_CFunction指针函数的原型定义,形参都是lua_State* L。

#include "tangguo.h"

int sub(lua_State* L) {
    double op1 = luaL_checknumber(L, 1);
    double op2 = luaL_checknumber(L, 2);
    lua_pushnumber(L, op1 - op2);
    return 1;
}

int add(lua_State* L) {
    double op1 = luaL_checknumber(L, 1);
    double op2 = luaL_checknumber(L, 2);
    lua_pushnumber(L, op1 + op2);
    return 1;
}

int luaopen_libtangguo(lua_State* L)
{
    luaL_openlibs(L);
    const char *libName = "libtangguo";
    luaL_register(L, libName, libtangguo);
    return 0;
}

涉及到lua调用c语言,面对的一个课题是,如何在c函数中取得lua传递的对数,如果将计算结果,返回给lua程序。在这种最常见的add、sub函数例子都数字运算,我们用luaL_checknumber这个函数,原型如下:

lua_Number luaL_checknumber (lua_State *L, int narg);

Checks whether the function argument narg is a numberand returns this number.

检查函数的参数是不是数字,返回这个数字。第一个参数是入参的状态机,第二个参数是lua调用c函数时,形参列表里第几个形参。

还有一个比较重要的函数,luaopen_libtangguo,这函数是用来注册这此函数。

luaL_register

lua_pushnumber

为了更好的适应编译环境,生成一个简单的Makefile, 需要注意的是LUALIB的定义要与你自己的环境相符。主要的参数是就是-I来指定.h的位置,-L用来定义用了那些库。

默认的编辑选项是要提定平台。

make linux

Makefile文件如下。

LUALIB=-I/usr/include/lua5.1 -L/usr/local/lib -ldl -lm

.PHONY: all win linux

all:
        @echo Please do \'make PLATFROM\' where PLATFORM is one of these;
        @echo win linux

win:

linux: libtangguo.so

libtangguo.so : tangguo.c
        #gcc --shared -Wall -fPIC -O2 $^ -o $@ $(LUALIB)
        gcc --shared -fPIC -O2 $^ -o $@ $(LUALIB)

clean:
        rm -f libtangguo.so

编译后会在当前目录生成.so文件,我们要以把.so文件复制到/usr/lib下

sudo ldconfig

我们来测试一下库是否工作,用package.loadlib直接了复对应的函数指钍。

local aadd = package.loadlib("libtangguo.so", "add")
local asub = package.loadlib("libtangguo.so", "sub")
local ret = aadd(1,5)
print(ret)

local ret = asub(6,3)
print(ret)

输出的结是:

3

6

后记:
涉及到lua调用c语言,面对的一个课题是,如何在c函数中取得lua传递的对数,如果将计算结果,返回给lua程序。

源码地址: