Lua学习笔记:C/C++中调用Lua函数示例

2024-09-11 11:01:42 浏览数 (2)

前言

关于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,返回在栈顶的错误消息就和原始错误消息完全一致。
代码语言:cpp复制
// 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脚本

0 人点赞