您的位置:首页 > 编程语言 > Lua

Lua & C 交互 3 c/c++调用lua

2016-01-11 23:18 555 查看
通过c读取lua中的数据,还是从lua的栈上面提取数据

新建一个test.lua文件

str = "hello world!"
num = 10
b = true
t = {
id = 1,
name = "sammy"
}

function testAdd(a,b)
print("a ="..a," b ="..b)
return a + b
end


lua文件里面写了字符串,数字,布尔值,table 和函数,通过lua_getglobal函数读取出来

int lua_getglobal(lua_State *L, const char *name)


将name值压入栈中,由lua查找,然后将找到的变量值返回到栈顶,然后我们可以通过lua_toxxx函数获得栈里面查找到的值

1.读取字符串,数字,布尔变量

#include <iostream>
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
int main()
{
lua_State *L = luaL_newstate();
if (!L)
return 1;

luaL_openlibs(L);

if (luaL_loadfile(L, "test.lua") || lua_pcall(L, 0, 0, 0))
{
std::cout << "error:" <<lua_tostring(L,-1)<< std::endl;
lua_close(L);
return 1;
}
std::cout << "current top index = "<< lua_gettop(L) << std::endl;
//读取数字
lua_getglobal(L, "num"); //查找变量num
std::cout << "num is number?  " << lua_isnumber(L, -1) << std::endl;
int num = lua_tonumber(L, -1);//栈顶存放着num的值,通过负数索引-1直接访问栈顶
std::cout << "num = " << num << std::endl;
std::cout << "current top index = " << lua_gettop(L) << std::endl;
lua_close(L);
return 0;
}


输出:

current top index = 0

current top index = 1

num is numberType? 1

num = 10

可以看到在使用lua_getglobal函数后,当前栈的长度变为1,栈顶存放着lua里面num变量的值

继续读取字符串,和布尔变量

//读取字符串
lua_getglobal(L, "str");
std::cout << "current top index = " << lua_gettop(L) << std::endl;
std::cout << "str is stringType?  " << lua_isstring(L, -1) << std::endl;
std::string str = lua_tostring(L, -1);
std::cout << "str = " <<str.c_str()<< std::endl;
std::cout << "" << std::endl;

//读取布尔值
lua_getglobal(L, "b");
std::cout << "current top index = " << lua_gettop(L) << std::endl;
std::cout << "b is booleanType?  " << lua_isboolean(L, -1) << std::endl;
bool b = lua_toboolean(L, -1);
std::cout << "b = " << b << std::endl;
std::cout << "" << std::endl;


输出:

current top index = 0

current top index = 1

num is numberType? 1

num = 10

current top index = 2

str is stringType? 1

str = hello world!

current top index = 3

b is booleanType? 1

b = 1

此时栈的情况可以一目了然地看出来了

2.读取lua中的table

lua文件中,我们有一个table

t = {
id = 1,
name = "sammy"
}


读取lua的table变量,table里面有两个字符串的key,我们可以用过lua_pushstring将key压入栈中,然后使用lua_gettable(lua_State *L, int index)函数查找t[key],然后弹出栈上的key值,将查找的结果放入key刚存放的栈的位置

lua_getglobal(L, "t");//t现在存放在索引-1(栈顶)位置
lua_pushstring(L, "id");//将key压栈
lua_gettable(L, -2);//这时候t位置就不再栈顶了,索引变为-2
int id = lua_tonumber(L, -1);//弹出栈的key,将找到的结果放入与key相同的栈的位置
lua_pushstring(L, "name");//同理查找key = name的值
lua_gettable(L, -3);
std::string name = lua_tostring(L, -1);
std::cout << "t = {\n id = " << id << ",\n name = " << name.c_str() << "  \n}" << std::endl;


输出:

t = {

id = 1,

name = sammy

}

还有一个函数lua_getfield(lua_State *L, int index, const char *k)(5.1引入的) 等价与上面的

lua_pushstring(L, "id");
lua_gettable(L, -2);


示例代码:

lua_getglobal(L, "t");
lua_getfield(L, -1, "id");
int id = lua_tonumber(L, -1);
lua_getfield(L, -2, "name");
std::string name = lua_tostring(L, -1);
std::cout << "t = { id = " << id<<",name = " << name.c_str() <<"}"<< std::endl;


lua中的table分为array 和map部分,在试这读取一个array部分的table

修改test.lua

t = {3,4,5,6}


同理,将key压入栈中,然后调用lua_gettable,只不过key不再是字符串,而是正整数了

lua_getglobal(L, "t");
lua_pushnumber(L, 1);
lua_gettable(L, -2);
int value1 = lua_tonumber(L, -1);
lua_pushnumber(L, 2);
lua_gettable(L, -3);
int value2 = lua_tonumber(L, -1);
lua_pushnumber(L, 3);
lua_gettable(L, -4);
int value3 = lua_tonumber(L, -1);
std::cout << "t = {\n "<<value1<<","<<value2<<","<<value3<<"  \n}" << std::endl;


输出:

t = {

3,4,5

}

遍历lua table

有时候并不会知道lua中的table里面究竟有多少值,这时候就需要通过遍历获得其中的值

通过使用lua_next函数遍历table

int lua_next (lua_State *L, int index);


这个函数会从index位置取得要遍历的table,返回值,先将key压入栈中,然后将value压入栈中,这时候key索引为-2,value索引-1

lua_getglobal(L, "t");
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
//此时栈的情况就是 -1 value   -2 key  -3 table
std::cout << "key = " << lua_tonumber(L, -2) << ",value = " << lua_tonumber(L, -1) << std::endl;
lua_pop(L, 1);
}


输出:

key = 1,value = 3

key = 2,value = 4

key = 3,value = 5

