欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

LuaWithC++:Lua的基础语法

程序员文章站 2022-04-08 10:17:33
操作流程 1.宿主语言建立Lua解释器(lua状态机)对象。 2.将宿主语言实现的Lua扩展(若有),如函数等,注册到Lua解释器中,供其使用。 3.读入Lua源程序或预先编...
操作流程

1.宿主语言建立Lua解释器(lua状态机)对象。

2.将宿主语言实现的Lua扩展(若有),如函数等,注册到Lua解释器中,供其使用。

3.读入Lua源程序或预先编译后的Lua程序(可以从文件、字符串、网络等任意来源)。

4.执行读入的Lua程序。

Lua与宿主语言的交互

宿主语言通过虚拟机,对Lua脚本中的变量实现增、删、读、写

宿主语言通过虚拟机调用Lua脚本中的函数

宿主语言定义新的数据类型供Lua脚本使用

Lua调用宿主语言编写的函数

基本用法

需要引入的头文件:

extern "C"               //**使用C语言编译**!
{
   #include          //Lua语言解析器
   #include           //Lua标准库
   #include          //Lua辅助工具
}

C++对Lua脚本的调用、解析,无交互

char* code = "for i=0, 5 do print(\'Hello, world!\') end";

void TestCWithLua()
{
    cout << "/*******测试C++调用lua的代码*******/" << endl;
    //1.创建lua解释器对象
    lua_State* s = luaL_newstate(); /*lua_open();*/
    //2.打开所有lua的库文件
    luaL_openlibs(s);
    //3.执行lua字符串代码
    luaL_dostring(s, code);
    //luaL_dofile(s, "LuaSrc\\testcwithlua.lua");
    //4.关闭lua解释器对象
    lua_close(s);
    cout << "/*******结束测试*******/" << endl;
}

上例只实现了对Lua脚本的解析,并没有实现Lua与宿主语言的数据交换和互操作。

和典型的脚本语言引擎相同,Lua虚拟机是一个堆栈机,其一切运算基本都在堆栈上完成,这个堆栈也是Lua API的关键部分,是Lua与宿主语言交换数据的手段。

题外:宿主语言可以用字符串构建任意Lua脚本,实现向Lua程序传递任意数据,就像构建SQL语句一样,也不失是最“笨”的交互方式。

Lua堆栈

LuaWithC++:Lua的基础语法

Lua虚拟机内部有一个堆栈,Lua API提供了对其的操作,不仅有出入栈操作,还可以以数组的形式,通过索引值随机读写栈元素,这是双方交换数据的主要方式。

用宿主语言可以编写供Lua调用的函数,宿主语言需要遵守调用约定,从栈中取得参数,最后也将结果入栈。将宿主函数通过lua_register注册入Lua虚拟机(这一过程实质为向Lua语言添加全局变量),就可以被Lua语言所调用。

宿主语言也可以将Lua函数压栈,再将参数依次压栈,最后使用lua_call,完成对Lua函数的调用。

Lua堆栈索引

LuaWithC++:Lua的基础语法

若Lua虚拟机堆栈里有N个元素,则可以用 1 ~ N 从栈底向上索引,也可以用 -1 ~ -N 从栈顶向下索引,一般后者更加常用。

堆栈的每个元素可以为任意复杂的Lua数据类型,堆栈中没有元素的空位,隐含为包含一个“空”类型数据。

Lua调用C++

testluawithc.lua

a = 13 
b = 5 
q, r = p(a, b) 
print(q, r)
int Divided(lua_State* s)//供Lua使用的函数通用原型
{
    double a = lua_tonumber(s, -2);//取得第一个参数
    double b = lua_tonumber(s, -1);//取得第二个参数
    int ia = static_cast(a);
    int ib = static_cast(b);
    int quot = ia / ib;
    int rem = ia % ib;
    lua_pushnumber(s, quot);//将第一个返回值入栈
    lua_pushnumber(s, rem);//将第二个返回值入栈
    return 2;//返回值为结果个数
}

void TestLuaWithC()
{
    cout << "/*******测试lua调用c++的代码*******/" << endl;
    lua_State* s = luaL_newstate();
    luaL_openlibs(s);
    lua_register(s, "p", Divided);
    int ret = luaL_dofile(s, "LuaSrc\\testluawithc.lua");
    //luaL_dostring(s, "a = 13 b = 5 q, r = p(a, b) print(q, r)");
    lua_close(s);
    cout << "/*******结束测试*******/" << endl;
}

注意:参数的传递,都是通过Lua栈。

由上例可见,可被Lua调用的宿主函数具有统一的原型:int f(lua_State *s),数据传递不通过其参数,而是通过堆栈;整型返回值指明了该函数真正向Lua返回的值的个数,即压栈的结果个数。函数返回后,Lua虚拟机会自动进行清栈工作,不需在函数内部来做。

显然,在Lua中函数可以有不止一个返回值,这在Lua语法中也有体现,可以将函数返回赋值给多个变量。

C++调用lua,lua全局变量和函数调用,有交互

testglobal.lua

show = function(m)
    print('Lua has got: ' ..m)
    return 'It is from Lua'
end
void TestGlobalAndCall()
{
    cout << "/*******测试使用lua全局变量的代码*******/" << endl;
    lua_State* s = luaL_newstate();
    luaL_openlibs(s);
    /*luaL_dostring(s, "show = function(m) \
                                                    print('Lua has got: ' ..m) \     //..字符串连接符
                                                    return 'It is from Lua' \
                                                end");*/
    luaL_dofile(s, "LuaSrc\\testglobal.lua");
    lua_getglobal(s, "show");//获得全局变量show
    lua_pushstring(s, "It is from C");//将字符串压栈
    lua_call(s, 1, 1);//调用lua函数,1个参数,1个返回值
    const char* result = lua_tostring(s, -1);//获取执行后的栈顶元素,即函数执行结果
    cout << "C has got:" << result << endl;
    lua_pop(s, 1);//弹出栈顶元素
    lua_close(s);
    cout << "/*******结束测试*******/" << endl;
}