软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 编程语言 -> 快速掌握Lua5.3——从Lua中调用C函数 -> 正文阅读

[编程语言]快速掌握Lua5.3——从Lua中调用C函数


Q:Lua调用C函数的两种方式?


A:
1、程序主体在C中运行,C函数注册到Lua中。C调用Lua,Lua调用C注册的函数,C得到函数的执行结果。
2、程序主体在Lua中运行,C函数作为库函数供Lua使用。
第一种方式看起来很罗嗦,也很奇怪。既然程序主体运行在C中,而且最终使用的也是C中定义的函数,那么为何要将函数注册给Lua,然后再通过Lua调用函数呢?
相比于第一种方式,第二种方式使用的更加普遍。
一个Lua库(Lua本身所提供的库)实际上是一个定义了若干Lua函数的”chunk”,这些函数通常作为”table”的域来保存。一个C库(C语言编写,注册给Lua使用的库)的实现方式类似于Lua库的实现方式。首先C库中定义提供给Lua使用的函数,其次还需要一个“特殊函数”,它的作用是注册所有C库中的函数,并将它们存储在适当的位置(类似于Lua库中的函数作为”table”的域来保存)。
Lua可以调用C库中的函数,就是通过这个注册的过程实现的。一旦C函数注册到Lua中,Lua就可以直接通过C函数的引用获取到C函数的地址(这也是我们注册的意义,将C函数的地址提供给Lua)。换句话说,一旦C函数注册,Lua调用他们不依赖于函数名,”package”位置,或者是可见规则。
以上两种方式下面都会列举对应的例子,理解第一种方式,将有助于你理解第二种方式的实现流程。

Q:从Lua中调用C所遵循的规则?


A:当C调用Lua函数的时候,必须遵循一些简单的协议来传递参数和获取返回结果。同样的,从Lua中调用C函数,也必须遵循一些协议来传递参数和获得返回结果。此外,从Lua调用C函数我们必须注册函数,也就是说,我们必须把C函数的地址以一个适当的方式传递给Lua解释器。
任何在Lua中注册的C函数必须有同样的原型,
typedef int (*lua_CFunction) (lua_State *L); // 定义在"lua.h"中。
被注册的C函数接收一个单一的lua_State类型的参数,同时返回一个表示返回值个数的数字。函数在将返回值入栈之前无需清理栈,在函数返回之后,Lua会自动清除栈中返回结果下面的所有内容。

Q:如何在C中调用注册给Lua的C函数?


A:
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

static int l_sin(lua_State *L)
{
    // 如果给定虚拟栈中索引处的元素可以转换为数字,则返回转换后的数字,否则报错。
    double d = luaL_checknumber(L, 1);
    lua_pushnumber(L, sin(d));  /* push result */

    /* 这里可以看出,C可以返回给Lua多个结果,
     * 通过多次调用lua_push*(),之后return返回结果的数量。
     */
    return 1;  /* number of results */
}

int main(void)
{
    lua_State *L = luaL_newstate();    // 创建Lua状态机。
    luaL_openlibs(L);    // 打开Lua状态机"L"中的所有Lua标准库。

    /* 这两句话还有更简单的方法:
     * lua_register(L, "mysin", l_sin)
     * 将C函数"l_sin"定义为Lua的全局变量"mysin"。
     * 其实现是如下宏:
     * #define lua_register(L,n,f) \
     *      (lua_pushcfunction(L, f), lua_setglobal(L, n))
     */
    lua_pushcfunction(L, l_sin);    // 将C函数转换为Lua的"function"并压入虚拟栈。
    lua_setglobal(L, "mysin");    // 弹出栈顶元素,并在Lua中用名为"mysin"的全局变量存储。

    const char* testfunc = "print(mysin(3.14 / 2))";

    if(luaL_dostring(L, testfunc))    // 执行Lua命令。
        printf("Failed to invoke.\n");
    lua_close(L);    // 关闭Lua状态机。

    return 0;
}
prompt> gcc main.c -llua -ldl -lm -Wall
prompt> ./a.out
0.99999968293183

另一个例子(假定我们的系统符合”POSIX”标准)的功能类似于ls,将指定目录中的所有文件以数组的形式返回。当有错误发生时,返回nil加上一个描述错误信息的字符串。
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <dirent.h>
#include <errno.h>

