Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7193418
  • 博文数量: 510
  • 博客积分: 12019
  • 博客等级: 上将
  • 技术积分: 6836
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-01 16:46
文章分类

全部博文(510)

文章存档

2022年(2)

2021年(6)

2020年(59)

2019年(4)

2018年(10)

2017年(5)

2016年(2)

2015年(4)

2014年(4)

2013年(16)

2012年(47)

2011年(65)

2010年(46)

2009年(34)

2008年(52)

2007年(52)

2006年(80)

2005年(22)

分类: C/C++

2009-09-03 12:00:44

例六,使用C++包装类

    尽管用Lua的C API已经可以方便地写出与Lua交互的程序了,不过对于用惯C++的人来说还是更愿意用C++的方式来解决问题。于是开源社区就出现了不少Lua C API的C++的wrap,比如:LuaBind,LuaPlus,toLua
    这里介绍的是LuaBind库,
    它在Windows下貌似只能支持MSVC和ICC,好在Lua可以支持动态库的载入,所以用VC+LuaBind写Lua库,再用C++Builder调用也是个好主意。
    在VC使用LuaBind的方法是把LuaBind的src目录下的cpp文件加入工程(当然也可以先编译成静态库),加入Lua库,设置LuaBind,Lua和Boost的头文件路径。

头文件:

  1. //Lua头文件
  2. extern "C"
  3. {
  4. #include 
  5. #include 
  6. #include 
  7. }
  8. //LuaBind头文件
  9. #include  


在C++中调用Lua函数

    调用Lua函数那是最简单不过的事情了,用LuaBind的call_function()模板函数就可以了:
  1. int main(
  2.   // 建立新的Lua环境
  3.   lua_State *myLuaState = luaL_newstate();
  4.  
  5.   // 让LuaBind“认识”这个Lua环境
  6.   luabind::open(myLuaState);
  7.  
  8.   // 定义一个叫add的Lua函数
  9.   luaL_dostring(
  10.     myLuaState,
  11.     "function add(first, second) "
  12.     "  return first + second "
  13.     "end "
  14.   );
  15.    
  16.   //调用add函数
  17.   cout << "Result: "
  18.        << luabind::call_function<int>(myLuaState, "add", 2, 3)
  19.        << endl;
  20.  
  21.   lua_close(myLuaState);
  22. }

在本例中我们先使用Lua C API产生一个Lua线程环境,然后调用luabind::open()让LuaBind关联这个线程环境,在使用LuaBind之前这步是必须做的,它要在Lua环境中注册一些LuaBind专用的数据。
在执行完Lua代码之后,我们使用luabind::call_function调用了Lua里的add函数,返回值是int

在Lua代码中调用C++函数

    从前面的文章里我们知道在Lua调用C函数需要经常操作栈,而LuaBind帮我们做了这些工作,下面的例子把print_hello函数送给Lua脚本调用:
  1. void print_hello(int number) {
  2.   cout << "hello world " << number << endl;
  3. }
  4.  
  5. int main(
  6.   // 建立新的Lua环境
  7.   lua_State *myLuaState = lua_open();
  8.  
  9.   // 让LuaBind“认识”这个Lua环境
  10.   luabind::open(myLuaState);
  11.  
  12.   // 添加print_hello函数到Lua环境中
  13.   luabind::module(myLuaState) [
  14.     luabind::def("print_hello", print_hello)
  15.   ];
  16.  
  17.   // 现在Lua中可以调用print_hello了
  18.   luaL_dostring(
  19.     myLuaState,
  20.     "print_hello(123) "
  21.   );
  22.  
  23.   lua_close(myLuaState);
  24. }

    向Lua环境加入函数或其它东东的方法是:
  luabind::module(lua_State* L, char const* name = 0) [
...
];
    其中module函数中的第二个指定要加入的东东所处的名空间(其实就是table),如果为0,则处于全局域之下。
    在中括号里的luabind::defprint_hello函数提供给Lua环境,第一个参数是Lua中使用的函数名。
    如果要定义多个函数,可以使用逗号分隔。

在Lua代码中使用C++类

    如果我们直接使用Lua C API向Lua脚本注册一个C++类,一般是使用userdata+metatable的方法,就象我们在例五中做的一样。这样做尽管难度不大,却非常繁琐而且不方便维护。
    使用LuaBind我们就可以更方便地向Lua脚本注册C++类了,例:
  1. class NumberPrinter {
  2.   public:
  3.     NumberPrinter(int number) :
  4.       m_number(number) {}
  5.  
  6.     void print() {
  7.       cout << m_number << endl;
  8.     }
  9.  
  10.   private:
  11.     int m_number;
  12. };
  13.  
  14. int main() {
  15.   lua_State *myLuaState = lua_open();
  16.   luabind::open(myLuaState);
  17.  
  18.   // 使用LuaBind导出NumberPrinter类
  19.   luabind::module(myLuaState) [
  20.     luabind::class_("NumberPrinter")
  21.       .def(luabind::constructor<int>())
  22.       .def("print", &NumberPrinter::print)
  23.   ];
  24.  
  25.   // 现在Lua中可以使用NumberPinter类了
  26.   luaL_dostring(
  27.     myLuaState,
  28.     "Print2000 = NumberPrinter(2000) "
  29.     "Print2000:print() "
  30.   );
  31.  
  32.   lua_close(myLuaState);
  33. }

为了注册一个类,LuaBind提供了class_类。它有一个重载过的成员函数 def() 。这个函数被用来注册类的成员函数、操作符、构造器、枚举和属性。
它将返回this指针,这样我们就可以方便地直接注册更多的成员。

属性

LuaBind 也可以导出类成员变量:
  1. template<typename T>
  2. struct Point {
  3.   Point(T X, T Y) :
  4.     X(X), Y(Y) {}
  5.  
  6.   T X, Y;
  7. };
  8.  
  9. template<typename T>
  10. struct Box {
  11.   Box(Point UpperLeft, Point LowerRight) :
  12.     UpperLeft(UpperLeft), LowerRight(LowerRight) {}
  13.  
  14.   Point UpperLeft, LowerRight;
  15. };
  16.  
  17. int main() {
  18.   lua_State *myLuaState = lua_open();
  19.   luabind::open(myLuaState);
  20.  
  21.   // 使用LuaBind导出Point类和Box
  22.   luabind::module(myLuaState) [
  23.     luabind::class_float> >("Point")
  24.       .def(luabind::constructor<floatfloat>())
  25.       .def_readwrite("X", &Point<float>::X)
  26.       .def_readwrite("Y", &Point<float>::Y),
  27.  
  28.     luabind::class_float> >("Box")
  29.       .def(luabind::constructorfloat>, Point<float> >())
  30.       .def_readwrite("UpperLeft", &Box<float>::UpperLeft)
  31.       .def_readwrite("LowerRight", &Box<float>::LowerRight)
  32.   ];
  33.  
  34.   // 现在Lua中可以使用为些类了
  35.   luaL_dostring(
  36.     myLuaState,
  37.     "MyBox = Box(Point(10, 20), Point(30, 40)) "
  38.     "MyBox.UpperLeft.X = MyBox.LowerRight.Y "
  39.   );
  40.  
  41.   lua_close(myLuaState);
  42. }

本例中使用def_readwrite定义类成员,我们也可以用def_readonly把类成员定义成只读。

LuaBind还可以把C++类导出成支持getter和setter的属性的Lua类:
  1. struct ResourceManager {
  2.   ResourceManager() :
  3.     m_ResourceCount(0) {}
  4.  
  5.   void loadResource(const string &sFilename) {
  6.     ++m_ResourceCount;
  7.   }
  8.   size_t getResourceCount() const {
  9.     return m_ResourceCount;
  10.   }
  11.  
  12.   size_t m_ResourceCount;
  13. };
  14.  
  15. int main() {
  16.   lua_State *myLuaState = lua_open();
  17.   luabind::open(myLuaState);
  18.  
  19.   // 导出类,在Lua中调用ResourceCount属性会调用C++中的ResourceManager::getResourceCount
  20.   // 属性定义有点象C++Builder里的__property定义,呵呵
  21.   luabind::module(myLuaState) [
  22.     luabind::class_("ResourceManager")
  23.       .def("loadResource", &ResourceManager::loadResource)
  24.       .property("ResourceCount", &ResourceManager::getResourceCount)
  25.   ];
  26.  
  27.   try {
  28.     ResourceManager MyResourceManager;
  29.  
  30.     // 把MyResourceManager定义成Lua的全局变量
  31.     luabind::globals(myLuaState)["MyResourceManager"] = &MyResourceManager;
  32.  
  33.     // 调用
  34.     luaL_dostring(
  35.       myLuaState,
  36.       "MyResourceManager:loadResource(\"abc.res\") "
  37.       "MyResourceManager:loadResource(\"xyz.res\") "
  38.       " "
  39.       "ResourceCount = MyResourceManager.ResourceCount "
  40.     );
  41.  
  42.     // 读出全局变量
  43.     size_t ResourceCount = luabind::object_cast<size_t>(
  44.       luabind::globals(myLuaState)["ResourceCount"]
  45.     );
  46.     cout << ResourceCount << endl;
  47.   }
  48.   catch(const std::exception &TheError) {
  49.     cerr << TheError.what() << endl;
  50.   }
  51.  
  52.   lua_close(myLuaState);
  53. }
阅读(7040) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~