Lua嵌入C++

目录

 

一:步骤

1.创建lua静态库工程

2.在C++工程中执行lua语句和lua文件

二.调用过程

1.建立lua堆栈

2.堆栈元素TValue类型

3.通信api


一:步骤

1.创建lua静态库工程

选择c++ 控制台程序==》取名 lua53==》选择应用程序里类型:静态链接库 ,取消附加选项

导入已经下载好的头文件和c文件,lua的 源代码

扫描二维码关注公众号,回复: 4775736 查看本文章

点击编译,生成lua53.lib 静态库文件

2.在C++工程中执行lua语句和lua文件

选择c++ 控制台程序==》取名 LuaTest==》选择应用程序里类型:控制台程序 ,取消附加选项。

在工程中使用上述的lib库文件:

使用静态库:需要lib文件,h文件

(1)lib文件所在路径:设置项目属性--vc++目录--库目录为lib所在的路径(要么直接将需要的库文件放到在此工程的目录下)

(2)h文件所在路径:C/C++  -->常规 -->附加包含目录:lua源码目录(要么直接需要的头文件 放到在此工程的目录下,如下面要用的lua.h lualib.h......)

(3)告诉linker 静态加载这个库文件:将lib添加到项目属性--链接器--输入--附加依赖项(要么直接在源代码中加入#pragma comment(lib, “**.lib”))

LuaTest.cpp 内容如下:

#include "stdafx.h"
#include <iostream>
extern "C"
{
#include	"lua.h"
#include	"lualib.h"
#include	"lauxlib.h"
}


int main()
{
	//新建虚拟机
	lua_State *L = luaL_newstate();
	//载入库
	luaL_openlibs(L);


	//1.直接执行lua语句: 可以看到控制台输出 "Hello World"
	const char *buf = "print('Hello World')";
	luaL_dostring(L, buf);


	//2.载入Lua文件执行: 输出 hello world ! 和 文件加载完毕 !
	luaL_dofile(L, "test.lua");

	//3.获取全局变量,进行输出: 我是 test 变量!
	lua_getglobal(L, "test");
	printf("\n%s\n", lua_tostring(L, -1));


	//关闭虚拟机
	lua_close(L);
	system("pause");

    return 0;
}

test.lua内容如下, 放到工程目录下 (编码格式只能是不带bom的utf8 或者 ansi)

print "hello world !"
print "文件加载完毕 !"
 
test = "我是 test 变量!"

因为是静态库文件,lib中有用的代码已经嵌入到exe了,所以把 test.lua 和 LuaTest.exe 复制到任何地方都可以运行。

二.调用过程

1.建立lua堆栈

LuaTest.cpp中的第一句 lua_State *L = luaL_newstate(); 会调用下面stack_init这个函数用来建立通信堆栈。在这个函数中,相当于创建了一个 TValue stack[max_stack_len] 。

static void stack_init (lua_State *L1, lua_State *L) {
  int i; CallInfo *ci;
  /* initialize stack array */   
  // 类似于 TValue stack[max_stack_len] 
  L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, TValue);
  L1->stacksize = BASIC_STACK_SIZE;
  for (i = 0; i < BASIC_STACK_SIZE; i++)
    setnilvalue(L1->stack + i);  /* erase new stack */
  L1->top = L1->stack;
  // EXTRA_STACK TM calls是什么
  L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK;
  /* initialize first ci */
  // 初始化 base_ci 以及 ci
  ci = &L1->base_ci;
  ci->next = ci->previous = NULL;
  ci->callstatus = 0;
  ci->func = L1->top;
  setnilvalue(L1->top++);  /* 'function' entry for this 'ci' */
  ci->top = L1->top + LUA_MINSTACK;
  L1->ci = ci;
}
// 一些相关的数据成员
struct lua_State {
  ...
  StkId top;  /* first free slot in the stack */
  StkId stack_last;  /* last free slot in the stack */
  StkId stack;  /* stack base */
  int stacksize;
  CallInfo base_ci;  /* CallInfo for first level (C calling Lua) */
  CallInfo *ci;  /* call info for current function */
  ...
}

2.堆栈元素TValue类型

堆栈中每个元素的类型是 TValue,TValue是个{值, 类型} 数据结构,其中值Value是个union类型,具体源码如下

typedef union Value {
  GCObject *gc;    /* collectable objects */ // table, thread, closure, string需要内存管理垃圾回收的类型
  void *p;         /* light userdata */      // #define LUA_TLIGHTUSERDATA	2
  int b;           /* booleans */            // #define LUA_TBOOLEAN		1
  lua_CFunction f; /* light C functions */   // #define LUA_TFUNCTION		6    LUA_TLCF
  lua_Integer i;   /* integer numbers */     // #define LUA_TNUMBER		3    LUA_TNUMINT	
  lua_Number n;    /* float numbers */       // #define LUA_TNUMBER		3    LUA_TNUMFLT	
} Value;


#define TValuefields	Value value_; int tt_


typedef struct lua_TValue {
  TValuefields;
} TValue;

// ts u cl h p th类型的最开始的元素 就是 GCObject类的成员
union GCUnion {
  GCObject gc;  /* common header */
  struct TString ts;
  struct Udata u;
  union Closure cl;
  struct Table h;
  struct Proto p;
  struct lua_State th;  /* thread */
};

