lua C API(3) - Lua 调用 C函数

从Lua中调用C函数,必须遵循一些协议来传递参数和获得返回结果。另外,从Lua调用C函数我们必须注册函数,也就是说,我们必须把C函数的地址以一个适当的方式传递给Lua解释器。

每一个函数都有他自己的私有栈,当Lua调用C函数的时候,第一个参数总是在这个私有栈的index=1的位置。

任何想注册到 lua 的函数必须拥有该原型,定义在 lua.h。

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

例子

//main.lua
function foo(x,y)
    a = x+y
    
    b,c = c_func(1)  --调用C函数
    
    return a,b,c
end
//main.c
#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

static int l_cf (lua_State *L) {
    double d = luaL_checknumber(L, 1);  //校验并获取
    
    //do something
    
    lua_pushnumber(L, d+1);  //第一个返回值压入栈
    lua_pushnumber(L, d+2);  //第二个返回值压入栈
    return 2;  //返回值个数
}

void main(){
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    //加载并执行代码
    if (luaL_loadfile(L, "main.lua") || lua_pcall(L, 0, 0, 0)){
        //如果失败,栈顶为错误信息
        printf("loadfile failed! %s\n", lua_tostring(L, -1));
    }
    
    //压入c函数
    lua_pushcfunction(L, l_cf);
    lua_setglobal(L, "c_func");   //设置到全局变量名c_func
    
    //获取全局变量函数并压入栈顶
    lua_getglobal(L, "foo");
    
    //压入2个参数
    lua_pushinteger(L, 2);
    lua_pushinteger(L, 3);
    
    //调用函数,2个传入参数,3个返回参数
    //调用完后弹出3个元素
    if (lua_pcall(L, 2, 3, 0) != 0){
        printf("error running function ’f’: %s",lua_tostring(L, -1));
        return;
    }
    //如果调用成功,3个返回结果压入栈
    
    int a = lua_tointeger(L, -3);  //获取第1个返回值
    int b = lua_tointeger(L, -2);  //获取第2个返回值
    int c = lua_tointeger(L, -1);  //获取第3个返回值
    
    printf("%d,%d,%d\n", a, b, c);  //5,2,3
}


C模块开发

一般来说,如果你想扩展你的 Lua 程序,最好写成c模块的方式供lua调用。

第一步:定义函数比如

static int leo_add(lua_State *L){

}

static int leo_two(lua_State *L){

}

第二步:声明一个数组来存放 调用函数名-实际函数名。

static const struct luaL_Reg mylib [] = {
    {"add", leo_add},
    {"say", leo_two},
    {NULL, NULL}  //标记结尾
};

第三步:声明一个主函数,利用 luaL_register。

int luaopen_mylib (lua_State *L) {
    luaL_register(L, "mylib", mylib);
    return 1;
}

第四步:我们把它编译成动态库比如 mylib.so,然后放在 package.cpath 指定的默认搜索路径上。

第五步:在lua文件里包含进来即可。

local mylib = require("mylib")

完整例子

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

static int leo_add(lua_State *L){
    double a = luaL_checknumber(L, 1);
    double b = luaL_checknumber(L, 2);
    
    lua_pushnumber(L, a+b);
    
    return 1;
}

//简单返回2个数字
static int leo_two(lua_State *L){
    lua_pushnumber(L, 100);
    lua_pushnumber(L, 200);
    
    return 2;
}

static const struct luaL_Reg mylib [] = {
    {"add", leo_add},
    {"two", leo_two},
    {NULL, NULL}  //标记结尾
};

int luaopen_mylib (lua_State *L) {
    luaL_register(L, "mylib", mylib);
    return 1;
}

编译

gcc mylib.c -fPIC -shared -o mylib.so -llua-5.1

如果是自己手动安装的lua或者luajit开发库,相对复杂,可以指定库路径,比如

gcc mylib.c -fPIC -shared -o mylib.so -llua-5.1 -I /usr/local/luajit2.1/include/luajit-2.1/ \
    -L /usr/local/luajit2.1/lib/

并指定运行时动态库搜索路径

[root@centos_242 ccc]# cat /etc/ld.so.conf.d/luajit.conf 
/usr/local/luajit2.1/lib
ldconfig

此时,一个动态库就生成了,接下来把它拷贝到lua默认动态库搜索路径,也可以放在自定义路径,但是在lua脚本文件里需要把该路径加到 package.cpath 里,假设我们把 mylib.so 放在 /root/ccc 目录里。

package.cpath = '/root/ccc/?.so;'..package.cpath

local mylib = require("mylib")

local sum = mylib.add(1,2)
print(sum)

local a,b = mylib.two()
print(a,b)
[root@centos_242 ccc]# lua main.lua 
3
100	200


上一篇: lua C API(2) - C 调用 Lua
下一篇: lua C API(4) - 编写C函数其他技术
作者邮箱: 203328517@qq.com