零、前言
从前面分享的 “C++ 调用 Lua 函数” 文章知道,C/C++ 调用 Lua 函数时,是需要按照规则,将参数和函数压入栈中,然后通过 lua_pcall
或 lua_call
调用,最终再通过栈获取 Lua 返回的值。
同样 “Lua 调用 C++ 函数” 也需要遵循一定规则,而不是所有的 C/C++ 函数均可以进行调用。需要将 C/C++ 函数进行注册,将函数地址提供给 Lua 进行调用。交互过程同样使用了与 C/C++ 调用 Lua 函数时相同类型的栈,C/C++ 函数从栈中获取参数,并将结果压入栈中进行返回给 Lua 函数。
值得注意:
“Lua 调用 C/C++ 函数” 时,每次函数调用都有自己私有的局部栈,这不是一个全局栈。函数入参的第一个参数总位于局部栈中索引为 1 的位置,即栈底。
即使是 C/C++ 调用 Lua 代码,然后 Lua 代码又调用 C/C++ 函数,也是遵循这一规则,“Lua 代码又调用 C/C++ 函数” 这一过程的栈,是本次调用的私有栈。
一、Lua 如何调用 C/C++ 函数
第一步,定义被 Lua 调用的函数,该函数需要遵循以下格式:
// 所有要注册到 Lua 的函数都需要遵循这一格式
typedef int (*lua_CFunction) (lua_State *L);
这个函数需要一个指向 Lua 状态类型的指针作为参数。返回值为一个整数,表示 C/C++ 函数需要返回给 Lua 调用点多少个返回值,即被调用的 C/C++ 函数中真正压入栈的值个数。
这个函数在压入结果前不用先将栈进行清空,在函数返回后,Lua 会自动保存返回值并清空整个栈。
第二步,通过 lua_pushcfunction
注册函数。
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
lua_pushcfunction 会获取一个指向 C 函数的指针,然后在 Lua 中创建一个 “function” 类型,代表注册的函数。
第三步,将通过 lua_pushcfunction
压入栈的函数关联到 Lua 的变量中,这样 Lua 脚本才可以进行使用。
二、举个例子
我们编写一个获取目录下文件名的函数,例如使用我们的项目结构(如下图所示),当 Lua 中传入 “调用C++函数” 的路径时,会返回该文件夹下的所有文件名。
第一步,定义 C++ 函数。
l_dir
获取 Lua 传递的参数,会进行以下操作(可以结合代码的注释理解)
- 检测参数是否为字符串,如果不是会抛异常到 Lua
- 打开文件路径,检测文件是否存在,如果不存在则抛异常到 Lua
- 遍历文件夹下的文件,将其设置到 table 中