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

2018-08-31 17:34:01

从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


备注:

1.本文只是对模块加载机制做简单的介绍,如果有疑问可以给我留言
2.lua的版本为5.1,运行环境centos7 64位
3.原文地址http://www.freecls.com/a/2712/102


©著作权归作者所有
收藏
推荐阅读
  • lua C API(2) - C 调用 Lua

    Lua 可以作为程序库用来扩展应用的功能。同时,Lua 程序中可以注册有其他语言实现的函数,这些函数可能由C语言(或其他语言)实现,可以增加一些不容易由 Lua 实现的功能。这使得 Lua 是可扩展的...

  • lua C API(1) - 函数汇总

    在调用C API时有几个重要的头文件:lua.h:基础函数库,lua_ 前缀。lauxlib.h辅助库,luaL_ 前缀,利用 lua.h 实现的更高层的抽象。lualib.h为了保持Lua的苗条,所...

  • lua模块安装

    lua模块安装可以利用 luarocks 命令,在centos7 上执行以下来安装。yum install epel-release yum install luarocks此时我们安装模块的的时候可...

  • lua coroutine(协程)

    协程相关的文章网上有很多,众说纷坛,相比其他技术概念理解起来没那么直接。因为协程涉及了很多底层高并发概念,没接触过操作系统调度,C语言等底层知识根本不可能完全理解。备注:以下我说的单进程就是指一个进程...

  • lua模拟面向对象编程

    lua中跟对象和类最像的就是表格有方法和属性,所以类和对象都只能用表格来模拟声明一下,其实表格的功能已经能应付大部分场景了,而且lua脚本语言本就不是面向对象语言(它的优势是轻量级简单快速),硬要模拟...

  • nginx模块 ngx_http_headers_module

    ngx_http_headers_module 模块是用来增加 Expires 和 Cache-control,或者是任意的响应头。Syntax: add_header name value [alw...

  • nginx模块 ngx_http_gunzip_module、ngx_http_gzip_module、ngx_http_gzip_static_module

    ngx_http_gunzip_module 模块将文件解压缩后并在响应头加上 "Content-Encoding: gzip" 返回给客户端。为了解决客户端不支持gzip压缩。编译的时候带上 --w...

  • nginx模块 ngx_http_flv_module、ngx_http_mp4_module

    ngx_http_flv_module模块提供了对 flv 视频的伪流支持。编译的时候带上 --with-http_flv_module。它会根据指定的 start 参数来指定跳过多少字节,并在返回数...

  • nginx模块 ngx_http_fastcgi_module

    ngx_http_fastcgi_module 模块使得nginx可以与 fastcgi 服务器通信。比如目前要使得 nginx 支持 php 就得使用 fastcgi技术,在服务器上装上 nginx...

  • nginx模块 ngx_http_autoindex_module

    ngx_http_autoindex_module 模块可以将uri以 / 结尾时,列出里面的文件和目录。Syntax: autoindex on | off; Default: autoindex ...

简介
天降大任于斯人也,必先苦其心志。