C++和Lua交互总结

news2024/9/20 18:01:46

C++和Lua交互总结

  • Chapter1. C++和Lua交互总结
    • 一、Lua与C++的交互机制——Lua堆栈
    • 二、堆栈的操作
    • 三、C++ 调用 Lua
      • 1)C++获取Lua值
      • 2)C++调用Lua函数
      • 示例:
    • 四、Lua 调用 C++
      • 包装C++函数
    • 最后总结一下


Chapter1. C++和Lua交互总结

原文链接:https://blog.csdn.net/qq826364410/article/details/88624824/

一、Lua与C++的交互机制——Lua堆栈

Lua和C++ 的交互机制的基础在于Lua提供了一个虚拟栈,C++ 和Lua之间的所有类型的数据交换都通过这个栈完成。无论何时C++想从Lua中调用一个值,被请求的值将会被压入栈,无论何时C++想要传递一个值给Lua,首先将整个值压栈,然后就可以在Lua中调用。

Lua中,对虚拟栈提供正向索引和反向索引两种索引方式,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。重要!后面所有的交互,都是基于Lua的虚拟栈来通信。假设当前Lua的栈中有5个元素,如下图所示:
在这里插入图片描述

二、堆栈的操作

因为lua与c/c++是通过栈来通信,lua提供了C API对栈进行操作。

我们先来看一个最简单的例子:

#include <iostream>  
#include <string.h>  
using namespace std;
 
extern "C"
{
#include "lua.h"  
#include "lauxlib.h"  
#include "lualib.h"  
}
 
int main()
{
	//1.创建一个state  
	// luaL_newstate返回一个指向堆栈的指针
	lua_State *L = luaL_newstate();
 
	//2.入栈操作  
	lua_pushstring(L, "I am so cool~");
	lua_pushnumber(L, 20);
 
	//3.取值操作  
	if (lua_isstring(L, 1)) {             //判断是否可以转为string  
		cout << lua_tostring(L, 1) << endl;  //转为string并返回  
	}
	if (lua_isnumber(L, 2)) {
		cout << lua_tonumber(L, 2) << endl;
	}
 
	//4.关闭state  
	lua_close(L);
 
	getchar();
	return 0;
}

在执行这个例子之前,我们需要引入Lua.lib静态库,也就是上文中extern "C"中执行的include。

extern “C”
主要作用就是为了能够正确实现C++ 代码调用其他C语言代码。加上extern “C”后,会指示编译器这部分代码按c语言的进行编译,而不是C++ 的。由于C++ 支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名,比如_Log_int_int;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名,比如_Log。

void Log(int a, int b){}

Lua源代码的下载地址: http://www.lua.org/ftp/,但没有提供相应的静态库,只有.c和.h文件,需要我们自己生成静态库。

生成Lua静态库方法: https://blog.csdn.net/qq826364410/article/details/88563408

也可以直接下载我生成好的静态库:https://download.csdn.net/download/qq826364410/11029611

好了,我们有了Lua静态库,就可以开心的玩耍了。

引入Lua静态库

  • 首先,新建一个Visual C++的空项目,右键点击工程属性,选择VC++目录,
  • 把lua工程中的.h头文件所在的目录加到包含目录中
  • 把Lua静态库文件所在的目录加到库目录中,
  • 最后,点击链接器->输入->附加依赖项->加上生成的Lua静态库,比如Lua.lib,记得用分号";"与其他库隔开。
    OK,大功告成!
    Lua虚拟栈在源码中是如何实现的:

