【COCOS2DX-LUA 腳本開發之十一】C/C++與Lua之間進行數據函數交互以及解決“PANIC: unprotected error in call to Lua API (轉)

本站文章均爲 李華明Himi 原創,轉載務必在明顯處註明:(作者新浪微博: @李華明Himi 

轉載自【黑米GameDev街區】 原文鏈接: http://www.himigame.com/lua1/1343.html 

          ☞ 點擊訂閱 ☜ 本博客最新動態!及時將最新博文通知您!


                 

在使用Cocos2d-x 時候,難免需要C/C++調用Lua函數、數據或Lua調用C/C++函數,那麼本篇講詳細介紹C/C++與Lua之間的數據、函數交互。

首先讓我們來簡單瞭解幾個Lua API函數:

int   luaL_dofile (lua_State *L, const char *filename)  :

加載並運行指定文件,沒有錯誤返回0

void  lua_settop (lua_State *L, int index):

參數允許傳入任何可接受的索引以及 0 。 它將把堆棧的棧頂設爲這個索引。 如果新的棧頂比原來的大,超出部分的新元素將被填爲 nil 。 如果 index 爲 0 ,把棧上所有元素移除。

void   lua_getglobal (lua_State *L, const char *name):

把全局變量 name 裏的值壓入堆棧。

void   lua_pop (lua_State *L, int n):

從堆棧中彈出 n 個元素。相當於清除!

void   lua_pushstring (lua_State *L, const char *s):

把指針 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//
//  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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//
//  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++測試代碼:

1
2
3
4
5
6
7
8
9
10
11
12
#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文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

 

運行測試結果如下:

1
2
3
4
5
6
7
8
9
10
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做這些交互時出現瞭如下錯誤:

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

如下圖:

QQ20130417-1

 

最後Himi發現造成此問題的原因有兩種:

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

      細心的童鞋應該看到,每次我使用 luaL_dofile 函數時傳入的都是調用了一個getFileFullPath的函數進行獲取文件的完整路徑!

在HclcData中包裝了一個函數:

1
2
3
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!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章