Lua是一种嵌入式语言,可以很好的嵌入其他应用程序。lua为我们提供了一组灵活的C API,使C代码能够很好的与Lua进行交互。包括读写Lua全局变量,调用Lua函数,运行Lua代码,注册C函数反过来供Lua调用。简单的说,C能调用Lua,反过来Lua也能调用C。真的是灰常强大灵活的脚本!!现在,先来学习一下怎么用C调用Lua。
其实最简单的我们已经做过了,通过一个dofile,运行一个lua脚本文件。
Lua与C的交互是通过一个虚拟栈进行的,这个栈对于Lua来说是严格的LIFO(后进先出)的,当调用Lua时,Lua只会改变栈的顶部。不过C有更大的自由度,可以检索栈中元素,甚至在任意位置插入和删除元素。
当Lua启动或者Lua调用C语言时,栈中至少有20个空间的空闲槽,一般调用来说这些空间足够了。如果调用的参数特别特别多,需要先检查槽够不够用,使用下面的函数:
<span style="white-space:pre"> </span>int lua_checkstack(lua_State* L, int sz );API使用索引来引用栈中的元素,记住最开始的索引为1,不是0!即第一个压入栈中的元素索引为1,第二个压入栈中的元素索引为2,直到栈顶。也可以使用负数的索引来访问栈顶的元素,即-1表示栈顶元素,以此类推。
在C语言的lua库中,提供了几个关于栈中元素操作的函数,由于C语言实现里没有泛型,所以,对应每一种数据类型都提供了一个函数,这里后面的数据类型暂时用*代替。
//检查栈中index索引的数据类型是否是*的类型 int lua_is*(lua_State * L, int index) //返回栈中index索引的数据的类型 int lua_type(lua_State* L, int index) //返回栈中index索引的数据的值,转化为*的类型 * lua_to*(lua_State* L, int index) //向栈中插入*类型的元素 void lua_push*(lua_State* L, type*)
而既然这个东东是个栈,所以当然也提供了一些列栈本身的操作:
//获得栈中元素个数 int lua_gettop(lua_gettop) (lua_State *L); //设置栈顶为一个指定位置 void lua_settop(lua_settop) (lua_State *L, int idx); //将指定索引上的值再次压入栈 void lua_pushvalue(lua_State *L, int idx); //删除指定索引的元素,之上的向下移补缺 void lua_remove(lua_State* L, int idx); //在index处开辟一个位置,上面的上移,然后将栈顶元素放到这个位置 void lua_insert(lua_State* L, int idx); //弹出栈顶元素,使用该元素替代index元素 void lua_replace(lua_State* L, int idx);
// LuaTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
//因为Lua是C的函数,而我们的程序是C++的,所以要使用extern "C"引入头文件
extern "C"{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "luaconf.h"
}
//注意还需要添加之前编译好的Lualib.lib文件,这里通过项目->属性->连接器->附加依赖项添加了
//否则需要 #pragma comment(lib, "lualib.lib")来添加
//打印stack中的数据
void CheckStack(lua_State* L)
{
int top = lua_gettop(L);//stack的大小
//遍历stack所有层
for (int i = 1; i <= top; i++)
{
int type = lua_type(L, i);
switch (type)
{
case LUA_TSTRING://字符串
printf("%s\n", lua_tostring(L, i));
break;
case LUA_TBOOLEAN://布尔值
printf(lua_toboolean(L, i) ? "true\n" : "false\n");
break;
case LUA_TNUMBER://数字
printf("%d\n", lua_tonumber(L, i));
break;
default://其他值
printf("%s\n", lua_typename(L, i));
break;
}
}
printf("\n");
}
int _tmain(int argc, _TCHAR* argv[])
{
//打开lua
lua_State* L = luaL_newstate();
//加载lib文件
luaL_openlibs(L);
//向栈中压入内容
lua_pushboolean(L, 1);
lua_pushstring(L, "hehe");
lua_pushnumber(L, 100);
//打印栈中内容
CheckStack(L);
//将index为1的内容再次压入栈中
lua_pushvalue(L, 1);
CheckStack(L);
//删除index为2的元素
lua_remove(L, 2);
CheckStack(L);
//设置栈顶为16(这个空了的地方貌似被补成每个类型一种,其余为空了)
lua_settop(L, 16);
CheckStack(L);
//结束
lua_close(L);
system("pause");
return 0;
}
结果:
true
hehe
0
true
hehe
0
true
true
0
true
true
0
true
string
table
function
userdata
thread
proto
(null)
(null)
(null)
(null)
(null)
(null)
(null)
请按任意键继续. . .
--配置文件,包含两个全局变量 arg1 = 1 arg2 = 2
// LuaTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
//因为Lua是C的函数,而我们的程序是C++的,所以要使用extern "C"引入头文件
extern "C"{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "luaconf.h"
}
//注意还需要添加之前编译好的Lualib.lib文件,这里通过项目->属性->连接器->附加依赖项添加了
//否则需要 #pragma comment(lib, "lualib.lib")来添加
//打印stack中的数据
void CheckStack(lua_State* L)
{
int top = lua_gettop(L);//stack的大小
//遍历stack所有层
for (int i = 1; i <= top; i++)
{
int type = lua_type(L, i);
switch (type)
{
case LUA_TSTRING://字符串
printf("%s\n", lua_tostring(L, i));
break;
case LUA_TBOOLEAN://布尔值
printf(lua_toboolean(L, i) ? "true\n" : "false\n");
break;
case LUA_TNUMBER://数字
printf("%d\n", lua_tonumber(L, i));
break;
default://其他值
printf("%s\n", lua_typename(L, i));
break;
}
}
printf("\n");
}
//读取lua脚本文件的函数(此处作为一个配置文件)
void LoadLua(lua_State* L , const char* filename, int * arg1, int * arg2)
{
if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
printf("loadfile failed! %s\n", lua_tostring(L, -1));//如果失败,栈顶为错误信息
//获得全局变量,压入栈中
lua_getglobal(L, "arg1");
lua_getglobal(L, "arg2");
//判断一下素不素想要的类型
if (!lua_isnumber(L, -2))
printf("arg1 is not number\n");
if (!lua_isnumber(L, -1))
printf("arg2 is not number\n");
//提取栈中的参数值
*arg1 = lua_tointeger(L, -2);
*arg2 = lua_tointeger(L, -1);
}
int _tmain(int argc, _TCHAR* argv[])
{
//打开lua
lua_State* L = luaL_newstate();
//加载lib文件
luaL_openlibs(L);
int i, j = 0;
LoadLua(L, "test.lua", &i, &j);
printf("i = %d, j = %d\n", i , j);
//结束
lua_close(L);
system("pause");
return 0;
}
简单解释一下:
luaL_loadfile是加载lua文件,作为一个程序块,但是并不执行。
lua_pcall运行编译好的程序块。
lua_getglobal通过名称获得全局变量的值,压入栈中。
这样就通过C程序加载lua文件,达到了加载配置文件的目的,虽然看起来比较麻烦,不过封装一下的话,还是很好用的。而且使用Lua作为配置文件,一方面是容易操作,不需要额外写一些配置文件读取的工具,另一方面是可以在配置文件中添加注释等等,甚至还可以写一些条件判断等等。
--配置文件,包含两个全局变量
struct = {arg1 = 1, arg2 = 2}// LuaTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
//因为Lua是C的函数,而我们的程序是C++的,所以要使用extern "C"引入头文件
extern "C"{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "luaconf.h"
}
//注意还需要添加之前编译好的Lualib.lib文件,这里通过项目->属性->连接器->附加依赖项添加了
//否则需要 #pragma comment(lib, "lualib.lib")来添加
//打印stack中的大小
void CheckStack(lua_State* L)
{
int top = lua_gettop(L);//stack的大小
printf("The size of the stack is %d\n", top);
}
//读取一个table的数据,table更加结构化
void LoadLua(lua_State* L , const char* filename, int * arg1, int * arg2)
{
//和直接读取一样,先加载file,然后执行
if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
printf("loadfile failed! %s\n", lua_tostring(L, -1));//如果失败,栈顶为错误信息
CheckStack(L);//此时stack为空
//将struct结构体放入栈中
lua_getglobal(L, "struct");
if (! lua_istable(L, -1))
printf("struct is not table!\n");
CheckStack(L);//此时栈大小为1
//从struct中提取arg1放在栈顶
lua_getfield(L, -1, "arg1");
if (! lua_isnumber(L, -1))
printf("arg1 is not number!\n");
CheckStack(L);//此时栈大小为2
*arg1 = lua_tointeger(L, -1);
//上一个元素用完了,就把栈顶元素弹出
lua_pop(L, 1);
CheckStack(L);//此时栈大小为1
//提取arg2放在栈顶
lua_getfield(L, -1, "arg2");
if (! lua_isnumber(L, -1))
printf("arg2 is not number!\n");
CheckStack(L);
*arg2 = lua_tointeger(L, -1);
}
int _tmain(int argc, _TCHAR* argv[])
{
//打开lua
lua_State* L = luaL_newstate();
//加载lib文件
luaL_openlibs(L);
int i, j = 0;
LoadLua(L, "test.lua", &i, &j);
printf("i = %d, j = %d\n", i , j);
//结束
lua_close(L);
system("pause");
return 0;
}
还是上次的那两个数据,不过这次他们被放在了一个table里面,即使有相同的N组,也可以用N个table来存储,不用担心杂乱的问题。
简单分析一下C读取table的过程:
还是通过loadfile加载进来,然后仍然是getglobal获得全局变量,但是这次的全局变量是一个table,在进行下一步操作之前先检查一下是否真是个table,然后,通过另一个函数getfiled()获得table中特定字段的内容,放在栈顶。读取一个之后,将其弹出,然后再次获得下一个字段的内容,读取,以此类推。
赶脚这个table类型的配置文件就跟XML差不多了。一直没找到C++下的好的XML解析器,实在不行以后就用Lua把。
--add fucntion function add(a, b) return a + b end
// LuaTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
//因为Lua是C的函数,而我们的程序是C++的,所以要使用extern "C"引入头文件
extern "C"{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "luaconf.h"
}
//注意还需要添加之前编译好的Lualib.lib文件,这里通过项目->属性->连接器->附加依赖项添加了
//否则需要 #pragma comment(lib, "lualib.lib")来添加
//打印stack中的数据
void CheckStack(lua_State* L)
{
int top = lua_gettop(L);//stack的大小
//遍历stack所有层
for (int i = 1; i <= top; i++)
{
int type = lua_type(L, i);
switch (type)
{
case LUA_TSTRING://字符串
printf("%s ", lua_tostring(L, i));
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("\n");
printf("The size of the stack is %d\n", top);
}
//读取一个table的数据,table更加结构化
void LoadLua(lua_State* L , const char* filename, int * arg1, int * arg2)
{
//和直接读取一样,先加载file,然后执行
if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
printf("loadfile failed! %s\n", lua_tostring(L, -1));//如果失败,栈顶为错误信息
CheckStack(L);//此时stack为空
//将struct结构体放入栈中
lua_getglobal(L, "struct");
if (! lua_istable(L, -1))
printf("struct is not table!\n");
CheckStack(L);//此时栈大小为1
//从struct中提取arg1放在栈顶
lua_getfield(L, -1, "arg1");
if (! lua_isnumber(L, -1))
printf("arg1 is not number!\n");
CheckStack(L);//此时栈大小为2
*arg1 = lua_tointeger(L, -1);
//上一个元素用完了,就把栈顶元素弹出
lua_pop(L, 1);
CheckStack(L);//此时栈大小为1
//提取arg2放在栈顶
lua_getfield(L, -1, "arg2");
if (! lua_isnumber(L, -1))
printf("arg2 is not number!\n");
CheckStack(L);
*arg2 = lua_tointeger(L, -1);
}
//调用Lua的函数
double add(lua_State* L , double x, double y)
{
//压入函数和参数
//提取lua中的函数,放入栈中
lua_getglobal(L, "add");
//将两个参数压入栈中
lua_pushnumber(L, x);
lua_pushnumber(L, y);
CheckStack(L);
//进行调用
if (lua_pcall(L, 2, 1, 0) != 0)
printf("function called failed! %s\n", lua_tostring(L, -1));
CheckStack(L);
//从栈中提取结果
if (!lua_isnumber(L, -1))
printf("function must return a double number!\n");
double result = lua_tonumber(L, -1);
lua_pop(L, 1);
CheckStack(L);
return result;
}
//读取lua文件并执行函数
void GetLuaFunctiorn(lua_State* L, const char* filename)
{
if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
printf("load file failed! %s\n", lua_tostring(L, -1));
double resulet = add(L, 1.5, 2.5);
printf("result is %f\n", resulet);
}
int _tmain(int argc, _TCHAR* argv[])
{
//打开lua
lua_State* L = luaL_newstate();
//加载lib文件
luaL_openlibs(L);
//使用函数
GetLuaFunctiorn(L, "test.lua");
//结束
lua_close(L);
system("pause");
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/puppet_master/article/details/47980401