Lua栈是在创建lua_State时建立的,TValue stack[max_stack_len] ,欲知内情可以查 Lua源码lstate.c的stack_init函数
Lua栈可以存储数字,字符串,表,闭包等,它们最终都用TValue这种数据结构来保存 。
在这里插入图片描述
TValue结构对应于lua中的所有数据类型, 是一个{值, 类型} 结构, 这就lua中动态类型的实现, 它把值和类型绑在一起, 用tt记录value的类型, value是一个联合结构, 由Value定义, 可以看到这个联合有四个域, 先说明简单的
p – 可以存一个指针, 实际上是lua中的light userdata结构
n – 所有的数值存在这里, 不管是int , 还是float
b – Boolean值存在这里, 注意, lua_pushinteger不是存在这里, 而是存在n中, b只存布尔
gc – 其他诸如table, thread, closure, string需要内存管理垃圾回收的类型都存在这里
gc是一个指针, 它可以指向的类型由联合体GCObject定义, 从图中可以看出, 有string, userdata, closure, table, proto, upvalue, thread
可以的得出如下结论:
1. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关。
2. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收。

三、C++ 调用 Lua

C++ 可以获取Lua中的值,可以调用Lua函数,还可以修改Lua文件

1)C++获取Lua值

使用lua_getglocal来获取值,然后将其压栈
使用lua_toXXX将栈中元素取出转成相应的C++类型的值
如果Lua值为table类型的话,通过lua_getfield和lua_setfield获取和修改表中元素的值

2)C++调用Lua函数

使用lua_getglobal来获取函数,然后将其压入栈;
如果这个函数有参数的话,就需要依次将函数的参数也压入栈;
这些准备工作都准备就绪以后,就调用lua_pcall开始调用函数了,调用完成以后,会将返回值压入栈中;

示例:

新建一个简单的lua放到工程的同级目录下
hello.lua

str = "I am x-man."
tbl = {name = "DC", id = 20114442}
function add(a,b)
	return a + b
end

然后,我们写一个Lua1.cpp来访问lua中的数据

#include <iostream>
#include <string.h>
using namespace std;
 
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
int main()
{
	//1.创建Lua状态,返回一个指向堆栈的指针
	lua_State *L = luaL_newstate();
	if (L == NULL)
	{
		return;
	}
 
	//2.加载lua文件
	int bRet = luaL_loadfile(L, "hello.lua");
	if (bRet)
	{
		cout << "load file error" << endl;
		return;
	}
 
	//3.运行lua文件
	bRet = lua_pcall(L, 0, 0, 0);
	if (bRet)
	{
		cout << "pcall error" << endl;
		return;
	}
 
	//4.读取全局变量,
	// 1.把 str 压栈 2.由lua去寻找全局变量str的值,并将str的值返回栈顶(替换str)
	// 如果存在相同命名的其他变量、table或函数,就会报错(读取位置发生访问冲突)
	lua_getglobal(L, "str");
	// -1取出栈顶元素,转化为string
	string str = lua_tostring(L, -1);
	cout << "str = " << str.c_str() << endl;
 
	//5.读取table,把table压栈
	lua_getglobal(L, "tbl");
	//-------------------------------
	// 1.把name压入栈中,2.由lua去寻找table中name键的值,并将键值返回栈顶(替换name)
	// 相当于lua_pushstring(L, "name") + lua_gettable(L, -2)执行结果是一样的
	lua_getfield(L, -1, "name");
	// 把name压入栈中
	//lua_pushstring(L, "name");
	// 弹出栈上的name,并从表中找到name的键值,把结果放在栈上相同的位置
	//lua_gettable(L, -2);
	//---------------------------------
	str = lua_tostring(L, -1);
	// 因为table在栈顶的下面,所以取-2,把id压栈,由lua找到table中id键的值,并返回栈顶(替换id)
	lua_getfield(L, -2, "id");
	// id的值已经在栈顶,取-1
	int id = lua_tonumber(L, -1);
	cout << "tbl:name = " << str.c_str() << endl;
	cout << "tbl:id = " << id << endl;
 
	// 读取函数,
	// 1.将函数add放入栈中,2.由lua去寻找函数add,并将函数add返回栈顶(替换add)。
	lua_getglobal(L, "add");		// 获取函数,压入栈中
	lua_pushnumber(L, 10);			// 压入第一个参数
	lua_pushnumber(L, 20);			// 压入第二个参数
	// 栈过程:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈
	// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。
	int iRet = lua_pcall(L, 2, 1, 0);
	if (iRet)						
	{
		// 调用出错
		const char *pErrorMsg = lua_tostring(L, -1);
		cout << pErrorMsg << endl;
		lua_close(L);
		return;
	}
	if (lua_isnumber(L, -1))        //取值输出
	{
		int fValue = lua_tonumber(L, -1);
		cout << "Result is " << fValue << endl;
	}
 
	// 栈的索引方式可以是正数也可以是负数,区别是:1永远表示栈底元素,-1永远表示栈顶元素。
	//至此,栈中的情况是:
	//=================== 栈顶 =================== 
	// 索引    类型      值
	// 5或-1   int       30 
	// 4或-2   int       20114442
	// 3或-3   string    shun 
	// 2或-4   table	 tbl
	// 1或-5   string	 I am so cool~
	//=================== 栈底 =================== 
 
	lua_pushstring(L, "Master");
	// 会将"Master"值出栈,保存值,找到到table的name键,如果键存在,存储到name键中
	lua_setfield(L, 2, "name");
	// 读取
	lua_getfield(L, 2, "name");
	str = lua_tostring(L, -1);
	cout << "tbl:name = " << str.c_str() << endl;
 
	// 创建新的table
	lua_newtable(L);
	lua_pushstring(L, "A New Girlfriend");
	lua_setfield(L, -2, "name");
	// 读取
	lua_getfield(L, -1, "name");
	str = lua_tostring(L, -1);
	cout << "newtbl:name = " << str.c_str() << endl;
 
	//7.关闭state
	// 销毁指定 Lua 状态机中的所有对象, 并且释放状态机中使用的所有动态内存。
	// (如果有垃圾收集相关的元方法的话,会调用它们)
	lua_close(L);
 
	getchar();
	return 0;
}

代码中,已经有很详细的注释了,这里总结一下:

1. 读取lua的全局变量:

lua_getglobal(L, "str"); 

内部实现:1.把全局变量 str 里的值压栈 2.由lua去寻找全局变量str的值,并将str的值返回栈顶(替换str)
注意:如果存在相同命名的其他变量、table或函数,就会报错(读取位置发生访问冲突)

2. 读取table中的键值:

lua_getglobal(L, "tbl");
lua_getfield(L, -1, "name");  

lua_getglobal方法跟上面的实现是一样的。

lua_getfield方法:

内部实现:1.把name压入栈中,2.由lua去寻找table中name键的值,如果键存在,将键值返回栈顶(替换name)

注意:这里的参数-1,就是表示把table中的键值返回到栈顶。

  1. 调用lua中的函数:
lua_getglobal(L, "add");		// 获取函数,压入栈中
lua_pushnumber(L, 10);			// 压入第一个参数
lua_pushnumber(L, 20);			// 压入第二个参数
// 栈过程:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈
// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数,
// iRet为0表示调用成功
int iRet = lua_pcall(L, 2, 1, 0);

四、Lua 调用 C++

Lua可以调用由C++定义、实现具体的函数
步骤:

  • 将C++的函数包装成Lua环境认可的Lua_CFunction格式
  • 将包装好的函数注册到Lua环境中
  • 像使用普通Lua函数那样使用注册函数

包装C++函数

为了从Lua脚本中调用C++函数,需要将被调用的C++函数从普通的C++函数包装成Lua_CFunction格式,并需要在函数中将返回值压入栈中,并返回返回值个数。

int (*lua_CFunction) (lua_State *L);

例如有一个C++函数:

int add(int a,int b)
{
    return a+b;
}

包装为:

int add(lua_state *L)
{
    int a = lua_tonumber(-1);
    int b = lua_tonumber(-2);
    int sum = a+b;
    // 将返回值压入栈中
    lua_pushnumber(L,sum);
    // 返回返回值个数
    return 1;
}

示例:
新建一个简单的lua放到工程的同级目录下
avg.lua

avg, sum = average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)
 
