0
点赞
收藏
分享

微信扫一扫

【COCOS2DX-LUA 脚本开发之十一】C/C++与Lua之间进行数据函数交互以及解决“PANIC: unprotected error in call to Lua API (attempt t

寒羽鹿 2022-11-24 阅读 102

本站文章均为​​李华明Himi​​​原创,转载务必在明显处注明:(作者新浪微博:​​@李华明Himi​​​) 


                  width="150" height="210" frameborder="0" scrolling="no" src="http://widget.weibo.com/relationship/bulkfollow.php?language=zh_cn&uids=1916000601&wide=1&color=FFFFFF,FFFFFF,0082CB,666666&showtitle=0&showinfo=1&sense=0&verified=1&count=1&refer=http%3A%2F%2Fwww.himigame.com%2Flua1%2F1343.html&dpc=1" style="border-width: 0px; margin: 0px; padding: 0px; font-family: arial, helvetica, clean, sans-serif; font-size: 12px; line-height: 16px; text-align: left; ">



在使用Cocos2d-x 时候,难免需要C/C++调用Lua函数、数据或Lua调用C/C++函数,那么本篇讲详细介绍C/C++与Lua之间的数据、函数交互。

首先让我们来简单了解几个Lua API函数:

int   luaL_dofile

加载并运行指定文件,没有错误返回0

void  lua_settop

参数允许传入任何可接受的索引以及 0 。 它将把堆栈的栈顶设为这个索引。 如果新的栈顶比原来的大,超出部分的新元素将被填为 nil 。 如果 index 为 0 ,把栈上所有元素移除。

void   lua_getglobal

把全局变量 name 里的值压入堆栈。

void   lua_pop

从堆栈中弹出 ​​n​

void   lua_pushstring

把指针 s 指向的以零结尾的字符串压栈。 Lua 对这个字符串做一次内存拷贝(或是复用一个拷贝), 因此 s 处的内存在函数返回后,可以释放掉或是重用于其它用途。 字符串中不能包含有零字符;第一个碰到的零字符会认为是字符串的结束。

更多的API请参考:​​http://www.codingnow.com/2000/download/lua_manual.html​​

了解了以上几个函数,为了方便童鞋们使用,Himi直接贴出封装好的类 HclcData,其中主要包括如下几个功能:

1. C/C++ 调用 Lua 全局变量

2. C/C++ 调用 Lua 全局Table 某元素

3. C/C++ 调用 Lua 全局Table

4. C/C++ 调用 Lua 函数

5. Lua 调用C/C++ 函数

 

下面直接贴出代码:HclcData.h

//​​       

​​// HclcData.h​​

​​// CppLua​​

​​//​​

​​// Created by Himi on 13-4-17.​​

​​//​​

​​//​​



​​#ifndef __CppLua__HclcData__​​

​​#define __CppLua__HclcData__​​



​​#include "cocos2d.h"​​

​​using​​​ ​​namespace​​​ ​​cocos2d;​​

​​using​​​ ​​namespace​​​ ​​std;​​



​​extern​​​ ​​"C"​​​ ​​{​​

​​#include "lua.h"​​

​​#include "lualib.h"​​

​​#include "lauxlib.h"​​

​​};​​



​​class​​​ ​​HclcData{​​

​​public​​​​:​​

​​static​​​ ​​HclcData* sharedHD();​​



​​//------------ c++ -> lua ------------//​​



​​/*​​

​​getLuaVarString : 调用lua全局string​​



​​luaFileName = lua文件名​​

​​varName = 所要取Lua中的变量名​​

​​*/​​

​​const​​​ ​​char​​​​* getLuaVarString(​​​​const​​​ ​​char​​​​* luaFileName,​​​​const​​​ ​​char​​​​* varName);​​



​​/*​​

​​getLuaVarOneOfTable : 调用lua全局table中的一个元素​​



​​luaFileName = lua文件名​​

​​varName = 所要取Lua中的table变量名​​

​​keyName = 所要取Lua中的table中某一个元素的Key​​

​​*/​​

​​const​​​ ​​char​​​​* getLuaVarOneOfTable(​​​​const​​​ ​​char​​​​* luaFileName,​​​​const​​​ ​​char​​​​* varName,​​​​const​​​ ​​char​​​​* keyName);​​



​​/*​​

​​getLuaVarTable : 调用lua全局table​​



​​luaFileName = lua文件名​​

​​varName = 所要取的table变量名​​



​​(注:返回的是所有的数据,童鞋们可以自己使用Map等处理)​​

​​*/​​

​​const​​​ ​​char​​​​* getLuaVarTable(​​​​const​​​ ​​char​​​​* luaFileName,​​​​const​​​ ​​char​​​​* varName);​​



​​/*​​

​​callLuaFunction : 调用lua函数​​



​​luaFileName = lua文件名​​

​​functionName = 所要调用Lua中的的函数名​​

​​*/​​

​​const​​​ ​​char​​​​* callLuaFunction(​​​​const​​​ ​​char​​​​* luaFileName,​​​​const​​​ ​​char​​​​* functionName);​​



​​//------------ lua -> c++ ------------//​​



​​void​​​ ​​callCppFunction(​​​​const​​​ ​​char​​​​* luaFileName);​​



​​private​​​​:​​

​​static​​​ ​​int​​​ ​​cppFunction(lua_State* ls);​​



​​static​​​ ​​bool​​​ ​​_isFirst;​​

​​static​​​ ​​HclcData* _shared;​​

​​const​​​ ​​char​​​​* getFileFullPath(​​​​const​​​ ​​char​​​​* fileName);​​

​​~HclcData();​​

​​};​​



​​#endif /* defined(__CppLua__HclcData__) */​​

 

HclcData.cpp

//​​       

​​// HclcData.cpp​​

​​// CppLua​​

​​//​​

​​// Created by Himi on 13-4-17.​​

​​//​​

​​//​​



​​#include "HclcData.h"​​

​​#include "CCLuaEngine.h"​​



​​bool​​​ ​​HclcData::_isFirst;​​

​​HclcData* HclcData::_shared;​​



​​HclcData* HclcData::sharedHD(){​​

​​if​​​​(!_isFirst){​​

​​_shared = ​​​​new​​​ ​​HclcData();​​

​​}​​

​​return​​​ ​​_shared;​​

​​}​​



​​const​​​ ​​char​​​​* HclcData::getLuaVarString(​​​​const​​​ ​​char​​​​* luaFileName,​​​​const​​​ ​​char​​​​* varName){​​



​​lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();​​



​​int​​​ ​​isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));​​

