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

来源:互联网 发布:淘宝银行卡怎么解绑 编辑:程序博客网 时间:2024/04/29 18:55

通过c读取lua中的数据,还是从lua的栈上面提取数据

新建一个test.lua文件

str = "hello world!"num = 10b = true t = {    id = 1,    name = "sammy"}function testAdd(a,b)    print("a ="..a," b ="..b)    return a + bend

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-bend

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

0 0
原创粉丝点击