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加价一倍】

相关文章
相关标签/搜索