利用luabind将Lua嵌入到C++项目中(一)

来源:互联网 发布:粉色ak47软件 编辑:程序博客网 时间:2024/06/05 04:25

                 开篇——环境假设

                                                                                                                                         By : HengStar(欣恒)


原文地址:http://blog.csdn.net/gongxinheng/archive/2009/07/25/4380526.aspx

(虽然本人文笔不好,水平也不高,不过下定决心要写一篇文章和大家分享一些小小的成就也不容易~所以望各位同行人士想要转载的请注明出处:谢谢合作^_^顺便打个小广告,嘿嘿,有兴趣想一起交流的可以加QQ: 31249966,觉得我文章有问题的也欢迎指出..QQ私聊吧-.- QQ:9292492 同时也感谢大虾linkerlin翻译的luabind文档给了英文水平不高的我很多帮助^^ 文档在这:http://blog.csdn.net/linkerlin/archive/2008/04/06/2254725.aspx
开场白(PS:如果你觉得啰唆可以跳过它-.-#):本人是网游开发人员,初次接触Lua脚本,由于原项目中的脚本技术比较落后,开发人员使用起来诸多不便,所以想引入一套好的脚本机制,在了解到了Lua的强大以及在游戏行业的广泛运用后,我毫不犹豫的选择了它。在一段时间的Lua学习下来后发现要在一个大型项目并且是已经上市运营的项目中加入这套东西实属不容易,要考虑的东西太多,毕竟C++Lua语言上的差异还是比较大的,要完全的实现C++Lua之间的方便交互还是要费很大的功夫的,特别是在一些面向对象的特性诸如多态、继承等大范围在项目中使用的,而且给自己的时间并不多,好在从同事那了解到有luabind这号角色的存在,而且也是开源的,我马上兴起准备开始一起研究,由于旧代码中大部分都是无法移植的代码,在嵌入工作中遇到了一系列的麻烦-.-随后慢慢道来,不过经过几天的努力钻研,已经成功投入到项目中开始使用了,不过新版本暂时还未发布废话先不说了,切入正题)

一.   Luabind加入到工程
必备:Luabind源码、Lua SDK及源码、Boost 源码
VS
中新建静态链接库工程,将Luabind源码路径下的src目录中所有文件添加到工程,编译为Luabind静态链接库(需要包含Luabind源码解压包后的根目录、LuaInclude文件目录、Boost 源码根目录,注意工程选项,因为当时我们的项目工程选项的结构对齐方式是采用“一字节对齐”,而编译Luabind静态链接库时用的是默认字节对齐方式,所以总发生莫名其妙的错误),将编译完的静态链接库加载到C++项目中;

二.   Lua加入到工程
方法同上

三.   创建LuaManager
需要包含的头文件:<lua.hpp> <luabind/luabind.hpp>
创建一个专门用于管理与Lua脚本引擎相关的管理器类,可以采用Singleton(单件)       技术(详见《设计模式》)
class LuaManager
{
  lua_State* m_pL; // Lua
状态,初始为NULL
 
成员方法见下

};

大致需要实现如下方法(仅供参考,根据不同的工程需求可以用更适合自己工程的方式实现):
1. init
(初始化):用于初始化Lua相关内容,代码示例:
  bool LuaManager::init()
  {
    if( !m_pL )  //
确保只会初始化一次
    {
      m_pL = luaL_newstate(); // 创建Lua状态
      if( !
m_pL ) return false;
      luaL_openlibs( L ); //
为该Lua状态打开所有Lua
      luabind::open( L ); //
为该Lua状态打开luabind
    }
    return registerAll(); //
见下
  }
2. registerAll(注册所有需要的内容):
  
学过Lua的朋友一定知道C/C++中的函数等要在Lua中调用需要注册,如果直接用Lua提供的方法注册会很麻烦,随着工程的规模的扩大还会提升管理难度,这就是我选择使用luabind的原因,它提供十分方便的机制封装了C/C++类的绑定,已经函数的注册,甚至C/C++中声明的全局变量也能直接注册到Lua中使用,(由于我们的项目代码量比较庞大,为了方便维护,我将此方法分为了两个,一个专门用来注册C++类的registerClasses和用于注册全局函数的registerFunctions,还会考虑加入一个注册全局变量的registerVars),代码示例:
 
bool LuaManager::registerAll()
  {

  //
以下是全局函数的注册
  
module( L )
       [
          def( "myfunc", &myfunc ),
          def( "specialfunc", &Specialfunc )
       ]

  //
以下是类的注册
  
module( L )
       [
           class_<testclass>("testclass") //
基类
           .def(constructor<const std::string&>()) //
构造函数
           .def("print_string", &testclass::print_string) //
虚成员函数
           ,
          //
子类,注意基类也要在模板参数中注明
          class_<myclass, testclass>("myclass")
          .def(constructor<const std::string&, int>()) //
构造函数
          .def("print_string", &myclass::print_string) //
成员函数
        ]
     return true; //
目前还没研究注册失败的情况,暂时先假设始终会成功吧
   }

  
更多细节请参见luabind文档
   http://blog.csdn.net/linkerlin/archive/2008/04/06/2254725.aspx

3. loadScript(加载脚本):用于加载写好的Lua脚本文件(建议用编译后的二进制Luac脚本文件,效率比较高),代码示例:
  bool LuaManager:: loadScript ( const string& fname )
  {
    if( luaL_loadfile(L, fname.c_str()) ) //
如果需要马上执行Lua脚本可以用luaL_dofile
    {
      cerr << lua_tostring( L, -1 ) << endl; //
输出错误信息
      return false;
    }
    return true;
  }

四.   初始化LuaManager
如此简单的工作就不多说了直接调用init并且用loadScript加载相应的脚本文件即可


小结:
本篇的内容比较简单,快速概括了luabind嵌入到C++工程的环境假设问题,很多信息能在文档里面找到,我这里也就不想再多说,如果有不明白的可以找我,一定竭力为大家解答^^

这里可以展现Lua的一个强大之处也就是支持动态加载脚本的功能,我目前的设想是准备把加载Lua脚本的接口提供给一个游戏中的GM指令,以后有脚本的更新发布,只需要简单的用GM指令重新加载这些脚本即可以完成服务器的脚本更新,而不需要重启服务器甚至于再编译一次服务器(目前项目中旧的脚本逻辑一直是和服务器代码相关联的,所以每次改完脚本都需要编译服务器,非常烦人

 

 

 

 

关于下一篇,我会提到一些自己在项目中遇到的C++语言特性造成和Lua的一些交互困难以及自己的解决方案和根据luabind源码了解到的一些简单的交互机制,敬请期待^^

                                                                                                                                                                      2009/7/25

原创粉丝点击