所以:

        1. lua中 ,light userdata,boolean, number,nil四种类型的值是直接存在栈上元素里的, 和垃圾回收无关.

        2. lua中,string, userdata,closure, table, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收.

C和lua中的类型对应

                       c          lua
         nil           无    {value=0, tt = t_nil}
      boolean       int  非0, 0    {value=非0/0, tt = t_boolean}
      number       int/float等    {value=1.5, tt = t_number}
   lightuserdata    void*, int*, 各种指针    {value=point, tt = t_lightuserdata}
      string          char  str[]    {value=gco, tt = t_string}   gco=TString obj
      table            无    {value=gco, tt = t_table}  gco=Table obj
      userdata            无    {value=gco, tt = t_udata} gco=Udata obj
      closure            无    {value=gco, tt = t_function} gco=Closure obj

ps:Lua定义

LUA_TFUNCTION,LUA_TSTRING,LUA_TNUMBER进行细分

#define LUA_TNIL		0
#define LUA_TBOOLEAN		1
#define LUA_TLIGHTUSERDATA	2
#define LUA_TNUMBER		3
#define LUA_TSTRING		4
#define LUA_TTABLE		5
#define LUA_TFUNCTION		6
#define LUA_TUSERDATA		7
#define LUA_TTHREAD		8

/*
** tags for Tagged Values have the following use of bits:
** bits 0-3: actual tag (a LUA_T* value)
** bits 4-5: variant bits
** bit 6: whether value is collectable
*/


/*
** LUA_TFUNCTION variants:
** 0 - Lua function
** 1 - light C function
** 2 - regular C function (closure)
*/

/* Variant tags for functions */
#define LUA_TLCL	(LUA_TFUNCTION | (0 << 4))  /* Lua closure */
#define LUA_TLCF	(LUA_TFUNCTION | (1 << 4))  /* light C function */
#define LUA_TCCL	(LUA_TFUNCTION | (2 << 4))  /* C closure */


/* Variant tags for strings */
#define LUA_TSHRSTR	(LUA_TSTRING | (0 << 4))  /* short strings */
#define LUA_TLNGSTR	(LUA_TSTRING | (1 << 4))  /* long strings */


/* Variant tags for numbers */
#define LUA_TNUMFLT	(LUA_TNUMBER | (0 << 4))  /* float numbers */
#define LUA_TNUMINT	(LUA_TNUMBER | (1 << 4))  /* integer numbers */


/* Bit mark for collectable types */
#define BIT_ISCOLLECTABLE	(1 << 6)

/* mark a tag as collectable */
#define ctb(t)			((t) | BIT_ISCOLLECTABLE)

3.通信api

堆栈索引的方式可是是正数也可以是负数,正数索引1永远表示栈底,负数索引-1永远表示栈顶。

// push 都是对栈顶进行操作
// lua_push*族函数都有"创建一个类型的值并压入
// c value –> lua value 做的事情:1. 栈顶新分配元素    2. 绑定或赋值
// 一个c value入栈就是进入了lua的世界, lua会生成一个对应的结构并管理起来, 从此就不再依赖这个c value
const char *lua_pushstring (lua_State *L, const char *s)
void lua_pushnumber (lua_State *L, lua_Number n)    // tt_类型 LUA_TNUMFLT
void lua_pushinteger (lua_State *L, lua_Integer n)  // tt_类型 LUA_TNUMINT
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n)


// lua value –> c value时, 是通过 lua_to* 族api实现


// idx堆栈索引的方式可是是正数也可以是负数,
// 正数索引1永远表示栈底,负数索引-1永远表示栈顶。
int lua_isstring (lua_State *L, int idx)
int lua_isnumber (lua_State *L, int idx)


int   lua_gettop (lua_State *L);            // 返回栈顶索引(即栈长度)  
void  lua_settop (lua_State *L, int idx);   // lua_settop(0) 清空栈   

// lua_getglobal(L,"var")做的两件事:
// 1.将var放入栈中,
// 2.由Lua去寻找变量var的值,并将变量var的值返回栈顶(替换var)。
int lua_getglobal (lua_State *L, const char *name)

// lua_getfield(L,-1,"name")做的两件事:到当前栈顶去找变量name的值
// lua_pushstring(L,"name") + lua_gettable(L,-2)
int lua_getfield (lua_State *L, int idx, const char *k)            

lua和c通信时有这样的约定: 所有的lua中的值由lua来管理, c++中产生的值lua不知道

类似表达了这样一种意思: "如果你(c/c++)想要什么, 你告诉我(lua), 我来产生, 然后放到栈上, 你只能通过api来操作这个值, 我只管我的世界", 这个很重要, 因为:

         "如果你想要什么, 你告诉我, 我来产生"就可以保证, 凡是lua中的变量, lua要负责这些变量的生命周期和垃圾回收, 所以, 必须由lua来创建这些值(在创建时就加入了生命周期管理要用到的簿记信息)

         "然后放到栈上, 你只能通过api来操作这个值", lua api给c提供了一套完备的操作界面, 这个就相当于约定的通信协议, 如果lua客户使用这个操作界面, 那么lua本身不会出现任何"意料之外"的错误.

         "我只管我的世界"这句话体现了lua和c/c++作为两个不同系统的分界, c/c++中的值, lua是不知道的, lua只负责它的世界

参考

https://www.cnblogs.com/sevenyuan/p/4511808.html

猜你喜欢

转载自blog.csdn.net/u012138730/article/details/84309134