static int l_dir(lua_State *L)
{
    DIR *dir;
    struct dirent *entry;
    int i = 0;
    // 如果给定虚拟栈中索引处的元素可以转换为字符串,则返回转换后的字符串,否则报错。
    const char *path = luaL_checkstring(L, 1);

    /* open directory */
    dir = opendir(path);
    if(dir == NULL) {
        // 出错返回"nil"加上一个描述错误信息的字符串。
        lua_pushnil(L);
        lua_pushstring(L, strerror(errno));
        return 2;    // "nil"加上字符串,共两个返回值。
    }

    /* create result table */
    lua_newtable(L);
    i = 1;
    while((entry = readdir(dir)) != NULL)    // 逐一读取目录中的文件。
    {
        lua_pushnumber(L, i++);  /* push key */
        lua_pushstring(L, entry->d_name);  /* push value */
        lua_settable(L, -3);    // t[k] = v
    }

    closedir(dir);

    return 1;    // 返回值只有一个,"table"。
}

int main(void)
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    // 将C函数"l_dir"定义为Lua的全局变量"mydir"。
    lua_register(L, "mydir", l_dir);

    // 打印"/home/"目录下的所有文件。
    const char* testfunc = "for i, v in pairs(mydir('/home')) do print(i, v) end";

    if(luaL_dostring(L, testfunc))    // 执行Lua命令。
        printf("Failed to invoke.\n");

    lua_close(L);

    return 0;
}
prompt> gcc main.c -llua -ldl -lm -Wall
prompt> ./a.out
1   vermilliontear
2   git
3   .
4   ..
5   lost+found

Q:如何在Lua中调用作为库函数提供给Lua的C函数?


A:我们使用上面的第一个例子,将其改造为C库的方式。
“mylib.c”文件中:
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

/* 所有注册给Lua的C函数具有
 * "typedef int (*lua_CFunction) (lua_State *L);"的原型。
 */
static int l_sin(lua_State *L)
{   
    // 如果给定虚拟栈中索引处的元素可以转换为数字,则返回转换后的数字,否则报错。
    double d = luaL_checknumber(L, 1);
    lua_pushnumber(L, sin(d));  /* push result */

    /* 这里可以看出,C可以返回给Lua多个结果,
     * 通过多次调用lua_push*(),之后return返回结果的数量。
     */
    return 1;  /* number of results */
}

/* 需要一个"luaL_Reg"类型的结构体,其中每一个元素对应一个提供给Lua的函数。
 * 每一个元素中包含此函数在Lua中的名字,以及该函数在C库中的函数指针。
 * 最后一个元素为“哨兵元素”(两个"NULL"),用于告诉Lua没有其他的函数需要注册。
 */
static const struct luaL_Reg mylib[] = {
    {"mysin", l_sin},
    {NULL, NULL}
};

/* 此函数为C库中的“特殊函数”。
 * 通过调用它注册所有C库中的函数,并将它们存储在适当的位置。
 * 此函数的命名规则应遵循:
 * 1、使用"luaopen_"作为前缀。
 * 2、前缀之后的名字将作为"require"的参数。
 */
extern int luaopen_mylib(lua_State* L)
{
    /* void luaL_newlib (lua_State *L, const luaL_Reg l[]);
     * 创建一个新的"table",并将"l"中所列出的函数注册为"table"的域。
     */ 
    luaL_newlib(L, mylib);

    return 1;
}

将”mylib.c”编译为动态连接库,
prompt> gcc mylib.c -fPIC -shared -o mylib.so -Wall
prompt> ls
mylib.c    mylib.so    a.lua

“a.lua”文件中:
--[[ 这里"require"的参数对应C库中"luaopen_mylib()"中的"mylib"。
     C库就放在"a.lua"的同级目录,"require"可以找到。]]
local mylib = require "mylib"

-- 结果与上面的例子中相同,但是这里是通过调用C库中的函数实现。
print(mylib.mysin(3.14 / 2))    --> 0.99999968293183

附加:


1、每一个与Lua通信的C函数都有其独有的虚拟栈。
2、在极端情况下,打印指定目录中文件的例子可能会造成小小的内存泄漏。在内存空间不足的情况下,l_dir()中的lua_newtable()lua_pushstring()lua_settable()都会立即抛出错误并终止程序的运行,这将导致closdir()无法被调用。
3、通常C库中“特殊的函数”都被定义为公有的(使用extern修饰),而其他函数均被定义为私有的(使用static修饰)。
4、当你想要使用C函数扩展你的Lua程序时,即使只有一个C函数,也最好使用C库的方式。因为在不久的将来(通常来说会很快),你将需要其他的C函数。
......显示全文...
    点击查看全文


上一篇文章      下一篇文章      查看所有文章
2016-04-04 00:13:46  
编程语言 最新文章
Java面试题(1)
ReactiveX序列——RxSwift
C++STL之ACM相关知识大全
c++中vector向量几种情况的总结(向量指针,
SSH框架整合demo
JAX
UVA
curl备忘(1)
C#机房重构——万事开头难(二)
OJ刷题
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年1日历
2018-1-17 9:23:38
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --