【Lua基础系列】之C/C++与lua的交互方式
大家好,我是Lampard~~
欢迎来到Lua基础系列的博客
PS:本博客知识参考资料为:《Lua程序设计第四版》,该书由Lua的创始人2018年所编著,所以大家可以放心去吸收知识
今天在正文之前先说一些心里话:
今天是我正式工作的第一天,感觉到自己真真正正要从学生转化成社会里的大人了。虽有准备,但心里总是戚戚。
公司是个好公司,第一天上班能感受到周围人的善意,对那里的伙食我已经很明显感觉到我将要长胖了。项目组里正在赶一个上线的项目,只有我一个人在碎碎的看着文档,这种落差无力感让人心里不舒服。不管怎么说,赶快成长起来吧。
前文再续,书接上一回。
今天讲的是如何在lua中如何实现与C/C++的交互:
谜底直接告诉大家,它们是通过一个虚拟,强大的栈来进行交互。这个栈是由lua实现的。C方面只是通过取栈得到想要的数
据,然后再通过往这个栈中压入元素,从而实现向Lua那边更新数据。这个强大的栈长这样:
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存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收.
好,当我们有了这个强大的栈之后,究竟要如何使用他呢?
假设我们有一个lua的文件如下:
str = "I am so cool"
tbl = {name = "shun", id = 20114442}
function add(a,b)
return a + b
end
现在向大家展示怎么去调用它:
#include <iostream>
#include <string.h>
using namespace std;
extern "C"
{
#include "lua.h"
#include "lauxlib.h" // externC语言与lua交互的方式
#include "lualib.h"
}
void main()
{
//1.创建Lua状态
lua_State *L = luaL_newstate(); // L是一个强大的struct,里面封装了lua的八种数据类型
if (L == NULL)
{
return ;
}
//2.加载Lua文件
int bRet = luaL_loadfile(L,"hello.lua"); // L加载了“hello。lua”这个文件
if(bRet)
{
cout<<"load file error"<<endl;
return ;
}
//3.运行Lua文件
bRet = lua_pcall(L,0,0,0); /*第一次调用是相当于 把一整个lua文件的内容 当成一个function来执行
你可以试一下 lua 设置个全局变量a为100 先把整个文件load一次
读下看看 a为多少 再pcall一次 看看a为多少就知道了
第一次调用a 得到的是nil 但是第2次调用就可以获得100了*/
if(bRet)
{
cout<<"pcall error"<<endl;
return ;
}
//4.读取变量
lua_getglobal(L,"str"); // 把str置于栈顶
string str = lua_tostring(L,-1); // 把L转化tostring为C++类的str
cout<<"str = "<<str.c_str()<<endl; //str = I am so cool~
//5.读取table
lua_getglobal(L,"tbl"); // 把tbl置在栈顶
lua_getfield(L,-1,"name"); // 访问L中第一个元素(表)的name属性置于栈顶
str = lua_tostring(L,-1);
cout<<"tbl:name = "<<str.c_str()<<endl; //tbl:name = shun
//6.读取函数
lua_getglobal(L, "add"); // 函数压入栈顶
lua_pushnumber(L, 10); // 压入第一个参数
lua_pushnumber(L, 20); // 压入第二个参数
int iRet= lua_pcall(L, 2, 1, 0);// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。
if (iRet) // 调用出错
{
const char *pErrorMsg = lua_tostring(L, -1);
cout << pErrorMsg << endl;
lua_close(L);
return ;
}
if (lua_isnumber(L, -1)) //取值输出
{
double fValue = lua_tonumber(L, -1);
cout << "Result is " << fValue << endl;
}
//至此,栈中的情况是:
//=================== 栈顶 ===================
// 索引 类型 值
// 4 int: 30
// 3 string: shun
// 2 table: tbl
// 1 string: I am so cool~
//=================== 栈底 ===================
//7.关闭state
lua_close(L);
return ;
}