1. 程式人生 > >lua luna工具庫

lua luna工具庫

全局 解決 .com void href char 開發 The 實現

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,它實際上做的事情是:

  1. 在構造時調用lua_gettop保存棧.
  2. 析構時調用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工具庫