Lua与C++交互(一)————堆栈

Lua虚拟机

什么是Lua虚拟机

Lua本身是用C语言实现的,它是跨平台语言,得益于它本身的Lua虚拟机。

虚拟机相对于物理机,借助于操作系统对物理机器(CPU等硬件)的一种模拟、抽象,主要扮演CPU和内存的作用。

虚拟机的主要职责就是:执行字节码中的指令,管理全局状态(global_state)、数据栈(StackValue)和函数调用链状态(CallInfo)

可以理解成,lua虚拟机就是一个独立的空间,它会维护Lua的所有运行。

创建Lua虚拟机

使用C函数,luaL_newstate 来创建。

会创建一个lua_State的结构体,该结构体就代表了一个Lua虚拟机。

一个进程中可以创建多个Lua虚拟机,即多个lua_State结构。

Lua虚拟机中是单线程实现,所以,多个创建的Lua虚拟机之间是相互独立的。

cocos2dx中的创建Lua虚拟机的地方

AppDelegate中

bool AppDelegate::applicationDidFinishLaunching()
{
    ;;....
    // register lua module
    auto engine = LuaEngine::getInstance();
    ;;...
}

这里在调用单例LuaEngine的时候,会创建LuaStack

LuaEngine中

bool LuaEngine::init(void)
{
    _stack = LuaStack::create();
    _stack->retain();
    return true;
}

LuaStack中

bool LuaStack::init(void)
{
    _state = lua_open();
    luaL_openlibs(_state);
    toluafix_open(_state);

    // Register our version of the global "print" function
    const luaL_Reg global_functions [] = {
        {"print", lua_print},
        {"release_print",lua_release_print},
        {nullptr, nullptr}
    };
    ;;....
}

其中成员变量_state的类型就是lua_State结构体,也就是Lua虚拟机。lua_open是函数luaL_newstate的宏定义。

同时可以得知,这里Lua虚拟机只有一个,这也是为什么在cocos2dx中需要严格按照规则来进行C++和Lua调用的原因。

lua_State

lua虚拟机对象,本身是一个结构体。

它是一个lua线程的执行状态,所有的C api都是基于这个结构体的。

struct lua_State
{
	CommonHeader;//#define CommonHeader
	GCObject *next; 
	lu_byte tt; 
	lu_byte marked
	lu_byte status;//虚拟机的错误状态码
	StkId top;//栈顶元素所在位置的下一个位置,也就是栈上第一个空闲的位置
	StkId base;//栈上,当前函数的基址(注意不是函数所在位置)
	global_State* l_G;//全局表,环境章节再说
	CallInfo* ci;//当前函数调用信息
	const Instruction* savedpc;//当前函数的指令位置,指向待取指指令的地址
	StkId stack_last;//栈最后一个位置的下一个位置
	StkId stack;//寄存器数组的起始位置
	CallInfo* end_ci;//函数调用信息数组的最后一个位置的下一个位置
	CallInfo* base_ci;//函数调用信息数组首地址
	int stacksize;//栈的大小
	int size_ci;//函数调用信息数组大小
	unsigned short nCcalls;//内嵌C调用层数
	unsigned short baseCcalls;//唤醒协程时的内嵌C调用层数
	lu_byte hookmask;
	lu_byte allowhook;
	int basehookcount;
	int hookcount;
	lua_Hook hook;
	TValue l_gt;//Global表
	TValue env;//环境表的临时位置
	GCObject* openupval;//open状态的upvalues
	GCObject* gclist;
	struct lua_longjmp* errorJmp;//跳转信息单链表,实现try catch的功能,见函数章节
	ptrdiff_t errfunc;//当前错误处理函数在栈上的索引
};

关于lua_State这里不过多阐述,感兴趣的可以看《lua设计与实现》这本书。

或者看看云风的《lua源码赏析》

或者看看这个博客
https://blog.csdn.net/yuanlin2008/category_1307277.html

Lua全局表

global_State 全局状态机

如果说lua_State是面对外面表现的虚拟机对象,那么global_State才是背后真正的大佬,该结构体不对外开放,即无法用Lua公开的API获取到它的指针、句柄或引用。

它里面有对主线程(lua_State实例)的引用、有全局字符串表、有内存管理函数、有GC需要的相关信息以及一切Lua在工作时需要的工作内存等等

想要深入了解,同样可以查看《lua设计与实现》这本书。

Lua堆栈

Lua的堆栈是Lua和C++交互的基础,也就是说C++和Lua之间的数据类型交互都是通过这个虚拟栈进行完成的。

不管是C++需要获取Lua的数据还是需要传递数据给Lua,都需要先将这个数据压入栈中。

在这里插入图片描述

从索引来说,分为正向和逆向索引。其中-1永远表示栈顶,1永远表示栈底。

入栈操作

涉及到一些Lua入栈的函数(常用)。

函数原型说明
lua_pushnumbervoid lua_pushnumber (lua_State *L, lua_Number n)将数值类型n 压入栈中
lua_pushnilvoid lua_pushnil (lua_State *L)将nil压入栈中
lua_pushbooleanvoid lua_pushboolean (lua_State *L, int b);将布尔类型压入栈中
lua_pushfstringconst char *lua_pushfstring (lua_State *L, const char *fmt, …)把一个格式化过的字符串压入堆栈,然后返回这个字符串的指针
lua_pushintegervoid lua_pushinteger (lua_State *L, lua_Integer n)将一个整型数字n压入栈中
lua_pushlstringvoid lua_pushlstring (lua_State *L, const char *s, size_t len)把指针 s 指向的长度为 len 的字符串压栈
lua_pushstringvoid lua_pushstring (lua_State *L, const char *s)把指针 s 指向的以零结尾的字符串压栈
lua_pushthreadint lua_pushthread (lua_State *L)把 L 中提供的线程压栈。 如果这个线程是当前状态机的主线程的话返回 1
lua_pushvaluevoid lua_pushvalue (lua_State *L, int index)把堆栈上给定有效处索引处的元素作一个拷贝压栈
lua_pushlightuserdatavoid lua_pushlightuserdata (lua_State *L, void *p)把一个 light userdata 压栈。 userdata 在 Lua 中表示一个 C 值。light userdata 表示一个指针。它是一个像数字一样的值:你不需要专门创建它,它也没有独立的 metatable ,而且也不会被收集(因为从来不需要创建)。只要表示的 C 地址相同,两个 light userdata 就相等
lua_pushcfunctionvoid lua_pushcfunction (lua_State *L, lua_CFunction f)将一个 C 函数压入堆栈。 这个函数接收一个 C 函数指针,并将一个类型为 function 的 Lua 值 压入堆栈。当这个栈顶的值被调用时,将触发对应的 C 函数

具体可以查看

https://www.w3cschool.cn/doc_lua_5_1/lua_5_1-index.html#lua_pushnumber

取值和出栈

与之对应的取值为:

lua_to*(lua_State * L,栈中位置)取值

对应的出栈为:

lua_pop(lua_State * L,出栈个数)出栈

举例

myName = “beauty girl”

在这里插入图片描述

  1. C++想要获取myName的值,根据规则,它需要把myName压入栈中,这样lua就能看到;
  2. lua从堆栈中获取myName的值,此时栈顶为空;
  3. lua拿着myName去全局表中查找与之对应的字符串;
  4. 全局表找到,并返回"beauty girl";
  5. lua把"beauty girl"压入栈中;
  6. C++从栈中获取"beauty girl"
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