前言
关于Lua中调用C/C 可以参考这篇文章:
Lua学习笔记:在Lua中调用C/C 函数示例
在学习了Lua中调用C/C 函数后,不免令人好奇,是否可以在Lua中定义一些函数来让C/C 来调用?我们甚至可以封装了C/C 的函数库供Lua模块化加载后再传递给需要的C/C 项目调用
工欲善其事必先利其器
首先要知道一点C/C 调用Lua里的函数是通过Lua提供的虚拟栈来完成参数的转移与获取的
"Lua将参数放到栈上, C/C 只能通过相应的api来操作这个值", lua api给c提供了一套完备的操作逻辑,C只需要根据Lua里函数的情况去调用相应的api就行了。
调用Lua函数示例
1.lua_pcall调用
这是最常用的方法,用于直接调用Lua函数,并可以在发生错误时捕获错误信息。
#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
- int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);
- L: Lua虚拟机
- nargs:压入栈的参数个数
- nresults:返回的参数个数
- msgh是0,返回在栈顶的错误消息就和原始错误消息完全一致。
// lua_pop(L,弹出栈顶元素数量1开始 );
#include <stdio.h>
#include <lua.hpp>
void Lua_Function5(lua_State* L)
{
// 获取堆栈已经存在多少元素
int top = lua_gettop(L);
printf("top[%d]n", top);
// 把全局变量 name 里的值压栈,返回该值的类型。
int tyep = lua_getglobal(L, "func2");
// 以保护模式调用函数 调用后必须还原堆栈,如果有2个返回值,必须lua_pop(L,2)
// 当函数调用完毕后,所有的参数以及函数本身都会出栈。 而函数的返回值这时则被压栈.
// int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);
// L: Lua虚拟机
// nargs:压入栈的参数个数
// nresults:返回的参数个数
// msgh是0,返回在栈顶的错误消息就和原始错误消息完全一致。
// 压入一个字符串
lua_pushstring(L, "Hello World");
// 压入一个数
lua_pushnumber(L, 1000);
// 压入一个布尔类型
lua_pushboolean(L, true);
// 调用函数
if (LUA_OK != lua_pcall(L, 3, 2, 0))
{
printf("error[%s]n", lua_tostring(L, -1));
}
printf("%d====n", lua_gettop(L));
const char* str = lua_tostring(L, -2); // 获取返回的值
int v = luaL_checknumber(L, -1);// -1表示栈顶(-2倒数第二个) 1表示栈底
printf("%s=====%dn", str, v);
// 进行堆栈平衡
// lua_pop(L, lua_gettop(L) - top); //弹出返回的数据
lua_pop(L, 2); //返回了2个数 上面是计算出来的弹出返回数据,可能出现执行出错
}
int LuaTest5()
{
// 创建一个虚拟机
lua_State* L = luaL_newstate();
// 加载一些常用的系统库
luaL_openlibs(L);
// 加载lua文件并执行 名称为 main.lua
if (luaL_dofile(L, "Test5.lua"))
{
// 在lua中 -1表示栈顶 如果出错 出错结果会放置在栈顶中
printf("%sn", lua_tostring(L, -1));
}
Lua_Function5(L);
// 关闭虚拟机
lua_close(L);
return 0;
}
代码语言:lua复制-- Test5.lua
function func2(a,b,c)
print(a,b,c)
return 'hello world',123
end
2.函数引用调用
lua_ref 和 lua_unref 是 Lua C API 中用于管理 Lua 函数或其他值的引用的函数。通过使用引用,可以将 Lua 栈上的值存储到 Lua 注册表中。
当需要多次调用同一个Lua函数时,可以使用 lua_ref 来保存对函数的引用,这样可以从全局变量中查找并调用。
代码语言:cpp复制#include <stdio.h>
#include <lua.hpp>
void Lua_refFunc(lua_State* L)
{
// 从全局环境中获取 "add" 函数,并压入栈顶
lua_getglobal(L, "add");
// 将栈顶的值(这里是 "add" 函数)保存到 Lua 注册表中,并返回一个整数引用
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
// 从注册表中根据保存的引用获取 "add" 函数,并压入栈顶
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushnumber(L, 5); // 第一个参数
lua_pushnumber(L, 3); // 第二个参数
// 调用 Lua 函数 "add",它接受两个参数,并期望返回一个结果
// 第四个参数为 0 表示没有错误处理函数
lua_pcall(L, 2, 1, 0);
// 获取栈顶的结果(即 "add" 函数的返回值)
//lua的number类型一般是浮点数类型将其转化为double
double result = lua_tonumber(L, -1);
printf("Result: %.1fn", result);
// 释放引用 - 从注册表中移除由 "ref" 指定的值
luaL_unref(L, LUA_REGISTRYINDEX, ref);
}
int LuaTest5()
{
// 创建一个虚拟机
lua_State* L = luaL_newstate();
// 加载一些常用的系统库
luaL_openlibs(L);
// 加载lua文件并执行 名称为 main.lua
if (luaL_dofile(L, "Test5.lua"))
{
// 在lua中 -1表示栈顶 如果出错 出错结果会放置在栈顶中
printf("%sn", lua_tostring(L, -1));
}
Lua_refFunc(L);
// 关闭虚拟机
lua_close(L);
return 0;
}
代码语言:lua复制-- Test5.lua
function add(x, y)
return x y
end
3.dostring调用
通过 lua_dostring 或者 luaL_dostring,可以直接在 Lua 虚拟机中执行一段字符串形式的 Lua 代码。这种方式非常适合动态生成的 Lua 代码或是在运行时需要解析和执行的 Lua 代码片段。
代码语言:cpp复制#include <stdio.h>
#include <lua.hpp>
void Lua_DoString(lua_State* L)
{
// 直接执行 Lua 代码
const char* luaCode = R"(
-- 调用 add 函数
return add(10, 20)
)";
if (luaL_dostring(L, luaCode) != 0) {
printf("Error executing Lua code: %sn", lua_tostring(L, -1));
return;
}
// 获取返回值
int result = luaL_checkinteger(L, -1);
printf("Result of the Lua code: %dn", result);
// 清理栈
lua_pop(L, 1);
}
void Lua_DoString1(lua_State* L)
{
// 直接执行 Lua 代码
const char* luaCode = R"(
function add(a, b)
return a b
end
return add(10, 20)
)";
if (luaL_dostring(L, luaCode) != 0) {
printf("Error executing Lua code: %sn", lua_tostring(L, -1));
return;
}
// 获取返回值
int result = luaL_checkinteger(L, -1);
printf("Result of the Lua code: %dn", result);
// 清理栈
lua_pop(L, 1);
}
int LuaTest5()
{
// 创建一个虚拟机
lua_State* L = luaL_newstate();
// 加载一些常用的系统库
luaL_openlibs(L);
// 加载lua文件并执行 名称为 main.lua
if (luaL_dofile(L, "Test5.lua"))
{
// 在lua中 -1表示栈顶 如果出错 出错结果会放置在栈顶中
printf("%sn", lua_tostring(L, -1));
}
Lua_DoString(L);
Lua_DoString1(L);
// 关闭虚拟机
lua_close(L);
return 0;
}
代码语言:lua复制-- Test5.lua
function add(x, y)
return x y
end
local data = { value = 10 }
总结
- Lua和C 是通过一个虚拟栈来交互的。
- C 调用Lua实际上是:由C 先把数据放入栈中,由Lua去栈中取数据,然后返回数据对应的值到栈顶,再由栈顶返回C 。
- Lua调C 也一样:C/C 先编写自己的模块函数,然后注册函数到Lua解释器中,最后由Lua去调用这个模块的函数。
参考文章
Lua与C/C 交互——C/C 调用Lua脚本