print("age", age)
for k,v in pairs(newTable) do 
     print("k = ",k," v = ",v)
end
print("name", newTable.name)

然后,创建一个Lua2.cpp:

#include <stdio.h>
 
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
 
static int average(lua_State *L)
{
	/* 得到参数个数 */
	int n = lua_gettop(L);
	double sum = 0;
	int i;
 
	/* 循环求参数之和 */
	for (i = 1; i <= n; i++)
	{
		/* 求和 */
		sum += lua_tonumber(L, i);
	}
	/* 压入平均值 */
	lua_pushnumber(L, sum / n);
	/* 压入和 */
	lua_pushnumber(L, sum);
	/* 返回返回值的个数 */
	return 2;
}
 
int main(int argc, char *argv[])
{
	/* 初始化Lua */
	/* 指向Lua解释器的指针 */
	lua_State* L = luaL_newstate();
 
	/* 载入Lua基本库 */
	luaL_openlibs(L);
	
	/* 注册函数 */
	lua_register(L, "average", average);
	
	// 设置lua中的全局变量
	lua_pushinteger(L, 18);   //入栈
	lua_setglobal(L, "age");  //1.先将18值出栈,保存值,2.在lua中,把值存储到全局变量age中
	
	// 设置lua中table
	lua_newtable(L); //创建一张空表,并将其压栈
	lua_pushstring(L, "lili");// 入栈
	// 1.先将"lili"值出栈,保存值,2.找table的name键,如果键存在,存储到name键中
	lua_setfield(L, -2, "name");//栈顶是lili,新创建的table在lili下,所以是-2
	// 将table赋值到lua中,并弹出table
	lua_setglobal(L, "newTable");
 
	/* 运行脚本 */
	luaL_dofile(L, "avg.lua");
	/* 清除Lua */
	lua_close(L);
 
	/* 暂停 */
	printf("Press enter to exit…");
	getchar();
	return 0;
}

1. 读取C++的变量:

// 设置lua中的全局变量
lua_pushinteger(L, 18);   //入栈
lua_setglobal(L, "age");  

lua_setglobal(L, “age”) 内部实现: 1.先将值出栈,保存值,2.在lua中,把值存储到全局变量age中

2. 调用在C++中创建的新表的元素:

// 设置lua中table
lua_newtable(L); //创建一张空表,并将其压栈
lua_pushstring(L, "lili");// 入栈
// 1.先将"lili"值出栈,保存值,2.找table的name键,如果键存在,存储到name键中
lua_setfield(L, -2, "name");//栈顶是lili,新创建的table在lili下,所以是-2
// 将table赋值到lua中,并弹出table
lua_setglobal(L, "newTable");

lua_setglobal(L, “newTable”) 内部实现: 1.先将table出栈,保存table,2.在lua中,存储到newTable表中

在lua中,print(“name”, newTable.name),使用newTable.name调用在C++中创建的新表的元素。

3. 调用C++中的函数:

将C++的函数包装成Lua环境认可的Lua_CFunction格式
将包装好的函数注册到Lua环境中
像使用普通Lua函数那样使用注册函数
包装C++函数:

static int average(lua_State *L)
{
	/* 得到参数个数 */
	int n = lua_gettop(L);
	double sum = 0;
	int i;
 
	/* 循环求参数之和 */
	for (i = 1; i <= n; i++)
	{
		/* 求和 */
		sum += lua_tonumber(L, i);
	}
	/* 压入平均值 */
	lua_pushnumber(L, sum / n);
	/* 压入和 */
	lua_pushnumber(L, sum);
	/* 返回返回值的个数 */
	return 2;
}

将包装好的函数注册到Lua环境中

/* 注册函数 */
lua_register(L, "average", average);

在lua中正常调用

avg, sum = average(10, 20, 30, 40, 50)

4.把C++的函数封装成模块
把C++的函数封装成模块:

①将C++的函数包装成Lua环境认可的Lua_CFunction格式,调用luaL_newlib,放入到一个lua表中压入栈里。

