LuaTinker的bug和缺陷

LuaTinker的bug和缺陷

LuaTinker是一套還不錯的C++代碼和Lua代碼的綁定庫,做者是韓國人Kwon-il Lee,做者應該是參考了LuaBind後,爲了簡化和避免太重而實現的。其官網在http://gpgstudy.com/gpgiki/LuaTinker ,但惋惜所有是韓文的,而最新的代碼能夠在Git上下載,https://github.com/zupet/LuaTinker 。對比LuaBind,LuaPlus這類庫,其實現很是很是很是很是很是輕,大約只有1000多行,至少給了你一個機會,去了解和改寫部分功能,因此其在國內也有很多羣衆基礎。並且其輕薄的身材也讓剖析一下一個腳本粘合層是如何工做成爲了可能。git

但另一方面LuaTinker的bug數量並不在少數。也有很多同窗曾經零散的提出來過。這兒只是作個總結,另外感謝fergzhang同窗。不少問題都是他幫忙指出的。github

  1. BUG(錯誤)

第一個問題,int64_t 數據的比較是錯誤的,徹底沒有考慮符號位的狀況嘛。函數

static int lt_s64(lua_State *L)
{
    //徹底沒有考慮符號位的狀況,竟然用memcmp
    lua_pushboolean(L, memcmp(lua_topointer(L, 1), lua_topointer(L, 2), sizeof(long long)) < 0);
    return 1;
} 

 

第二個問題,在處理類成員的修改函數(__newindex)時,沒有考慮要處理父類成員的,而LuaTinker是支持繼承的,並且int lua_tinker::meta_get(lua_State *L) (對應__index)也是支持。這應該是做者的一個疏漏。測試

int lua_tinker::meta_set(lua_State *L)
{
    enum_stack(L);
    lua_getmetatable(L, 1);
    lua_pushvalue(L, 2);
    lua_rawget(L, -2);
    enum_stack(L);

    if (lua_isuserdata(L, -1))
    {
        user2type<var_base *>::invoke(L, -1)->set(L);
}
    else if (lua_isnil(L, -1))
{
    //這兒沒有調用invoke_parent(L)處理父類的狀況
        lua_pushvalue(L, 2);
        lua_pushvalue(L, 3);
        lua_rawset(L, -4);
    }
    lua_settop(L, 3);
    return 0;
}

 

第三個bug,使用I64d這種過期的標籤,I64d應該是微軟很老很老很老的一個格式化字符串標籤,並且徹底不具有可移植性。應該改成%lld。lua

static int tostring_s64(lua_State *L)
{
    char temp[64];
    sprintf_s(temp, "%I64d", *(long long*)lua_topointer(L, 1));
    lua_pushstring(L, temp);
    return 1;
}

 

第4個bug,var_base基類的析構函數沒有寫virtualspa

struct var_base
{
    //原來的析構函數沒有寫virtual
    virtual ~var_base() {};
    virtual void get(lua_State *L) = 0;
    virtual void set(lua_State *L) = 0;
};

 

第5個bug,table的3個構造函數中有一個沒有增長引用計數,這個bug在網上不少同窗都指出過。.net

lua_tinker::table::table(lua_State* L, const char* name)
{
    lua_pushstring(L, name);
    lua_gettable(L, LUA_GLOBALSINDEX);

    if(lua_istable(L, -1) == 0)
    {
        lua_pop(L, 1);

        lua_newtable(L);
        lua_pushstring(L, name);
        lua_pushvalue(L, -2);
        lua_settable(L, LUA_GLOBALSINDEX);
    }

    m_obj = new table_obj(L, lua_gettop(L));
        //原來的代碼沒有這段,缺乏增長引用計數處理
        m_obj->inc_ref();
}

, CSDN的ainn_pp也寫過這個問題,參考:http://blog.csdn.net/ainn_pp/article/details/2773855code

  1. 缺陷

LuaTinker也有不少缺陷和不足,協程

第一個不足,各類函數參數的支持個數良莠不齊。LuaTinker的寫的時間應該比較早,沒有C++ 11的模版變參(Variadic Template)的支持,因此只能用寫多個模版函數(類)的方式解決多模板參數問題。但LuaTinker一方面寫的參數個數不多,一方面LuaTinker的個個地方支持的參數個數數量徹底不統一,3個,4個,5個的都有。其實這部分最好用C++ 11的(Variadic Template)重寫。blog

