导出 C/C++ API 给 Lua 使用[转]
導出 C/C++ API 給 Lua 使用
cocos2d-x 和 quick-cocos2d-x 的底層代碼都是使用 C++ 語言開發的。為了使用 Lua 腳本語言進行開發,我們利用?tolua++?工具,將大量的 C/C++ API 導出到了 Lua 中。
使用 tolua++ 的基本步驟:
根據實踐,我們建議采用如下的方案來完成整個導出工作。
?
從 C/C++ 源文件創建 .tolua 文件
假設我們的?MyClass.h?頭文件內容如下:
#ifndef __MY_CLASS_H_ #define __MY_CLASS_H_class MyClass { public:static void addTwoNumber(float number1, float number2);private:MyClass(void) {} };#endif // __MY_CLASS_H_為了便于維護,應該將 .h 文件對應的 tolua 命名為 XXX_luabinding.tolua。這樣生成的 luabinding 接口文件名就是 XXX_luabinding.cpp 和 XXX_luabinding.h,不會和已有的 C/C++ 源文件沖突。
創建 MyClass_luabinding.tolua 文件,并修改內容為:
class MyClass : public CCObject { public:static void addTwoNumber(float number1, float number2); };這里可以看到 .tolua 的內容有明顯簡化。詳細的內容修改規則,會在本文后續部分說明。
?
生成 luabinding 接口文件
quick 為了簡化這一步工作,提供了相應工具,我們只需要創建一個腳本文件來調用工具即可。
創建 build_luabinding.sh 文件,內容如下:
#!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "$DIR" OUTPUT_DIR="$DIR" MAKE_LUABINDING="$QUICK_COCOS2DX_ROOT"/bin/compile_luabinding.sh $MAKE_LUABINDING -E MyClass -d "$OUTPUT_DIR" MyClass_luabinding.tolua記得在命令行中 chmod 755 build_luabinding.sh,否則無法執行該腳本。
Windows 版的批處理內容如下:
@echo off set DIR=%~dp0 set OUTPUT_DIR=%DIR% set MAKE_LUABINDING="%QUICK_COCOS2DX_ROOT%\bin\compile_luabinding.bat" pushd cd /d "%DIR%" call %MAKE_LUABINDING% -E MyClass -d %OUTPUT_DIR% MyClass_luabinding.tolua注意:運行腳本前請確保已經下載了 quick-cocos2d-x,并且正確設置了 QUICK_COCOS2DX_ROOT 環境變量。環境配置請參考《入門指引》。
在命令行下運行我們創建的腳本,如果一切順利,我們會看到如下輸出信息:
creating file: MyClass_luabinding.cpp creating file: MyClass_luabinding.h// add to AppDelegate.cpp #include "MyClass_luabinding.h"// add to AppDelegate::applicationDidFinishLaunching() CCLuaStack* stack = CCScriptEngineManager::sharedManager()->getScriptEngine()->getLuaStack(); lua_State* L = stack->getLuaState(); luaopen_MyClass_luabinding(L);?
載入 luabinding 接口文件
打開我們的項目,將 MyClass_luabinding.cpp 和 MyClass_luabinding.h 文件加入工程。然后修改 AppDelegate.cpp 文件:
在 AppDelegate.cpp 頭部區域添加:
#include "MyClass_luabinding.h"在 AppDelegate::applicationDidFinishLaunching() 函數內添加:
luaopen_MyClass_luabinding(L);注意這一行代碼應該添加在其他 luaopen 函數后面,例如:
// register lua engine CCLuaEngine *pEngine = CCLuaEngine::defaultEngine(); CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);CCLuaStack *pStack = pEngine->getLuaStack(); lua_State* L = pStack->getLuaState();// load lua extensions luaopen_lua_extensions(L); // load cocos2dx_extra luabinding luaopen_cocos2dx_extra_luabinding(L);// thrid_party luaopen_third_party_luabinding(L);// CCBReader tolua_extensions_ccb_open(L);// MyClass luaopen_MyClass_luabinding(L);應該將我們的代碼追加到 tolua_extensions_ccb_open() 后面。
經過上述修改后,重新編譯運行項目應該就可以在 Lua 腳本中使用我們導出的 MyClass 對象極其方法了。
?
.tolua 文件內容的修改規則
前面的 MyClass 是一個非常簡單的例子,但我們實際游戲中的 C/C++ API 可能比較復雜。在修改 .tolua 文件內容時,應該仔細閱讀以下內容。
?
刪除所有無需在 Lua 中使用的內容
導出的 API 越多,在 Lua 虛擬機中占用的符號表空間就越多。因此我們第一步要做的就是刪除所有無需在 Lua 中使用的內容。
對于 enum、宏定義,如果需要導出,原文保留即可。但宏定義只能導出數值定義,例如:
#define kCCHTTPRequestMethodGET 0 #define kCCHTTPRequestMethodPOST 1而非數值的宏定義無法導出,以下內容會導出失敗:
#define kMyConstantString "HELLO"刪除所有無法識別的宏,例如 CC_DLL。
刪除 C++ class 中所有非 public 的定義。
刪除 C++ class 中的類成員變量。
刪除 inline 關鍵詞,以及 inline function 的實現,只保留聲明。
?
處理 CCObject 繼承類
CCObject 及其繼承類都具備“引用計數”和“自動釋放”機制。如果你的 C++ 對象是從 CCObject 繼承的,那么必須告訴 tolua++ 做相應處理,否則可能出現內存泄漏等問題。
對于 quick,只需要在 build 腳本中通過 -E CCOBJECTS 參數指定這些 class 的名字即可。
例如前面 MyClass 的示例中,用 -E CCOBJECTS=MyClass 告訴 tolua++ 應該將 MyClass 當作 CCObject 的繼承類進行處理。
如果有多個類,那么每個類名之間用“,”分隔即可,例如:
$MAKE_LUABINDING -E CCOBJECTS=MyClass,MyClass2,MyClass3 -d "$OUTPUT_DIR" MyClass_luabinding.tolua?
展開宏
有些宏是不能直接刪除的,例如 CC_PROPERTY。對于這類宏,需要根據宏定義,將宏展開為聲明。
CC_PROPERTY(float, m_fDuration, Duration)展開為:
float getDuration(); void setDuration(float v);需要如此處理的宏包括:CC_PROPERTY_READONLY, CC_PROPERTY, CC_PROPERTY_PASS_BY_REF, CC_SYNTHESIZE_READONLY, CC_SYNTHESIZE_READONLY_PASS_BY_REF, CC_SYNTHESIZE, CC_SYNTHESIZE_PASS_BY_REF, CC_SYNTHESIZE_RETAIN。
幸運的是這些宏大多只用在 cocos2d-x 基礎代碼里,我們自己的 C++ class 還是不要用這些宏了。
?
處理名字空間
如果使用了名字空間,那么在 .tolua 的頭部應該加入:
$using namespace myname;這里用到的“$”符號,后續內容會原樣放入 luabinding 文件。
?
添加必要的 #include 指令
如果生成的 luabinding 接口文件無法編譯,需要檢查是否是需要 include 相應的頭文件,并添加如下代碼:
$#include "MyClass.h"?
修改函數參數和返回值類型,去除 const 修飾符
一些函數的參數或返回值,使用了 const 修飾符。由于 tolua++ 的限制,并不能很好的處理這類定義,所以我們要從 .tolua 文件中移除 const 修飾符。唯一例外的就是 const char* 不需要修改為 char*。
例如:
CCPoint convertToNodeSpace(const CCPoint& worldPoint);應該修改為:
CCPoint convertToNodeSpace(CCPoint& worldPoint);這樣修改的原因是 tolua++ 把 const CCPoint 和 CCPoint 當做兩個不同的類型來處理。如果不做修改,那么調用函數時會報告參數類型不符。
?
從 C/C++ 函數返回多個值
如果一個函數的所有參數都是引用或指針類型,并且不是 const char*,那么在 luabinding 接口文件中,該函數會返回多個值。
例如:
void getPosition(float* x = 0, float* y = 0);在 Lua 中調用這個函數,會得到兩個返回值:
local x, y = node:getPosition()?
將 Lua 函數傳入 C/C++
quick 里,允許將 Lua 函數傳入 C/C++,只要求 C/C++ 函數中使用 int 做參數類型。但在 .tolua 文件里,則必須使用 LUA_FUNCTION 做參數類型。
例如:
static CCHTTPRequest* createWithUrlLua(int listener,const char* url,int method = kCCHTTPRequestMethodGET);listener 參數用于保存傳入的 Lua 函數,所以 .tolua 文件里要改寫為:
static CCHTTPRequest* createWithUrlLua(LUA_FUNCTION listener,const char* url,int method = kCCHTTPRequestMethodGET);具體用法請參考 lib/cocos2dx_extra/extra/network/CCHTTPRequest 中的 createWithUrlLua() 方法。
?
在 Lua 和 C/C++ 間交換二進制數據
要從 C/C++ 返回二進制數據給 Lua,函數返回值類型必須是 int,而 .tolua 文件中修改返回值為 LUA_STRING。函數中,需要用 CCLuaStack::pushString() 將二進制數據放入 Lua stack。然后返回“需要傳遞給 Lua 的值”的數量。
具體用法請參考 lib/cocos2dx_extra/extra/network/CCHTTPRequest 中的 getResponseDataLua() 方法。
從 Lua 傳遞二進制數據給 C/C++ 很簡單,使用 const char* 參數類型和 int 類型參數分別指定二進制數據的指針和數據長度。
具體用法請參考 lib/cocos2dx_extra/extra/crypto/CCCrypto 中的 decryptXXTEALua() 方法。
?
更多用法
關于利用 tolua++ 的更多用法,建議參考 lib/cocos2dx_extra 中的 CCCrypto、CCNative、CCHTTPRquest 等 class。這些 class 對 Lua 提供了良好的支持,具體用法上也覆蓋了絕大多數 C/C++ 和 Lua 交互的需求。
- END -
轉載于:https://www.cnblogs.com/dwjaissk/p/3476445.html
總結
以上是生活随笔為你收集整理的导出 C/C++ API 给 Lua 使用[转]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 加载驱动的相关函数
- 下一篇: [LeetCode]Add Binary