②将自定义模块,注册到Lua环境中。

③在lua中,加上自定义模块名调用C++函数。

avg.lua,这里的lua文件,在调用C++的函数时,需要加上自定义的模块名。 比如,我们定义模块名为mylib。

avg, sum = mylib.average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)

Lua1.cpp

#include <stdio.h>
 
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
 
static int average(lua_State *L)
{
	/* 得到参数个数 */
	int n = lua_gettop(L);
	double sum = 0;
	int i;
 
	/* 循环求参数之和 */
	for (i = 1; i <= n; i++)
	{
		/* 求和 */
		sum += lua_tonumber(L, i);
	}
	/* 压入平均值 */
	lua_pushnumber(L, sum / n);
	/* 压入和 */
	lua_pushnumber(L, sum);
	/* 返回返回值的个数 */
	return 2;
}
 
// 1. 列出需要封装的C++函数
// luaL_Reg为注册函数的数组类型
static const luaL_Reg mylibs_funcs[] = {
	 { "average", average },
	 { NULL, NULL }
};
 
// 2. 将所有函数放到一个table中,并压入栈中
int lua_openmylib(lua_State* L) {
	//创建一个新的表,将所有函数放到一个table中
	//将这个table压到stack里
	luaL_newlib(L, mylibs_funcs);
	return 1;
}
 
// 3. 将自定义模块加到注册列表里
static const luaL_Reg lua_reg_libs[] = {
	 { "base", luaopen_base },
	 { "mylib", lua_openmylib }, //这里为自定义的模块名字mylib
	 { NULL, NULL }
};
 
int main(int argc, char *argv[])
{
	/* 初始化Lua */
	/* 指向Lua解释器的指针 */
	lua_State* L = luaL_newstate();
 
	/* 载入Lua基本库 */
	luaL_openlibs(L);
 
	//4. 注册让lua使用的模块
	const luaL_Reg* lua_reg = lua_reg_libs;
	for (; lua_reg->func; ++lua_reg) {
		// 加载模块
		// 首先查找 package.loaded 表, 检测 modname 是否被加载过。 
		// 如果被加载过,require 返回 package.loaded[modname] 中保存的值。
		// 如果 modname 不在 package.loaded 中, 则调用函数 openf ,并传入字符串 modname。
		// 将其返回值置入 package.loaded[modname]。
		// 如果最后一个参数为真, 同时也将模块设到全局变量 modname 里。在栈上留下该模块的副本。
		luaL_requiref(L, lua_reg->name, lua_reg->func, 1);
		// 从栈中弹出 1 个元素
		lua_pop(L, 1);
	}
 
	/* 运行脚本 */
	luaL_dofile(L, "avg.lua");
	/* 清除Lua */
	lua_close(L);
 
	/* 暂停 */
	printf("Press enter to exit…");
	getchar();
	return 0;
}

5. Lua调用C++类注册生成的Lua模块
由于篇幅的限制,请移步:https://blog.csdn.net/qq826364410/article/details/88652441

6. 在Lua中以面向对象的方式使用C++注册的类
由于篇幅的限制,请移步:https://blog.csdn.net/qq826364410/article/details/88639408

Lua和C++交互:全局数组交互
https://blog.csdn.net/qq826364410/article/details/88713839

补充
这里补充其他一些栈操作:

int   lua_gettop (lua_State *L);	        //返回栈顶索引(即栈长度)
// lua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数量。
// 如果值比原栈顶高,则高的部分nil补足,如果值比原栈低,则原栈高出的部分舍弃。
// 所以可以用lua_settop(0)来清空栈。
void  lua_settop (lua_State *L, int idx);	
void  lua_pushvalue (lua_State *L, int idx);    //将idx索引上的值的副本压入栈顶
void  lua_remove (lua_State *L, int idx);	//移除idx索引上的值
void  lua_insert (lua_State *L, int idx);	//弹出栈顶元素,并插入索引idx位置
void  lua_replace (lua_State *L, int idx);	//弹出栈顶元素,并替换索引idx位置的值
// 确保堆栈上至少有 n 个额外空位。 如果不能把堆栈扩展到相应的尺寸,
// 函数返回假。 失败的原因包括将把栈扩展到比固定最大尺寸还大 (至少是几
// 千个元素)或分配内存失败。 这个函数永远不会缩小堆栈; 如果堆栈已经
// 比需要的大了,那么就保持原样
int   lua_checkstack (lua_State *L, int n); 

