lua pcall 返回值_游戏开发实现C++与Lua交互!

一、搭建C++调用Lua环境

一、环境准备

从Lua5.1.4开始官方给出的文件只有源代码和makefile文件了,官网给出的bulid方式也是在类linux平台,如果只是想找个库使用下可以到这里来下载:joedf.ahkscript.org/Lua ,如果需要自定修改库配置的话,就需要自己编译。关于编译Windows版本的教程网上也有很多,如果我有时间,后续也会写一篇编译教程。

附录

1、Lua5.1.4 for Windows之前的下载地址:files.luaforge.net/rele

2、源码的下载地址:lua.org/ftp/#

二、开发环境

1、我使用的是vs2017写的测试用例,首先建立一个空的C++控制台应用程序,然后在里面创建一个LuaTest.cpp文件和一个Test.lua文件,目录结构如下:

ac00fc181751487baef1b77d05f74122.png

2、添加项目包含目录和依赖项。我是把安装的Lua文件直接拷贝到新建的项目工程内的,这样做的好处,是方便把测试工程给大家,不需要安装Lua,工程就可以直接运行。

b69c0e8799e5bb138852fa02b1c010d8.png
0616cfcabd7ab9b3d01ad85cb29e3d4d.png
0f8e7bb778a14eb8edc5ac41f723c1e9.png

三、代码

1、在Test.lua文件内添加如下代码:

print "Hello, Lua! Demo1"

2、在LuaTest.cpp文件内添加如下代码:

#include extern "C" {#include "lua.h"#include "lualib.h"#include "lauxlib.h"}lua_State* L;int main(int argc, char *argv[]){L = lua_open();luaL_openlibs(L);luaL_dofile(L, "Test.lua");lua_close(L);printf("Press enter to exit...");getchar();return 0;}

四、测试

如果一切顺利,此时你按下F5,应该会弹窗如下窗口,说明你环境搭建成功了。后续我也会把整个系列文章的测试工程地址发给大家。

dde23a97ae065a3c7ca61f86cbcacfb2.png

二、C++调用Lua函数

上一篇文章中我们已经把测试环境搭建完毕了,接下来就用上次的项目工程进行代码测试和分析。

这篇文章主要讲在C++中怎么调用Lua中的函数add,并且把lua中函数计算结果返回给C++,然后在打印出来计算的结果。

一、直接上代码:

1、在Test.lua文件内添加如下代码:

print "Hello, Lua Demo2!"function add(x,y)return x + yend

2、在LuaTest.cpp文件内添加如下代码:

#include extern "C" {#include "lua.h"#include "lualib.h"#include "lauxlib.h"}lua_State* L;int LuaAdd(int x,int y){int sum;//code5lua_getglobal(L, "add");//code6lua_pushnumber(L, x);//code7lua_pushnumber(L, y);//code8lua_call(L, 2, 1);//code9sum = (int)lua_tointeger(L, -1);//code10lua_pop(L, 1);return sum;}int main(int argc, char *argv[]){int sum;//code1L = lua_open();//code2luaL_openlibs(L);//code3luaL_dofile(L, "Test.lua");//code4sum = LuaAdd(2014, 15);printf("The sum is: %d\n", sum);//code11lua_close(L);printf("Press enter to exit...");getchar();return 0;}

二、代码分析

code1,通过lua_open()函数来创建一个lua的虚拟机L。Tips:在5.2以及后续版本中已经被废弃,请使用新的函数luaL_newstate和lua_newstate。lua_newstate可自定义内存分配函数,luaL_newstate使用默认的内存分配方式。

code2,打开Lua中的所有标准库,如io库、string库等。

code3,luaL_dofile来加载和执行Test.lua脚本。参数是lua脚本的路径,由于我的lua文件就在工程根目录,所有直接写脚本名字就可以了。l

uaL_dofile函数的宏定义如下:

cd9fc67fd550ca5c5ff3183f6f8505a1.png

Tips:luaL_dofile函数实际上是执行了lua_load函数来加载lua文件,加载成功之后会编译一个代码块作为一个匿名函数放置在栈顶。然后调用lua_pcall执行匿名代码块,最终C代码才能调用lua中的函数和变量等等。

