LuaTinker:清晰简单的lua的封装.及其中的陷阱

LuaTinker:清晰简单的lua的封装.及其中的陷阱

Lua有很多封装.其中的利弊已经有不少介绍.只是提一下luaplus.本来期望值蛮高的.但后来发现文档质量不高.sample编译不过去.不过调试功能做的比较强..
最后选择了LuaTinker.一个韩国人写的.2个文件.5个sample.简单清晰.赞一个.~

首先试验了几个基本功能.都没什么问题.

当时比较疑惑的几个问题是.在lua中可以使用指针么?可以使用c++中的数据结构么?.

lua中的函数可以以指针为参数么?

结论是可以的.不过跟强大的luabind比起来.luatinker没有对导出枚举提供支持.

但其他方面功能并不差.

比如c++中有类A

  1. class A
  2. {
  3. public:
  4. A() {}
  5. void test();
  6. private:
  7. A( const A& ) {}
  8. }

lua中定义

但如果以对象引用为参数的时候发现对象被复制

..

  1. A& ra = a;
  2. lua_tinker::call<void>(L, "lua_func", ra);

这里的 ra没有像引用一样工作 .而是以值传递的方式进入的函数



这就奇怪了..于是开始读代码


代码就不贴了.不过很显然的是.LuaTinker里有写一堆处理引用的偏特化.也就是表明应该是考虑过引用的
.
那问题出在哪了呢
..

LuaTinker
里调用一个lua里的函数的call是这样定义的


  1. template<typename RVal, typename T1>
  2. RVal call(lua_State* L, const char* name, T1 arg)
  3. {
  4. lua_pushcclosure(L, on_error, 0);
  5. int errfunc = lua_gettop(L);
  6. lua_pushstring(L, name);
  7. lua_gettable(L, LUA_GLOBALSINDEX);
  8. if(lua_isfunction(L,-1))
  9. {
  10. push(L, arg);
  11. if(lua_pcall(L, 1, 1, errfunc) != 0)
  12. {
  13. lua_pop(L,1);
  14. }
  15. }
  16. else
  17. {
  18. print_error(L, "lua_tinker::call() attempt to call global `%s' (not a function)", name);
  19. }
  20. lua_remove(L, -2);
  21. return pop<RVal>(L);
  22. }


这时会有编译错误.是说A的拷贝构造函数是私有的.也就是说自动推导出的T

A.
跟调用 Fun( a )效果是一样的
.

显示指定参数


就没有问题

..

那么..在模板参数推导的时候.为什么引用类型会被转换成对象类型呢
?..
翻阅了c++标准以及c++primer.原来在参数自动推导中.会进行类型的隐式转换

在决定模板参数类型前,编译器执行下列隐式类型转换:

左值变换

修饰字转换

派生类到基类的转换

于是..引用类型.被转换为了一个左值.进一步讲.自动推导的模板函数的参数必须是值传递
.
关于转换细节.请参阅以上两本书
.

..看来我又踩到了c++的陷阱.以前一直没注意..写的代码可能也会有类似问题
..

那是否可以做个改动以避免这个陷阱呢.首先想到的是将call函数以一个类模板封装起来.因为类模板是必须要指定类型的

像这样


然后实现中显示指定LuaTinkercall的参数类型.后来发现LuaTinkercall的实现中有个push函数.也有同样的问题

.

push
的实现是


lua2type这里就开始对引用做处理了.而且都是类模板.不会有自动推导上的错误

.
于是把

push(L, arg);
改为

push<T1>(L, arg);
试验.成功


但感觉不是很方便,因为这只完成了一个参数的函数的支持.类似的还要写支持2参数.3参数的
..
LuaTinker
里只写到3参数.不够就自己写吧
..

于是想能不能写个类把参数封装一下.想起lokiTypelist概念很好.于是想能不能定义一个

Valuelist. 这样在使用的时候,只要保证模板参数T2也是一个ValueList类型的,就实现了递归定义可以保存任意数量的数据。同时和Typelist一样,也需要一个结束类型标志。因此定义下面的结构作为结束标志

:
然后函数的参数类型就为ValueList并为ValueList< class T1,null_type >写个特化版本就可以了

.
Typelist
技术在C++ 设计新思维中有详细的介绍.就不多说了
.

但这样定义起ValueList很麻烦..又不能写一个模板函数来帮助解决(因为一旦通过模板函数推导.又会牵扯到引用类型的问题
)

于是又想.能不能写个引用的封装.这样这个封装了引用的对象被复制也无所谓

.
后来想想.如果把RefHolder传入lua并使用.那就要注册这个类的某个特化版本.也就是说.如果想对类A的引用进行封装.那就要注册RefHolderlua..而且为了支持重载操作.肯定要做更多工作

..
后来看了下luabind.如果希望传入引用.是要用Boost.Ref包装一下..那又要做很多处理
..

折腾了一番..最后发现...算了.我在写的时候注意点.代码里再加上些注释跟说明.以此来避免使用引用时出现bug.会简单些..

还有一次在使用lua_tinker::table的时候程序发生了异常.

lua_tinker::table_obj是用来操作lua中的表的类.维护了lua中堆栈的状态.

lua_tinker::table是对table_obj的一个封装.通过table可以对lua中的表进行访问和操作.发生异常后看了一下堆栈.发现是lua_tinker::table中的指向table_obj的指针出现了异常.

于是检查代码.发现table_obj维护了一个引用计数.当此计数为0时则被删除.而这个引用计数在构造函数里初始化的时候为0..当时觉得很奇怪..按照对智能指针的理解.引用计数应该初始化为1吧..后来发现在table构造的时候先new出table_obj对象.然后手动对此对象增加一个引用计数.这样也没什么问题..但仔细一看.table有三个构造函数.其中有一个只构造了table_obj对象但没有增加此对象的引用计数.

  1. lua_tinker::table::table(lua_State* L)
  2. {
  3. lua_newtable(L);
  4. m_obj = new table_obj(L, lua_gettop(L));
  5. m_obj->inc_ref();
  6. }
  7. lua_tinker::table::table(lua_State* L, const char* name)
  8. {
  9. enum_stack(L);
  10. lua_pushstring(L, name);
  11. lua_gettable(L, LUA_GLOBALSINDEX);
  12. if(lua_istable(L, -1) == 0)
  13. {
  14. lua_pop(L, 1);
  15. lua_newtable(L);
  16. lua_pushstring(L, name);
  17. lua_pushvalue(L, -2);
  18. lua_settable(L, LUA_GLOBALSINDEX);
  19. }
  20. m_obj = new table_obj(L, lua_gettop(L));
  21. }
  22. lua_tinker::table::table(lua_State* L, int index)
  23. {
  24. if(index < 0)
  25. {
  26. index = lua_gettop(L) + index + 1;
  27. }
  28. m_obj = new table_obj(L, index);
  29. m_obj->inc_ref();
  30. }

感觉很奇怪..也许是粗心写漏了吧..作者又是韩国人.没法发信交流..

先自己改了..此问题解决..

如果哪位仁兄对此有研究.希望多多交流

.~

参考资料: http://blog.csdn.net/taodm/archive/2003/05/18/15766.aspx

跟踪了一下.发现当arg为类A的对象的引用时.推导出的arg的类型为A.而不是A&..
但如果显示指定参数类型的话就没问题了
..

这么讲可能不好理解..简单的描述一下

一个函数模板.定义如下
  1. template< class T >
  2. void Fun( T t)
  3. {
  4. }

调用函数

免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部