下面就分两个主要部分进行介绍(C++和栈操作;以及Lua和栈操作)

2.C++和栈之间操作相关函数

①c -> stack 将C++数据压到栈里函数:lua_pushxxx

LUA_API void        (lua_pushnil) (lua_State *L);
LUA_API void        (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void        (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
                                                      va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void  (lua_pushboolean) (lua_State *L, int b);
LUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int   (lua_pushthread) (lua_State *L);

②stack -> c 判断栈里类型相关函数: lua_isxxx(lua_State *L, int idx)

LUA_API int             (lua_isnumber) (lua_State *L, int idx);
LUA_API int             (lua_isstring) (lua_State *L, int idx);
LUA_API int             (lua_iscfunction) (lua_State *L, int idx);
LUA_API int             (lua_isinteger) (lua_State *L, int idx);
LUA_API int             (lua_isuserdata) (lua_State *L, int idx);
LUA_API int             (lua_type) (lua_State *L, int idx);

③stack -> c 获取栈里数据相关函数:lua_toxxx (lua_State *L, int idx)

LUA_API lua_Number      (lua_tonumberx) (lua_State *L, int idx, int *isnum);
LUA_API lua_Integer     (lua_tointegerx) (lua_State *L, int idx, int *isnum);
LUA_API int             (lua_toboolean) (lua_State *L, int idx);
LUA_API const char     *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t          (lua_rawlen) (lua_State *L, int idx);
LUA_API lua_CFunction   (lua_tocfunction) (lua_State *L, int idx);
LUA_API void           *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State      *(lua_tothread) (lua_State *L, int idx);
LUA_API const void     *(lua_topointer) (lua_State *L, int idx);

3.Lua和栈之间的操作相关函数

①从Lua中得到数据放到栈里进行操作:lua_getxxx

LUA_API int (lua_getglobal) (lua_State *L, const char *name);
LUA_API int (lua_gettable) (lua_State *L, int idx);
LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawget) (lua_State *L, int idx);
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);
 
LUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int   (lua_getmetatable) (lua_State *L, int objindex);
LUA_API int  (lua_getuservalue) (lua_State *L, int idx);

②从栈里将数据写入到Lua中:lua_setxxx

