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

2024-09-10 10:43:18 浏览数 (3)

前文须知

Lua的VS安装参考此文:

本文会通过一些示例展示如何用lua调用C/C 传递过来的函数,并辅以部分解释语句:


Lua中调用C/C 函数简介:

  • 任何在Lua中注册的C函数必须有同样的原型, typedef int (lua_CFunction) (lua_State L); 其定义在"lua.h"中。 被注册的C函数接收一个单一的lua_State类型的参数,同时返回一个表示返回值个数的数字。
  • 而Lua利用一个虚拟的堆栈来给C传递值或从C获取值。每当Lua调用C函数,都会获得一个新的堆栈,该堆栈初始包含所有的调用C函数所需要的参数值(Lua传给C函数的调用实参),并且C函数执行完毕后,会把返回值压入这个栈(Lua从中拿到C函数调用结果)。
  • 对lua堆栈不太理解的可以去搜 Lua初学者(四)--Lua调用原理展示(lua的堆栈)这篇文章

c/c 注册函数给lua调用

C/C 注册函数给lua的方式有多种

  1. 使用lua_register通过 _G 表间接地将函数注册到全局环境中
  2. lua_pushcfunction到栈里再通过lua_setglobal取出注册到_G表里或者通过
  3. 使用lua_rawsetfield /lua_setfield注册到特定的表里

1.函数注册到全局环境的方式

无参函数

代码语言:cpp复制
#include <stdio.h>
#include <lua.hpp>
extern "C" {
	// 一个Lua函数的标准模型
	LUALIB_API int lua_TestFunc2(lua_State* L)
	{
		printf("lua调用C函数n");
		// 表示有0个返回值
		return 0;
	}
}

int main()
{
	// 创建一个虚拟机
	lua_State* L = luaL_newstate();

	// 加载一些常用的系统库
	luaL_openlibs(L);

	// 注册一个函数给lua  以k v 的形式注册
	lua_register(L, "testFunc", lua_TestFunc2);

     /* 因为其宏定义为
     * #define lua_register(L,n,f) 
     * (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
     * 所以等价于
     * 向栈中压入一个函数至栈顶
     *  lua_pushcfunction(L, lua_TestFunc);	
     * 将栈顶的名称元素设置名称为testFunc 在lua中可以范围该名称
     *  lua_setglobal(L, "testFunc");
     */

	// 加载lua文件并执行
	luaL_dofile(L, "Test2.lua");

	// 关闭虚拟机
	lua_close(L);
	return 0;
}
代码语言:lua复制
-- Test2.lua
print("Lua_Hello World!")
testFunc()
  • 对于LUALIB_API 这是一个为了确保函数能够被正确地导出并在 Lua 中调用的宏
  • extern "C"是为了确保以C的编译器去编译,避免C 的编译器导致的错误,毕竟lua是纯C的
  • 一般要暴露给lua的函数都可以如上标注以防奇怪的错误。

有参函数的注册互动

代码语言:cpp复制
#include <stdio.h>
#include <lua.hpp>

extern "C" {
	// 一个Lua函数的标准模型
	LUALIB_API int lua_TestFunc3(lua_State* L)
	{
		// 获取有几个参数
		int top = lua_gettop(L);
		printf("传入几个参数 top:[%d]n", top);
		// 下标从1开始  检查第一个参数是否为整型,是则返回整型
		int num1 = luaL_checkinteger(L, 1);
		int num2 = luaL_checkinteger(L, 2);
		int num3 = luaL_checkinteger(L, 3);
		printf("从栈底由下到上开始取数据:%d %d %dn", num1,num2,num3);

		int num4 = luaL_checkinteger(L, -1);
		int num5 = luaL_checkinteger(L, -2);
		int num6 = luaL_checkinteger(L, -3);
		printf("从栈顶由上到下开始取数据:%d %d %dn", num4, num5, num6);

		lua_pushinteger(L, 999);// 压入第一个参数 整型
		lua_pushstring(L, "hello World!!!!!");// 压入地二个参数 字符串
		// 表示有2个返回值
		return 2;
	}
}

int main()
{
	// 创建一个虚拟机
	lua_State* L = luaL_newstate();

	// 加载一些常用的系统库
	luaL_openlibs(L);
	// 注册一个函数给lua全局环境
	lua_register(L, "testFunc", lua_TestFunc3);

	// 加载lua文件并执行
	if (luaL_dofile(L, "Test3.lua"))
	{
		// 在lua中 -1表示栈顶 如果出错 出错结果会放置在栈顶中
		printf("%sn", lua_tostring(L, -1));
	}
	// 关闭虚拟机
	lua_close(L);
	return 0;
}

通过从lua的栈里取出数据作为函数的参数使用

在push数据到lua的栈里后,需要函数的返回值告诉lua有几个返回值

Tips:

  • 正数索引是从栈底开始计数的,索引 1 表示栈底的第一个元素(即最先进入栈的元素),索引 2 表示栈底的第二个元素,依此类推。
  • 负数索引是从栈顶开始计数的,索引 -1 表示栈顶的元素(即最近进入栈的元素),索引 -2 表示栈顶之前的元素,依此类推。 通常建议使用负数索引

    0 人点赞