​​if​​​​(isOpen!=0){​​

​​CCLOG(​​​​"Open Lua Error: %i"​​​​, isOpen);​​

​​return​​​ ​​NULL;​​

​​}​​



​​lua_settop(ls, 0);​​

​​lua_getglobal(ls, varName);​​



​​int​​​ ​​statesCode = lua_isstring(ls, 1);​​

​​if​​​​(statesCode!=1){​​

​​CCLOG(​​​​"Open Lua Error: %i"​​​​, statesCode);​​

​​return​​​ ​​NULL;​​

​​}​​



​​const​​​ ​​char​​​​* str = lua_tostring(ls, 1);​​

​​lua_pop(ls, 1);​​



​​return​​​ ​​str;​​

​​}​​



​​const​​​ ​​char​​​​* HclcData::getLuaVarOneOfTable(​​​​const​​​ ​​char​​​​* luaFileName,​​​​const​​​ ​​char​​​​* varName,​​​​const​​​ ​​char​​​​* keyName){​​



​​lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();​​



​​int​​​ ​​isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));​​

​​if​​​​(isOpen!=0){​​

​​CCLOG(​​​​"Open Lua Error: %i"​​​​, isOpen);​​

​​return​​​ ​​NULL;​​

​​}​​



​​lua_getglobal(ls, varName);​​



​​int​​​ ​​statesCode = lua_istable(ls, -1);​​

​​if​​​​(statesCode!=1){​​

​​CCLOG(​​​​"Open Lua Error: %i"​​​​, statesCode);​​

​​return​​​ ​​NULL;​​

​​}​​



​​lua_pushstring(ls, keyName);​​

​​lua_gettable(ls, -2);​​

​​const​​​ ​​char​​​​* valueString = lua_tostring(ls, -1);​​



​​lua_pop(ls, -1);​​



​​return​​​ ​​valueString;​​

​​}​​



​​const​​​ ​​char​​​​* HclcData::getLuaVarTable(​​​​const​​​ ​​char​​​​* luaFileName,​​​​const​​​ ​​char​​​​* varName){​​

​​lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();​​



​​int​​​ ​​isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));​​

