lua luna工具庫
luna工具庫
概述
luna庫提供了幾個lua開發的常見輔助功能:
- lua/c++綁定
- lua序列化與反序列化
- 變長整數編碼,用於lua序列化,當然也可以方便的用於其他場合
這裏把代碼編譯成了動態庫,由於代碼非常簡單,實際使用時也可以簡單的復制文件到自己的工程.
lua_archiver引用了lz4庫用於數據壓縮(lz4.h+lz4.c).
lua/c++綁定庫(luna.h, luna.cpp)支持Windows, Linux, macOS三平臺,默認的luna.h實現需C++14支持.
如果編譯器不支持C++14,也可以將其替換為C++11版本的luna11.h.
之所以實現這個lua/c++綁定,是出於以下的想法:
- 希望所有事情在c++代碼中就搞定,不希望額外再運行一個什麽轉換處理工具
- 希望能夠方便的導出一般C++函數,而不必寫一大堆lua api調用代碼
- 希望能簡單的處理導出對象的生命期
- 希望能方便的在lua代碼中對導出對象進行擴展,重載等
- 希望使用盡可能簡單,無需對luna庫本身做任何初始化
- 希望執行時無副作用,即沒有全局或靜態的數據,進程中存在多個lua_State時不會相互幹擾
編譯說明
luna同時支持Windows, Linux, MacOS三平臺,編譯器最低必須支持C++11,最好支持C++14,需要安裝cmake.
在編譯之前,應先編譯安裝lua,然後:
cd luna
mkdir build
cd build
cmake ..
make
在macos上,可能會遇到編譯器默認沒有搜索/usr/local/include
目錄而導致找不到lua.hpp的問題.
解決辦法是執行xcode-select --install
,該命令不僅僅是安裝命令行工具,還會設置相應的參數.
除了編譯為動態庫,也可以簡單的將源碼復制到工程中使用,如果打算這樣用,那麽註意:
luna.h並不直接存在,而是cmake根據編譯環境選取的luna14.h或者luna11.h,你可以根據自己的環境選擇即可.
C++導出全局函數
當函數的參數以及返回值是***基本類型***或者是***已導出類***時,可以直接用lua_register_function
導出:
int func_a(const char* a, int b);
int func_b(my_export_class_t* a, int b);
some_export_class* func_c(float x);
lua_register_function(L, func_a);
lua_register_function(L, func_b);
lua_register_function(L, func_c);
當然,你也可以導出lua標準的C函數.
導出類
首先需要在你得類聲明中插入導出聲明:
class my_class final
{
// ... other code ...
int func_a(const char* a, int b);
int func_b(some_class_t* a, int b);
char m_name[32];
public:
// 插入導出聲明:
DECLARE_LUA_CLASS(my_class);
};
在cpp中增加導出表的實現:
EXPORT_CLASS_BEGIN(my_class)
EXPORT_LUA_FUNCTION(func_a)
EXPORT_LUA_FUNCTION(func_b)
EXPORT_LUA_STRING(m_name)
EXPORT_CLASS_END()
可以用帶_AS
的導出宏指定導出的名字,用帶_R
的宏指定導出為只讀變量. 比如: EXPORT_LUA_STRING_AS(m_name, Name)
關於導出類(對象)的註意點
目前通過靜態斷言作了限制: 只能導出聲明為final的類
這是為了避免無意間在父類和子類做出錯誤的指針轉換 如果需要父類子類同時導出且保證不會出現這種錯誤,可以自行去掉這個斷言
關於C++導出對象的生存期問題
註意,C++對象一旦被push進入lua,其生命期就交給lua的gc管理了,C++層面不能隨便刪除. 這些lua托管的對象在gc時,會默認調用delete,如果不希望調用delete,可以在對象中實現自定義gc方法: void __gc()
. 另外,由於lua的gc回收資源總是具有一定延遲的,所以如果C++對象持有較多的資源的話,最好顯示釋放資源或者在lua層面顯示的調用gc.
對於已經push到lua的對象,如果想從C++解除引用,可以調用lua_detach(L, object)
;
class my_class final
{
// ...
public:
DECLARE_LUA_CLASS(my_class);
void __gc()
{
// lua gc時,如果存在本函數,那麽會調用本函數取代默認的delete
}
};
lua中訪問導出對象
lua代碼中直接訪問導出對象的成員/方法即可.
local obj = get_obj_from_cpp();
obj.func("abc", 123);
obj.name = "new name";
另外,C++對象導出到lua中是通過一個table來實現的,可以稱之為影子對象. 不但可以在lua中訪問C++對象成員,還可以有下面這些常見用法:
- 重載(覆蓋)對象上的C++導出方法.
- 在影子對象上增加額外的成員變量和方法.
- 在C++中調用lua中為對象增加的方法,參見
lua_call_object_function
.
C++中調用lua函數
目前提供了兩種支持:
- C++調用全局函數
- C++調用全局table中的函數
- C++調用導出對象上附加的函數.
下面以調用全局table中的函數為例:
function s2s.some_func(a, b)
return a + b, a - b, "tom";
end
上面的lua函數返回三個值,那麽,可以在C++中這樣調用:
lua_guard g(L); //用它來做棧保護
int x, y;
const char* name = nullptr;
// 小心,如果用char*做字符串返回值的話,確保name變量不要在lua_guard g的作用域之外使用
lua_call_table_function(L, nullptr, "s2s", "some_func", std::tie(x, y, name), 11, 2);
註意上面的lua_guard,它實際上做的事情是:
- 在構造時調用
lua_gettop
保存棧. - 析構時調用
lua_settop
恢復棧.
// 註意這裏由於需要傳入abc三個參數,所以需要寫一個std::tie()表示沒有返回參數
lua_call_table_function(L, nullptr, "s2s", "some_func", std::tie(), a, b, c);
如果沒有參數,也沒有返回值,那就是最簡單的寫法了:
lua_call_table_function(L, nullptr, "s2s", "some_func");
lua luna工具庫