目录
一:步骤
1.创建lua静态库工程
选择c++ 控制台程序==》取名 lua53==》选择应用程序里类型:静态链接库 ,取消附加选项
导入已经下载好的头文件和c文件,lua的 源代码
点击编译,生成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只负责它的世界