​​if​​​​(isOpen!=0){​​

​​CCLOG(​​​​"Open Lua Error: %i"​​​​, isOpen);​​

​​return​​​ ​​NULL;​​

​​}​​



​​lua_getglobal(ls, varName);​​



​​int​​​ ​​it = lua_gettop(ls);​​

​​lua_pushnil(ls);​​



​​string result=​​​​""​​​​;​​



​​while​​​​(lua_next(ls, it))​​

​​{​​

​​string key = lua_tostring(ls, -2);​​

​​string value = lua_tostring(ls, -1);​​



​​result=result+key+​​​​":"​​​​+value+​​​​"\t"​​​​;​​



​​lua_pop(ls, 1);​​

​​}​​

​​lua_pop(ls, 1);​​



​​return​​​ ​​result.c_str();​​

​​}​​



​​const​​​ ​​char​​​​* HclcData::callLuaFunction(​​​​const​​​ ​​char​​​​* luaFileName,​​​​const​​​ ​​char​​​​* functionName){​​

​​lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();​​



​​int​​​ ​​isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));​​

​​if​​​​(isOpen!=0){​​

​​CCLOG(​​​​"Open Lua Error: %i"​​​​, isOpen);​​

​​return​​​ ​​NULL;​​

​​}​​



​​lua_getglobal(ls, functionName);​​



​​lua_pushstring(ls, ​​​​"Himi"​​​​);​​

​​lua_pushnumber(ls, 23);​​

​​lua_pushboolean(ls, ​​​​true​​​​);​​



​​/*​​

​​lua_call​​

​​第一个参数:函数的参数个数​​

​​第二个参数:函数返回值个数​​

​​*/​​

​​lua_call(ls, 3, 1);​​



​​const​​​ ​​char​​​​* iResult = lua_tostring(ls, -1);​​



​​return​​​ ​​iResult;​​

​​}​​



​​void​​​ ​​HclcData::callCppFunction(​​​​const​​​ ​​char​​​​* luaFileName){​​



​​lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();​​



​​/*​​

​​Lua调用的C++的函数必须是静态的​​

​​*/​​

​​lua_register(ls, ​​​​"cppFunction"​​​​, cppFunction);​​



​​int​​​ ​​isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));​​

​​if​​​​(isOpen!=0){​​

​​CCLOG(​​​​"Open Lua Error: %i"​​​​, isOpen);​​

​​return​​​​;​​

​​}​​

​​}​​



​​int​​​ ​​HclcData::cppFunction(lua_State* ls){​​

​​int​​​ ​​luaNum = (​​​​int​​​​)lua_tonumber(ls, 1);​​

​​int​​​ ​​luaStr = (​​​​int​​​​)lua_tostring(ls, 2);​​

​​CCLOG(​​​​"Lua调用cpp函数时传来的两个参数: %i %s"​​​​,luaNum,luaStr);​​



​​/*​​

​​返给Lua的值​​

​​*/​​

​​lua_pushnumber(ls, 321);​​

​​lua_pushstring(ls, ​​​​"Himi"​​​​);​​



​​/*​​

​​返给Lua值个数​​

​​*/​​

​​return​​​ ​​2;​​

​​}​​



​​const​​​ ​​char​​​​* HclcData::getFileFullPath(​​​​const​​​ ​​char​​​​* fileName){​​

​​return​​​ ​​CCFileUtils::sharedFileUtils()->fullPathForFilename(fileName).c_str();​​

​​}​​



​​HclcData::~HclcData(){​​



​​CC_SAFE_DELETE(_shared);​​

​​_shared=NULL;​​

​​}​​

 

大家可以直接拿来用的,使用简单,测试如下:

首先C++测试代码:

#include "HclcData.h"​​       



​​CCLOG(​​​​"Str = %s"​​​​,HclcData::sharedHD()->getLuaVarString(​​​​"Test.lua"​​​​,​​​​"luaStr"​​​​));​​

​​CCLOG(​​​​"Str2 %s"​​​​,HclcData::sharedHD()->getLuaVarString(​​​​"Test.lua"​​​​,​​​​"luaStr2"​​​​));​​

​​CCLOG(​​​​"age = %s"​​​​,HclcData::sharedHD()->getLuaVarOneOfTable(​​​​"Test.lua"​​​​, ​​​​"luaTable"​​​​,​​​​"age"​​​​));​​