第二個缺陷是個硬傷,LuaTinker對Lua 協程的支持,用了一個很費力,但又不討好的方式。你必須把協程的第一個參數定義爲lua_State *L,並且返回值必須是lua_yield(L, 0)。這樣的限制,大大限制了使用。

//第一個參數必須是(lua_State *L
int TestFunc2(lua_State *L, float a)
{
printf("# TestFunc2(L,%f) is invoke.\n", a);
//返回的地方必須是調用lua_yield
    return lua_yield(L, 0);
}

class TestClass
{
public:

    //類函數也同樣
    int TestFunc2(lua_State *L, float a)
    {
        printf("# TestClass::TestFunc2(L,%f) is invoke.\n", a);
        return lua_yield(L, 0);
    }
};

 

而若是其實在其函數的封裝上加以區分,本身在最後調用lua_yield,這就能夠避免這個麻煩。這個實現實在讓我沒有多大胃口。

第三個地方是,是對引用變量(參數),註冊的問題,其實這個也不是LuaTinker的問題,而是C++模版的問題,C++的自動模版函數參數推導是存在一些潛規則的。其中有一個就是左值變換,在 《CUJ:高效使用標準庫:顯式函數模板參數申明與STL》 一文中有比較清晰的解釋。以下示例:

//下面的寫法是沒法獲得引用的,必須顯式指定參數。
//lua_tinker::set(L, "ref_a", ref_a);

//顯式聲明引用參數
lua_tinker::set<TestA &>(L, "ref_a", ref_a);

 

第4個問題就是,模版處理中,對於cv(const volatile)的去除掉處理也並不理想。Lua內部對於外部的class的註冊是使用函數class_add,也就是用一個名稱關聯一個Lua的meta table,在類的後面的使用中,經過class_name<T>::name函數取得類名,但實際中,不少時候T是帶有const 或者 volatile的修飾符的。包括,LuaTinker在部分處理中去掉了const,但不少地方又忽略問題,編譯器不會認爲classA和const class A是一個東東的。因此結果就是有時候沒法讓你的userdata找到對應的meta table。

lua_tinker::class_add<TestA>(L, "TestA");
lua_tinker::class_con<TestA>(L, lua_tinker::constructor<TestA>);

//用模板函數輔助幫忙實現一個方法,能夠經過class 找到對應的類名稱(註冊到LUA的名稱),
template<typename T>
struct class_name
{
    // global name
    static const char *name(const char *name = NULL)
    {
        static char temp[256] = "";
        if (name)
        {
            strcpy_s(temp, name);
        }
        return temp;
    }
};

 

fergzhang同窗,針對LuaTinker的一些bug修正作了一個版本。同時好像支持了5.2的版本,他放在了https://github.com/zfengzhen/lua_tinker_5.2.git

而我本身針對上面的問題實現了一套LuaTie的庫,沒有時間,有時間整理出來。內部用C++ 11的特性作了一些改寫。

https://github.com/sailzeng/zcelib/blob/master/src/commlib/zcelib/zce_script_lua_tie.cpp

https://github.com/sailzeng/zcelib/blob/master/src/commlib/zcelib/zce_script_lua_tie.h

工程內部是有測試的例子的,但沒有整理出來,看起來比較麻煩。等後面整理出來再寫一篇介紹的文章把。

最後,雖然我在挑刺,但仍是再次表達一下對 LuaTinker做者Kwon-il Lee的感謝,這套代碼幫助我瞭解瞭如何綁定腳本以及瞭解MPL的一些基本知識.並且坦白講LuaBind是和Boost綁定的,能費力從這些代碼中簡化出LuaTinker(閱讀Boost代碼實在談不上愉快),真的是一件很了不得的事情,LuaTinker的做者可能更明白輕對於代碼的好處,Kwon-il Lee在主頁上有一段說明其爲何沒有支持LuaBind所支持的重載,而我也認爲一個優秀的庫始終要是簡單的。

 

【本文做者是雁渡寒潭,本着自由的精神,你能夠在無盈利的狀況完整轉載此文檔,轉載時請附上BLOG連接:http://www.cnblogs.com/fullsail/,不然每字一元,每圖一百不講價。對Baidu文庫和360doc加價一倍】

相關文章
相關標籤/搜索