code4,是执行我们自己写的一个加法函数。里面封装里对lua的一些调用

code5,lua_getglobal是从全局表中找到add字段对应的数据并把它送入栈顶。

我们看一下lua_getglobal的定义,其实就是一个宏。

通过lua_getfield把字段s送入到栈中。可参考栈的运行图Log index 1

7ebb026c7cc1c73c1da2de63c854a411.png
53ecd3223b248a468c1ea3c37ac4b636.png

code6,lua_pushnumber把参数x的值压如栈中。

code7,lua_pushnumber把参数y的值压如栈中。此时栈内有三条数据了。最终站内的变化,可以参考栈的运行图Log index 2

code8,lua_call函数告诉lua虚拟机 L,它传入2个参数,需要返回1个值。这时候lua主程序开始把栈内的2个参数数据取出来,然后传入到函数add中。然后执行函数add,最后把计算出来的结果返回到栈顶。执行玩lua_call之后,栈内只剩下一个函数的返回值了。效果如栈的运行图Log index 3

code9,lua_tointeger是去栈顶取出返回值,然后复制给sum

code10,是一个宏,用于从虚拟栈中弹出指定数量的元素,这里的1表示仅弹出栈顶的元素。弹出一个元素之后,此时栈内没有数据了。参考栈的运行图Log index 4

code11,lua_close关闭当前虚拟L,并释放L所占用的动态内存。

三、运行结果如下图

c9e5b0a229b7c2cbd03b48b0370ac47a.png

四、程序运行时栈内的变化情况如下图:

b7649aafac5c9ae90a95b3b9f1558c4e.png

三、Lua调用C++函数

上一篇文章中我们已经知道了,C++怎么调用Lua中的函数,接下来我们学习一下,Lua怎么调用C++中的函数。

这篇文章主要讲在Lua中执行average()函数,怎么调用到C++中的Average函数。然后把Average函数的执行结果再返回给Lua中。

一、直接上代码:

1、在Test.lua文件内添加如下代码:

print "Hello, Lua! Demo3"avg, sum = average(10,20,30,40,50);print("The average is ", avg)print("The sum is ", sum)

2、在LuaTest.cpp文件内添加如下代码:

#include extern "C" {#include "lua.h"#include "lualib.h"#include "lauxlib.h"}lua_State* L;static int Average(lua_State *L){//code3int n = lua_gettop(L);double sum = 0;//code4for (int i = 1; i <= n; ++i){sum += lua_tonumber(L, i);}//code5lua_pushnumber(L, sum / n);lua_pushnumber(L, sum);//code6return 2;}int main(int argc, char *argv[]){L = lua_open();luaL_openlibs(L);//code1lua_register(L, "average", Average);//code2luaL_dofile(L, "Test.lua");lua_close(L);printf("Press enter to exit...");getchar();return 0;}

二、代码分析,和上一篇C++调用Lua中重复的函数,这里就不做分析了,不明白的,可以去看上一篇。

code1、lua_register注册函数把Lua函数和C++函数进行绑定。我们F12看一下lua_register里面怎么定义的。lua_register其实是一个宏定义

cde617fd769a12b22cac5aa0a1f75995.png

包括lua_pushcfunction和lua_setglobal操作。其实就是先用lua_pushcfunction把在c++中定义的函数压如栈中,然后lua_setglobal来设置栈顶的元素对应的值,这样就可以把lua函数和栈顶的c++函数建立引用关系。

lua_setglobal其实也是一个宏定义,就是一个特殊的lua_setfield操作。

6c24afbd07b83811811be6c0989e47c9.png
3d88161b42b7d721482ed87aa28e98b7.png

code2、加载并执行lua脚本,此时lua中的函数average被执行,同时向栈中压如5个参数。参考栈的运行图Log index 1

code3、 lua_gettop是取出栈顶的索引值。此时栈顶的索引值大小就是站内元素的个数。

code4、使用循环变量站内所有的元素,通过lua_tonumber取出站内的值,然后进行相加操作。

code5、把要返回的值再压如栈。此时此时栈内7条数据,参考栈的运行图Log index 2