​​CCLOG(​​​​"name = %s"​​​​,HclcData::sharedHD()->getLuaVarOneOfTable(​​​​"Test.lua"​​​​, ​​​​"luaTable"​​​​,​​​​"name"​​​​));​​

​​CCLOG(​​​​"sex = %s"​​​​,HclcData::sharedHD()->getLuaVarOneOfTable(​​​​"Test.lua"​​​​, ​​​​"luaTable"​​​​,​​​​"sex"​​​​));​​

​​CCLOG(​​​​"Table = %s"​​​​,HclcData::sharedHD()->getLuaVarTable(​​​​"Test.lua"​​​​, ​​​​"luaTable"​​​​));​​

​​CCLOG(​​​​"Call Lua Function Back: %s"​​​​,HclcData::sharedHD()->callLuaFunction(​​​​"Test.lua"​​​​, ​​​​"luaLogString"​​​​));​​



​​HclcData::sharedHD()->callCppFunction(​​​​"Test.lua"​​​​);​​

​​HclcData::sharedHD()->callLuaFunction(​​​​"Test.lua"​​​​, ​​​​"call_Cpp"​​​​);​​

 

对应测试的Test.Lua文件:

luaStr  ​​​​=​​​ ​​"I' m Himi"​​



​​luaStr2 ​​​​=​​​ ​​"are you ok!"​​



​​luaTable​​​​=​​​​{age ​​​​=​​​ ​​23​​​​,name​​​​=​​​​"Himi"​​​​,sex​​​​=​​​​"男"​​​​}​​



​​function luaLogString(_logStr,_logNum,_logBool)​​



​​print​​​​(​​​​"Lua 脚本打印从C传来的字符串:"​​​​,_logStr,_logNum,_logBool)​​

​​return​​​ ​​"call lua function OK"​​

​​end​​



​​function call_Cpp(_logStr,_logNum,_logBool)​​

​​num,​​​​str​​​ ​​=​​​ ​​cppFunction(​​​​999​​​​,​​​​"I'm a lua string"​​​​)​​

​​print​​​​(​​​​"从cpp函数中获得两个返回值:"​​​​,num,​​​​str​​​​)​​

​​end​​

 

运行测试结果如下:

Cocos2d: Str = I' m Himi​​       

​​Cocos2d: Str2 are you ok!​​

​​Cocos2d: age = 23​​

​​Cocos2d: name = Himi​​

​​Cocos2d: sex = 男​​

​​Cocos2d: Table = name:Himi age:23 sex:男 ​​

​​Lua 脚本打印从C传来的字符串: Himi 23 ​​​​true​​

​​Cocos2d: Call Lua Function Back: call lua function OK​​

​​Cocos2d: Lua调用cpp函数时传来的两个参数: 999 I'm a lua string​​

​​从cpp函数中获得两个返回值: 321 Himi​​

 

在Himi做这些交互时出现了如下错误:

​​“PANIC: unprotected error in call to Lua API (attempt to index a nil value)​​

如下图:

​​​​

 

最后Himi发现造成此问题的原因有两种:

1. 是你的lua文件位置路径!

      细心的童鞋应该看到,每次我使用 luaL_dofile 函数时传入的都是调用了一个getFileFullPath的函数进行获取文件的完整路径!

在HclcData中包装了一个函数:

​​const char​​​​*​​​​HclcData​​​​:​​​​:​​​​getFileFullPath​​​​(​​​​const char​​​​*​​​​fileName​​​​)​​​​{​​
​​ ​​​​return​​​​CCFileUtils​​​​:​​​​:​​​​sharedFileUtils​​​​(​​​​)​​​​-​​​​>​​​​fullPathForFilename​​​​(​​​​fileName​​​​)​​​​.c_str​​​​(​​​​)​​​​;​​
​​}​​


2. 如果你是cpp调用lua函数,那么你的这个lua函数不能是local的!

    反之,如果你lua调用cpp函数,同理,cpp函数肯定是static的!


   另外,如果你cpp调用lua,等同于重新加载了这个lua文件,是不同的对象!因此你应该建立一个新的lua文件,主要用于交互所用!

     例如你a.lua中有一个tab的成员变量,那么你使用cpp调用lua函数后,这个tab是新的对象!


最后附上HclcData和Test.lua 下载地址:​​http://vdisk.weibo.com/s/y0zws​​

OK,本篇就到这里,有什么问题及时联系Himi!

 

举报

相关推荐

cocos2d-x C++与Lua交互

0 条评论