lua 是一门小巧高效的脚本语言,核心代码不到 500kb,由于要保持小巧,所以 lua 的核心库功能不可能很复杂,只实现一些基本功能,甚至没有操作目录的 API,但由于 lua 良好的设计,并且是用标准 C 开发,所以很容易用 C 语言来扩展 lua 的功能。
用 C 编写 lua 模块需要遵循一定的规则,由于 C 和 lua 中的数据结构是完全不一样的,并且 lua 函数可以返回多个结果,所以要有一种在 lua 脚本和 C 函数之间交换数据的机制,这个机制就是虚拟栈,这个栈可以储存任何类型的数据,供 lua 调用的 C 函数从这个栈中获取调用参数,然后将结果再压入栈中返回到 lua,这个虚拟栈用索引来操作,栈底(第一个入栈)的元素索引为 1, 第二个入栈的索引为 2,依次类推..., 这个虚拟栈也支持逆向索引,即栈顶(最后一个入栈)的元素索引为 - 1, 倒数第二个入栈的为 - 2,依次类推, 这两种索引方式依据实际情况都很常用。
lua 的 C API 提供了很多的从栈中读取数据以及向栈中添加数据的基本方法,下面列出一些常用方法:
- void lua_pushboolean (lua_State *L, int b); //向栈中压入一个bool值void lua_pushinteger (lua_State *L, lua_Integer n); //压入一个整数void lua_pushnil (lua_State *L); //压入一个nilvoid lua_pushnumber (lua_State *L, lua_Number n); //压入一个双精度数void lua_pushstring (lua_State *L, const char *s); //压入一个字符串int lua_toboolean (lua_State *L, int index); //从栈中读取一个bool值,index为读取的数据在栈中的索引lua_Integer lua_tointeger (lua_State *L, int index); //读取一个整数lua_Number lua_tonumber (lua_State *L, int index); //读取一个浮点数const char *lua_tostring (lua_State *L, int index); //读取一个字符串
以上方法第一个参数都是 lua_State *L, 我们可以认为该参数代表了虚拟栈, int index 是需要读取的元素在栈中的索引。
由于为 lua 编写的 C 函数从虚拟栈中读取参数,然后再把返回结果添加到栈中,所以为 lua 编写的 C 函数都只需要一个参数,就是这个虚拟栈 lua_State *L, 下面是 C 函数的函数原型:
- int funcname(lua_State * L);
int 类型的返回值表示该函数返回值的个数,下面是一个求两个数和的 C 函数示例:
- int sum(lua_State *L){int a = lua_tointeger(L, 1); //第一个加数,函数的第一个参数总是索引1int b = lua_tointeger(L, 2); //第二个加数lua_pushinteger(L, a + b); //压入结果return 1;//返回1表示该方法只有一个返回值}
函数写好了,那 lua 怎么才能调用该函数呢,第一种方法是使用 lua_pushcfunction 方法在 lua 的源码中直接注册这个函数,然后重新编译 lua 就可以使用这个方法了,第二种方法把这个函数编译进一个 lua C module,然后放入 lua 加载 C 模块的路径中,代码中 require 这个模块即可。由于第一种方法需要修改源码,并且每次修改 C 函数都需要重新编译 lua,显然很麻烦,所以这里主要介绍第二种方法。
编写 C 模块有固定的代码结构:
- //加载lua头文件#include //编写C函数 static使外部无法直接访问这个函数static int sum(lua_State *L){int a = lua_tointeger(L, 1); //第一个加数,函数的第一个参数总是索引1int b = lua_tointeger(L, 2); //第二个加数lua_pushinteger(L, a + b); //压入结果return 1;//返回1表示该方法只有一个返回值}//声明一个luaL_Reg结构体数组static const struct luaL_Reg funcs[] = {{"sum", sum}, //该结构体第一个成员是一个字符串,表示C模块中该函数的名称,第二个成员是指向该函数的指针{NULL, NULL}// 该数组最后一个元素始终是 {NULL, NULL}};//最后注册这个数组,并给这个C模块定义一个名字,注意,这个函数名不是固定的,如果你的模块名叫xxx,那么这个函数名就应该是 luaopen_xxxint luaopen_clib(lua_State *L){luaL_register(L, "clib", funcs);//第二个参数要和你的模块名一致return 1;}
把这个 C 文件编译成动态链接库,动态链接库的文件名命名规则为 xxx.so, 其中 xxx 就是你在 C 文件中定义的模块名
- gcc - fPIC - c clib.c - I / usr / include# - I指定lua头文件的位置gcc - shared clib.o / usr / lib64 / liblua - 5.1.so - o clib.so# / usr / lib64 / liblua - 5.1.so为你的lua库路径,根据你的实际路径修改
把编译好的 clib.so 放到你的 lua C 模块路径下,那怎么知道自己 lua 加载 C 模块的路径呢,在 lua 脚本中打印一下 package.cpath 就可以获取到这个路径
- [root@localhost lua-c-module]# luaLua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio> print(package.cpath)./?.so;/usr/lib64/lua/5.1/?.so;/usr/lib64/lua/5.1/loadall.so>
可以看到 我这里的 cpath 路径有当前目录 ./ 和 /usr/lib64/lua/5.1/ , 所以 我把 clib.so 移动到 / usr/lib64/lua/5.1/ 目录下
编写 lua 测试脚本
- local clib = require "clib"local s = clib.sum(3, 99) print(s)
执行结果:
- [root@localhost lua]#lua test.lua 102
这样,一个简单的 clib 模块就开发好了,是不是也很简单呢,想要更深入的了解 lua C 模块的编写技术,欢迎关注后续文章:)。就爱阅读 www.92to.com 网友整理上传, 为您提供最全的知识大全, 期待您的分享,转载请注明出处。
来源: http://www.92to.com/bangong/2017/08-18/27149923.html