Lua C API 教程
前一篇文章介绍了怎么从 C 程序中调用 Lua 代码。但内容并没有深入,还有很多东西需要反复去尝试,并且需要通过 Lua 辅助来调用 C 程序。 本章将着重介绍如何继续扩展你的 Lua 程序 - 在Lua中调用C函数。
剖析一个 Lua 程序对 C 的调用
从 Lua 的角度来看,调用 C 程序其实就是调用一个模块,在Lua中一个代码块称为chunk,一个chunk里面通常有若干函数,这些函数用table存储。而Lua的C扩展模块也是可以这样实现的,Lua调用C函数时,并不依赖于函数名、包的位置,而只依赖于注册函数时传入的函数地址。
一个简单的 Lua 程序,调用一个包含了 foo 和 bar 函数的 mylib 模块:
1 | local mylib = require "mylib" |
上面的代码 require "mylib" 语句加载了一个名为 mylib 的模块,显然模块具有函数 foo 和 bar。所以 C 程序不仅需要定义 foo() 和 bar() 函数,还需要将自己注册为一个模块,并将函数注入模块当中。
require "mylib" 做了两件事:
- 寻找一个叫做
mylib.so(windows 一个是mylib.dll) 的动态库文件。 - 一旦发现
mylib.so, 就会去查找一个名为luaopen_mylib的函数来运行。
通常,创建 C 模块时,函数都是私有的,都声明为static。一个模块函数定义大概是这样子:
1 | static int (lua_State *L) |
为了让模块函数注册到 Lua 中去,我们需要声明一个 luaL_Reg 的数组,来包含模块中的所有函数及名称。任何 luaL_Reg 数组都必须以一对为 NULL 的 name 与 func 结束:
1 | static const struct luaL_Reg mylib[] = { |
最后,声明一个打开模块的主函数,该函数名称必须以 luaopen_ 开头,后面跟着的是你编写的模块名称,它唯一的参数就是 Lua 状态机。并使用 luaL_newlib 创建一张新表,并把 luaL_Reg 中的函数注册进去:
1 | int luaopen_mylib(lua_State *L) { |
最棘手的莫过于编译一个写好的新模块,它看起来是这样子:
在 Mac OS 中:
1 | cc -o mylib.so mylib.c -I /usr/local/include -L /usr/local/lib -bundle -undefined dynamic_lookup |
在 Linux 中:
1 | gcc -o mylib.so mylib.c -I/usr/include/lua5.1 -llua5.1 -Wall -shared -fPIC |
做的更好一些
下面我们模拟编写一个名为 power 的模块,并实现两个名为 square 和 cube 的函数来进行计算任务,并把计算结果返回 Lua。
首先,编写模块 C 程序:
1 |
|
然后,把c程序编译成动态库 pwer.so。
ps:MAC OSX中编译时需要加入-bundle -undefined dynamic_lookup 选项,不然会引起”multiple lua vms detected” 错误。
1 | cc -o power.so power.c -I /usr/local/include -L /usr/local/lib -bundle -undefined dynamic_lookup |
别写测试 Lua 脚本:
1 | local power = require("power") |
运行:
1 | lua test_power.lua |
更进一步
如果你已经和 Lua 一起工作过一段时间后,你一个会注意到 Lua 没有内置 sleep() 延迟函数。一种解决方案是调用 C 的 sleep() 函数。我们做的是需要实现两个名为 sleep() 和 msleep 和函数来让 Lua 代码延迟指定多少秒、多少毫秒。