LUA_API void  (lua_setglobal) (lua_State *L, const char *name);
LUA_API void  (lua_settable) (lua_State *L, int idx);
LUA_API void  (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void  (lua_seti) (lua_State *L, int idx, lua_Integer n);
LUA_API void  (lua_rawset) (lua_State *L, int idx);
LUA_API void  (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
LUA_API void  (lua_rawsetp) (lua_State *L, int idx, const void *p);
LUA_API int   (lua_setmetatable) (lua_State *L, int objindex);
LUA_API void  (lua_setuservalue) (lua_State *L, int idx);

最后总结一下

  1. Lua和C++是通过一个虚拟栈来交互通信的。

  2. C++调用Lua: 由C++先把函数名、变量名、table中键放入栈中,然后把函数名、变量名、table中键出栈,并返回对应的值到栈顶,再由栈顶返回C++。

  3. Lua调C++:

**Lua调用C++的函数:**先把普通的C++函数包装成Lua_CFunction格式,然后注册函数到Lua解释器中,然后由Lua去调用这个模块的函数。

**Lua以面向对象的方式调用C++的类:**新建一个元表metatable,并设置元表里key为"__index"的值的为metatable本身,然后将成员操作方法添加到元表metatable里,在创建对象函数中,把元表赋值给对象指针,这样通过":"操作符就可以找到对应的方法了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/820937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无涯教程-Lua - 条件判断

if结构要求程序员确定一个或多个要由程序判断或测试的条件&#xff0c;以及要确定的条件为真的情况下要执行的一条或多条语句&#xff0c;如果条件为真&#xff0c;则执行指定语句&#xff0c;如果条件为假&#xff0c;则执行其他语句。 Lua编程语言假定布尔值 true 和 non-nil…

PHP高级检索功能的实现以及动态拼接sql

我们学习了解了这么多关于PHP的知识&#xff0c;不知道你们对PHP高级检索功能的实现以及动态拼接sql是否已经完全掌握了呢&#xff0c;如果没有&#xff0c;那就跟随本篇文章一起继续学习吧! PHP高级检索功能的实现以及动态拼接sql。完成的功能有&#xff1a;可以单独根据一个…

Python常用命令

1.python常用函数 type() 函数&#xff1a;查询对象的类型 input() 函数在&#xff1a;函数接受一个标准输入数据&#xff0c;返回为 string 类型 range() 函数&#xff1a;用于构造一个从[start, stop) &#xff08;不包含stop&#xff09;之间的连续的不可变的整数序列对象…

SpringBoot整合knife4j(快速入门超详细版)

前言 查看此文章前强烈建议先看这篇文章&#xff1a;Java江湖路 | 专栏目录 该文章纪录的是SpringBoot快速集成Knife4j&#xff0c;每一步都有记录&#xff0c;争取每一位看该文章的小伙伴都能操作成功。达到自己想要的效果~ 文章目录 前言1、什么是Knife4j2、SpringBoor整合K…

实现邮箱管理之gmail邮箱、office365(Azure)邮箱之披荆斩棘问题一览

要进行Office365邮箱的授权对接&#xff0c;你需要先申请一个应用&#xff0c;并获取授权访问令牌。 以下是一个简单的步骤&#xff1a; 登录 Azure 门户&#xff1a;https://portal.azure.com/创建一个新的应用程序&#xff0c;或者使用现有的应用程序。要创建新的应用程序&…

在人间烟火里,卡萨帝开启品牌新征程

大暑刚过天正热&#xff0c;尤其是在今年厄尔尼诺现象席卷太平洋的背景下&#xff0c;人们对空调的需求持续“升温”。CCTV2财经频道《正点财经》在专题报道中提到&#xff0c;6月国内空调产销两旺&#xff0c;同比增长均在35%以上。 炙热的天气下&#xff0c;南方居民有更加难…

PySpark 数据操作

数据输入 RDD对象 如图可见&#xff0c;PySpark支持多种数据的输入&#xff0c;在输入完成后&#xff0c;都会得到一个&#xff1a;RDD类的对象 RDD全称为&#xff1a;弹性分布式数据集&#xff08;Resilient Distributed Datasets&#xff09; PySpark针对数据的处理&…

【SAP MII学习】Day01--Overview, Security Services, and Workbench

1. Module 1: Overview 1.1 问题存在的原因 上图展示的是在工厂中IT的架构图&#xff0c;主要分为一下的三个层次&#xff1a; Shop Floor Automation and Control Systems (SFAC):collect data from the PLCs and sensors that are connected to the machinery on the facto…

vue2 ant datepicker 日期选择框 点击此刻时间多8小时不正确解决方案

点击“此刻”后&#xff0c;可以看到在DatePicker上时间是正确的 但是打印出来的dateString时间却多了8小时 原因&#xff1a; 因为点击 此刻 那个按钮时&#xff0c;moment 对象会经过 getTodayTime 设置了 utcOffset utcOffset 会把 moment 对象的 _isUTC 设为 true 并设置 _…

北漂Java程序员入职五个月的收获总结

&#x1f468;‍&#x1f4bb;博主主页&#xff1a;小尘要自信 &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;Java程序员的成长 &#x1f468;‍&#x1f4bb;上一篇文章&#xff1a;告别过去&#xff0c;拥抱未来&#xff1a;一个Java开发者的成长之路 &#x1f468;‍&a…

火热报名中 | 赛宁独家技术支持第七届“蓝帽杯”网络安全技能大赛

由公安部网络安全保卫局、教育部教育管理信息中心、中国教育协会指导&#xff0c;中国人民公安大学主办&#xff0c;奇安信科技集团股份有限公司协办&#xff0c;南京赛宁信息技术有限公司提供技术支持的2023第七届“蓝帽杯”全国大学生网络安全技能大赛于近日正式开启报名。 …

桥接模式(Brige)

桥接模式是一种结构型设计模式&#xff0c; 又称为柄体(Handle and Body)模式或接口(Interface)模式。桥接模式&#xff0c;可将将抽象部分与它的实现部分分离&#xff0c;使它们都可以独立地变化。如将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构&#x…

蓝桥云课ROS机器人旧版实验报告-02架构及概念

项目名称 实验二 ROS[Kinetic/Melodic/Noetic]架构及概念 成绩 内容&#xff1a;机器人操作系统文件系统级、计算图级、社区级、创建功能包并实现功能等 实验记录&#xff08;70分&#xff09; 安装一个可用的 build-from-source 包&#xff1a; 在克隆之前&#xff…

Jmeter函数助手(一)随机字符串(RandomString)

一、目标 实现一个请求单次调用&#xff0c;请求体里多个集合中的相同参数&#xff08;zxqs&#xff09;值随机从序列{01、02、03、03、04、05、06、07、08}中取 若使用CSV数据文件、用户参数等参数化手段&#xff0c;单次执行请求&#xff0c;请求体里多个集合中的相同参数&a…

生产服务器突然本机无法访问本机IP的端口

生产服务器突然本机无法访问本机IP的端口 一、现象描述 生产服务器突然无法访问自己本机IP地址的端口&#xff0c;通过localhost或者127.0.0.1都可以正常访问 二、问题分析 服务器是搭建在虚拟机上面&#xff0c;起初由于服务器内存不足的原因&#xff0c;导致了服务器故障无…

基于ssm 网上购物系统-计算机毕设 附源码12503

基于SSM网上购物系统 摘 要 近年来&#xff0c;随着移动互联网的快速发展&#xff0c;电子商务越来越受到网民们的欢迎&#xff0c;电子商务对国家经济的发展也起着越来越重要的作用。简单的流程、便捷可靠的支付方式、快捷畅通的物流快递、安全的信息保护都使得电子商务越来越…

Nginx配置与使用

引言 早期的业务都是基于单体节点部署&#xff0c;由于前期访问流量不大&#xff0c;因此单体结构也可满足需求&#xff0c;但随着业务增长&#xff0c;流量也越来越大&#xff0c;那么最终单台服务器受到的访问压力也会逐步增高。时间一长&#xff0c;单台服务器性能无法跟上业…

Spring学习记录----十五、面向切面编程AOP+十六、Spring对事务的支持

十五、面向切面编程AOP IoC使软件组件松耦合。AOP让你能够捕捉系统中经常使用的功能&#xff0c;把它转化成组件。 AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;面向切面编程&#xff0c;面向方面编程。&#xff08;AOP是一种编程技术&#xff09; …

什么是EhCache 缓存

EhCache 缓存-提高检索效率的利器 缓存-官方文档 文档地址: https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache EhCache 缓存 配置文档: https://www.cnblogs.com/zqyanywn/p/10861103.html 基本介绍 EhCache 是一个纯Java 的缓存框架&#xff0c;具有快速、精干等…

GORM 并发执行 Save 更新记录报:Error 1062 (23000)

文章目录 1.Save 简介2.问题3.原因4.小结参考文献 1.Save 简介 先看一下 Save 方法的描述&#xff1a; // Save updates value in database. If value doesnt contain a matching primary key, value is inserted. func (db *DB) Save(value interface{}) (tx *DB)Save 有两个…