code6、告诉lua主程序,返回2个值。lua这是可以用参数接受这两个值

三、运行结果如下图

d06f552f6061036b7ea2f5394ae192a5.png

四、程序运行时栈内的变化情况如下图:

d90c7f9249a0e1a917017a9ff28525e6.png

四、C++获得Lua的变量和Table的值

上两篇文章都已经把Lua和C++函数的调用讲完了,这篇开始讲变量和Table的调用。

这篇文章主要是讲C++怎么调用获得Lua中的变量和Table的值,并且把lua中的值打印出来。

一、直接上代码:

1、在Test.lua文件内添加如下代码:

print "Hello, Lua Demo4!"name="my name is lua"nameTable={sex = "male", age=18}

2、在LuaTest.cpp文件内添加如下代码:

#include extern "C" {#include "lua.h"#include "lualib.h"#include "lauxlib.h"}lua_State* L;int main(int argc, char *argv[]){L = lua_open();luaL_openlibs(L);luaL_dofile(L, "Test.lua");lua_settop(L, 0);//code1lua_getglobal(L, "name");//code2int isStr = lua_isstring(L, -1);if (isStr == 0){printf("stack top is not string\n");}else{printf("stack top is string\n");}//code3const char* strName = lua_tostring(L, -1);printf("name: %s\n", strName);//code4lua_getglobal(L, "nameTable");//code5lua_pushstring(L, "sex");lua_gettable(L, -2);lua_pushstring(L, "age");lua_gettable(L, -3);//code6int iAge = (int)lua_tointeger(L, -1);const char* strSex = lua_tostring(L, -2);printf("age: %d\n", iAge);printf("sex: %s\n", strSex);lua_close(L);/* pause */printf("Press enter to exit...");getchar();return 0;}

二、代码分析,曾经讲过的函数这里就不做分析了,不明白的,可以去看前面的文章。

code1、因为luaL_dofile(L, "Test.lua")已经把lua文件加载到内存并行执行了pcall函数。lua_getglobal(L, "name")就是从全局表中找到name字段对应的值,并把它放到栈顶。可以参考栈的运行图Log index 1

code2、lua_isstring(L, -1)是用来判断栈顶是否是string类型,还有一些类似的函数,可以自行查看API。

code3、lua_tostring(L, -1)从栈顶取出值,然后赋值给一个变量使用。数据还在栈没,没有弹出。

code4、lua_getglobal(L, "nameTable")从全局表中找到nameTable对应的数据,并把他放到栈顶。此时栈内有两条数据了,看栈的运行图Log index 2

code5、lua_pushstring是向栈内压如一个值。lua_gettable是从table中取出刚才压入的数据对应的值,并且替换掉sex。从栈的运行图Log index 3中,可以清晰的看出,数据已经从table中取出放到栈上了

code6、分别使用系统函数 lua_tointeger和lua_tostring取出栈上面的值。最终栈内是四个值,如栈的运行图Log index 4。如果此时调用lua_settop(L, 0) 那么会清空栈内所有的数据。

三、运行结果如下图

020556adec63fda762100920538411f4.png

四、程序运行时栈内的变化情况如下图:

8d1eb71ae16b390a2377b4e0cfce1198.png

五、C++修改Lua的变量和Table的值

上一篇文章讲了C++如何获得Lua中的变量和Table中的值,这篇文章主要讲如何修改Lua中的变量的值和Table中的变量的值,并把修改后的值打印出来。

一、直接上代码:

1、在Test.lua文件内添加如下代码:

print "Hello, Lua Demo5"name="my name is lua"iVar = 5nameTable={sex = "male", age=18}function PrintLuaLog()print("name: " ..name)print("iVar: " ..iVar)for key, value in pairs(nameTable) doprint("key: "..key .. " value: ".. value)endendfunction AddIncrease()iVar = iVar + 10nameTable.age = nameTable.age + 10endPrintLuaLog()print("//do lua end")

2、在LuaTest.cpp文件内添加如下代码:

int main(int argc, char *argv[]){L = lua_open();luaL_openlibs(L);luaL_dofile(L, "Test.lua");lua_getglobal(L, "name");const char* strName = lua_tostring(L, -1);printf("name: %s\n", strName);StackDump(L, 1);//code1lua_pop(L, 1);//code2lua_pushstring(L, "my name is lua modify ");lua_setfield(L, LUA_GLOBALSINDEX, "name");StackDump(L, 2);//code3lua_getglobal(L, "name");StackDump(L, 3);printf("setglobal name: %s\n", lua_tostring(L, -1));//code4lua_settop(L, 0);//code5lua_getglobal(L, "nameTable");//code6//lua_pushstring(L, "sex");//lua_gettable(L, -2);//lua_pushstring(L, "age");//lua_gettable(L, -3);//code7lua_getfield(L, -1, "sex");lua_getfield(L, -2, "age");StackDump(L, 4);printf("age: %d\n", lua_tointeger(L, -1));printf("sex: %s\n", lua_tostring(L, -2));//code8//lua_pushstring(L, "age");//lua_pushnumber(L, 19);//lua_settable(L, 1);//code9lua_pushnumber(L, 19);lua_setfield(L, 1, "age");//code10//lua_pushstring(L, "age");//lua_gettable(L, 1);code11lua_getfield(L, 1, "age");StackDump(L, 5);printf("setglobal age: %d\n", (int)lua_tointeger(L, -1));//code12lua_getglobal(L, "PrintLuaLog");lua_pcall(L, 0, 0, 0);StackDump(L, 6);//code13lua_getglobal(L, "AddIncrease");lua_pcall(L, 0, 0, 0);//code14lua_getglobal(L, "PrintLuaLog");lua_pcall(L, 0, 0, 0);//code15lua_settop(L, 0);lua_getglobal(L, "iVar");printf("iVar: %d\n", lua_tointeger(L, -1));lua_pop(L, 1);//code16lua_getglobal(L, "nameTable");lua_getfield(L, -1, "age");printf("age: %d\n", lua_tointeger(L, -1));StackDump(L, 7);lua_close(L);printf("Press enter to exit...");getchar();return 0;}

二、代码分析,曾经讲过的函数这里就不做分析了,不明白的,可以去看前面的文章。

code1、执行lua_pop,把上面为了打印name数据而压入栈的数据弹出栈,保持栈是空的。

code2、用"my name is lua modify "的值来替换掉原来lua中name字段的值。修改规则,先压入栈内一个要修改的值value,然后调用lua_setfield,通过全局索引和key字段来进行修改要修改的key字段对应的值。修改之后会把刚才压入栈的值弹出栈。此时栈还是为空,参考栈的运行图Log index2

code3、打印刚才修改后的name值,确认一下是否修改成功。

code4、lua_settop(L, 0)是清空栈的操作函数,无论栈中有多少元素,全部清空。

code5、开始把lua中的nameTable压如栈中等待使用。

code6、code6和code7的执行效果一样,是两种获得lua table中数据的方式

code7、把table中的数据压如栈中,等待使用。

code8、code8和code9的执行效果都是一样,都是修改table中age字段的值。我们看修改规则:先把table中的key压入栈中,然后再压入要修改的值value,最后调用lua_settable来修改。修改执行完毕会把刚才压入栈中的两个值全部弹出栈。

code9、修改table中字段的值的规则,先把要修改的值压入栈中,然后调用lua_setfield来用栈顶的值修改掉原来table中的key对应的值。修改执行完毕,会把刚才压入栈中的值全部弹出。

code10、code10和code11的执行结果是一样的,都是从table中取出字段age的值,然后压入栈中。

code12、是执行lua中的打印函数,来验证一下lua中的值是否被掉。

code13、是执行lua中的递加方法,然后看一下执行之后,lua中的各个变量的值

codd14、再次打印递加之后的lua中变量的值

code15、通过C++调用的方式打印lua中变量iVar的值

code16、通过C++调用的方式打印lua table中的变量的值。

所有的执行情况,可以参照栈的运行图,里面有详细的数据入栈和出栈操作

三、运行结果如下图

27f6a169e8f7732f847a6b6ce7eabfe0.png

四、程序运行时栈内的变化情况如下图:

23f8fc5292ce4570ef1c5dcd6baedbc9.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值