在调用lue_next前,先pushnil进去,这就要先看lua中next的用法,就知道原因了

next (table [, index])

运行程序来遍历表中的所有域。 第一个参数是要遍历的表,第二个参数是表中的某个键。 next 返回该键的下一个键及其关联的值。 如果用 nil 作为第二个参数调用 next 将返回初始键及其关联值。 当以最后一个键去调用,或是以 nil 调用一张空表时, next 返回 nil。 如果不提供第二个参数,将认为它就是 nil。 特别指出,你可以用 next(t) 来判断一张表是否是空的。

索引在遍历过程中的次序无定义, 即使是数字索引也是这样。 (如果想按数字次序遍历表,可以使用数字形式的 for 。)

当在遍历过程中你给表中并不存在的域赋值, next 的行为是未定义的。 然而你可以去修改那些已存在的域。 特别指出,你可以清除一些已存在的域。

压入一个nil进入栈中,lua_next函数返回初始key和value了

尝试push一个key = 1值进去

lua_getglobal(L, "t");
lua_pushnumber(L,1);
while (lua_next(L, -2) != 0) {
std::cout << "key = " << lua_tonumber(L, -2) << ",value = " << lua_tonumber(L, -1) << std::endl;
lua_pop(L, 1);
}


输出:

key = 2,value = 4

key = 3,value = 5

在遍历中,在遍历中会调用lua_pop来弹出1个元素,这里将value弹出,此时栈顶的值就变成key了,为下一次next函数做准备

遍历包含array和map部分的table示例

test.lua文件:

t = {
1,2,3,4,
id = 23,
name = "tom",
age = 20,
}


c++代码(其实还少了好几个类型的判断,真正读取lua中的table的时候,根据实际情况来遍历)

lua_getglobal(L, "t");
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
std::string keyType = lua_typename(L, lua_type(L, -2));
std::string valueType = lua_typename(L, lua_type(L, -1));

if (keyType == std::string("number"))
{
std::cout << "key = " << lua_tonumber(L, -2);
}
else if (keyType == std::string("string"))
{
std::cout << "key = " << lua_tostring(L, -2);
}

if (valueType == std::string("number"))
{
std::cout << ",value = " << lua_tonumber(L, -1) << std::endl;;
}
else if (valueType == std::string("string"))
{
std::cout << ",value = " << lua_tostring(L, -1) << std::endl;;
}

lua_pop(L, 1);
}


输出:

key = 1,value = 1

key = 2,value = 2

key = 3,value = 3

key = 4,value = 4

key = id,value = 23

key = name,value = tom

key = age,value = 20

3.调用lua中的函数

c调用lua的函数,方法和上面差不多,就是取出函数压入栈中后,更具函数需要什么参数,往栈里面压入函数需要参数

int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);

以保护模式调用一个函数。

nargs 和 nresults 的含义与 lua_call 中的相同。 如果在调用过程中没有发生错误, lua_pcall 的行为和 lua_call 完全一致。 但是,如果有错误发生的话, lua_pcall 会捕获它, 然后把唯一的值(错误消息)压栈,然后返回错误码。 同 lua_call 一样, lua_pcall 总是把函数本身和它的参数从栈上移除。

如果 msgh 是 0 , 返回在栈顶的错误消息就和原始错误消息完全一致。 否则, msgh 就被当成是 错误处理函数 在栈上的索引位置。 (在当前的实现里,这个索引不能是伪索引。) 在发生运行时错误时, 这个函数会被调用而参数就是错误消息。 错误处理函数的返回值将被 lua_pcall 作为错误消息返回在堆栈上。

典型的用法中,错误处理函数被用来给错误消息加上更多的调试信息, 比如栈跟踪信息。 这些信息在 lua_pcall 返回后, 由于栈已经展开,所以收集不到了。

lua_pcall 函数会返回下列常数 (定义在 lua.h 内)中的一个:

LUA_OK (0): 成功。

LUA_ERRRUN: 运行时错误。

LUA_ERRMEM: 内存分配错误。对于这种错,Lua 不会调用错误处理函数。

LUA_ERRERR: 在运行错误处理函数时发生的错误。

LUA_ERRGCMM: 在运行 __gc 元方法时发生的错误。 (这个错误和被调用的函数无关。)

最后函数运行返回的结果,会压入栈中

//调用lua函数
lua_getglobal(L, "testAdd");//将获得函数压栈
lua_pushnumber(L, 15);//第一个参数压栈
lua_pushnumber(L, 24);//第二个参数压栈
if (lua_pcall(L, 2, 1, 0))//调用lua_pcall(L,参数个数,返回值个数,msgh)
{
std::cout << "error:" << lua_tostring(L, -1)<< std::endl;
lua_close(L);
return 1;
}
if (lua_isnumber(L, -1))//调用成功后,将结果压入栈中
{
int value = lua_tonumber(L, -1);
std::cout << "testAdd:value = " << value << std::endl;
}


输出:

a =15 b =24

testAdd:value = 39

调用多个返回值的lua函数的示例

test.lua文件

function test(a,b)
print("a ="..a," b ="..b)
return a + b,a-b
end


c++代码

lua_getglobal(L, "test");
lua_pushnumber(L, 15);
lua_pushnumber(L, 24);
if (lua_pcall(L, 2, 2, 0)) //第三个参数改为2表示有2个返回值
{
std::cout << "error:" << lua_tostring(L, -1) << std::endl;
lua_close(L);
return 1;
}

std::cout << "test = " << lua_tonumber(L, -2) << "," << lua_tonumber(L, -1) << std::endl;
//在lua中先返回第一个值压入栈中,然后第二个值返回,压入栈中,此时返回值1索引-2,返回值2索引-1


输出:

a =15 b =24

test = 39,-9
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: