C/C++与lua之间能过实现交互,它的原理是:
1.lua里面的API都是用C写的。
2.lua跟C/C++的交互是通过一个虚拟stack来进行数据的沟通的。在VS工程中,我们需要加入C API的头文件lua.h, luaconfig.h , lualib.h, luaxlib.h 。这几个头文件都是lua源代码,可以直接下载使用。lua.h提供原子级别的API,对栈的基本操作都在里面实现,里面的API都是lua_开头。luaxlib.h 定义了辅助库提供的函数,它的所有定义都以luaL_开头,辅助库是一个使用lua.h中的API编写出的一个较高的抽象层。下面看下完整源代码:
#include "stdafx.h"
#include "stdio.h"
extern "C"
{
#include "lua.h" //提供原子级别的API
#include "lualib.h"
#include "lauxlib.h" //定义了辅助哭提供的函数
}; //lua头文件中的API都是用C写的
#pragma comment(lib,"lua5.1.lib") //让程序知道调用的lua里面的API可以直接在这个库里面找
//这个函数,我们实现将压进stack里面的所有数据,从base到top,根据不同类型打印出来
void stackDump(lua_State *L)
{
int i;
int top = lua_gettop(L); //获取stack大小
for ( i = 1;i <= top;i++ )
{
int type = lua_type(L, i);
switch(type)
{
case LUA_TSTRING:
{
printf("%s",lua_tostring(L, i)); //lua库中的函数,添加了头文件,可以直接使用
break;
}
case LUA_TBOOLEAN:
{
printf(lua_toboolean(L, i)?"true":"false");
break;
}
case LUA_TNUMBER:
{
printf("%g",lua_tonumber(L, i));
break;
}
default:
{
printf("%s",lua_typename(L ,i));
break;
}
}
printf(" ");
}
printf("\n");
}
int _tmain(int argc, _TCHAR* argv[])
{
lua_State *L; //创建一个lua_State实例
L = luaL_newstate(); //必须要创建lua_State环境,stack存在于这个环境中
luaL_openlibs(L); //加载lua静态链接库lua5.1.lib
lua_pushboolean(L,true);
lua_pushinteger(L,2);
lua_pushnumber(L,2.333);
lua_pushstring(L,"then"); //分别压入了四个不同类型的元素,现在stack里面,从base到top依次为 true , 2 , 2.333 ,then
stackDump(L); //打印结果 从base到top为 true , 2 , 2.333 ,then
printf("stay here");
return 0;
}
C API. Lua既是一种扩展语言,也是一种可扩展语言;说它是扩展语言,意思是C/C++可以用lua进行扩展,这时C/C++拥有控制权,lua是一个库,这种形式的C/C++代码称为“应用程序代码”;说它是可扩展语言,意思是lua自身也可以通过在lua环境中注册用C语言(或其他语言)实现的函数,然后lua可以直接调用这些函数,这时lua拥有控制权,C是一个库,这种形式的C代码称为“库代码”。这就是C跟lua交互的两种形式。应用程序代码与库代码都使用相同的API来与lua通信,这些API就是C API
C API
Lua脚本实现交互提供了一系列的C API,常用API有:
luaL_newstate函数用于初始化一个lua_State实例
luaL_openlibs函数用于打开Lua中的所有标准库,如io库、string库等。
luaL_loadbuffer编译了buff中的Lua代码,如果没有错误,则返回0,同时将编译后的程序块压入虚拟栈中。
lua_pcall函数会将程序块从栈中弹出,并在保护模式下运行该程序块。执行成功返回0,否则将错误信息压入栈中。
lua_tostring函数中的-1,表示栈顶的索引值,栈底的索引值为1,以此类推。该函数将返回栈顶的错误信息,但是不会将其从栈中弹出。
lua_pop是一个宏,用于从虚拟栈中弹出指定数量的元素,这里的1表示仅弹出栈顶的元素。
lua_close用于释放状态指针所引用的资源
Lua针对每种C类型,都有一个C API函数与之对应,如:
void lua_pushnil(lua_State* L); --nil值
void lua_pushboolean(lua_State* L, int b); --布尔值
void lua_pushnumber(lua_State* L, lua_Number n); --浮点数
void lua_pushinteger(lua_State* L, lua_Integer n); --整型
void lua_pushlstring(lua_State* L, const char* s, size_t len); --指定长度的内存数据
void lua_pushstring(lua_State* L, const char* s); --以零结尾的字符串,其长度可由strlen得出
Lua提供了一组特定的函数用于检查返回元素的类型,如:
int lua_isboolean (lua_State *L, int index);
int lua_iscfunction (lua_State *L, int index);
int lua_isfunction (lua_State *L, int index);
int lua_isnil (lua_State *L, int index);
int lua_islightuserdata (lua_State *L, int index);
int lua_isnumber (lua_State *L, int index);
int lua_isstring (lua_State *L, int index);
int lua_istable (lua_State *L, int index);
int lua_isuserdata (lua_State *L, int index);
以上函数,成功返回1,否则返回0。需要特别指出的是,对于lua_isnumber而言,不会检查值是否为数字类型,而是检查值是否能转换为数字类型
C/C++与lua交互的总结:
1.引入lua的库函数
extern "C"
{
#include "lua.h" //提供原子级别的API
#include "lualib.h"
#include "lauxlib.h" //定义了辅助哭提供的函数
};//C++代码中,用extern "C" 区别c代码,因为lua头文件中的API都是用C写的
2.创建一个lua_State对象
lua_State *L; //创建一个lua_State实例,lua_State主要是管理一个lua虚拟机的执行环境, 一个lua虚拟机可以有多个执行环境。Lua虚拟机通过维护这样一个虚拟栈来实现两种之间的通信
//C/C++与lua的数据通信
//Lua虚拟机提供Lua_State这样一种数据结构。任何一种数据从C\C++传入Lua虚拟机中,Lua都会将这类数据转换为一种通用的结构lua_TValue,并且将数据复制一份,将其压入虚拟栈中
3.初始化实例
L = luaL_newstate(); //必须要创建lua_State环境,stack存在于这个环境中
4.打开Lua中的所有标准库
luaL_openlibs(L); //加载lua静态链接库lua5.1.lib
5.向stack中添加数据
lua_pushboolean(L,true);
lua_pushinteger(L,2);
lua_pushnumber(L,2.333);
lua_pushstring(L,"then"); //类似于栈,第一个数据放在栈底,API使用“索引”来引用栈中的元素,第一个压入栈的为1,第二个为2,依此类推。我们也可以使用负数作为索引值,其中-1表示为栈顶元素,-2为栈顶下面的元素,同样依此类推
6.将stack中的数据读出来
stackDump(L)