例六,使用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的头文件路径。
头文件:
- //Lua头文件
- extern "C"
- {
- #include
- #include
- #include
- }
- //LuaBind头文件
- #include
在C++中调用Lua函数
调用Lua函数那是最简单不过的事情了,用LuaBind的call_function()模板函数就可以了:- int main(
- // 建立新的Lua环境
- lua_State *myLuaState = luaL_newstate();
- // 让LuaBind“认识”这个Lua环境
- luabind::open(myLuaState);
- // 定义一个叫add的Lua函数
- luaL_dostring(
- myLuaState,
- "function add(first, second)"
- " return first + second"
- "end"
- );
- //调用add函数
- cout <"Result: "
- >(myLuaState, "add", 2, 3)
- <
- lua_close(myLuaState);
- }
在本例中我们先使用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脚本调用:- void print_hello(int number) {
- cout <"hello world " << number <
- }
- int main(
- // 建立新的Lua环境
- lua_State *myLuaState = lua_open();
- // 让LuaBind“认识”这个Lua环境
- luabind::open(myLuaState);
- // 添加print_hello函数到Lua环境中
- luabind::module(myLuaState) [
- luabind::def("print_hello", print_hello)
- ];
- // 现在Lua中可以调用print_hello了
- luaL_dostring(
- myLuaState,
- "print_hello(123)"
- );
- lua_close(myLuaState);
- }
向Lua环境加入函数或其它东东的方法是:
luabind::module(lua_State* L, char const* name = 0) [其中module函数中的第二个指定要加入的东东所处的名空间(其实就是table),如果为0,则处于全局域之下。
...
];
在中括号里的luabind::def把print_hello函数提供给Lua环境,第一个参数是Lua中使用的函数名。
如果要定义多个函数,可以使用逗号分隔。
在Lua代码中使用C++类
如果我们直接使用Lua C API向Lua脚本注册一个C++类,一般是使用userdata+metatable的方法,就象我们在例五中做的一样。这样做尽管难度不大,却非常繁琐而且不方便维护。使用LuaBind我们就可以更方便地向Lua脚本注册C++类了,例:
- class NumberPrinter {
- public:
- NumberPrinter(int number) :
- m_number(number) {}
- void print() {
- cout << m_number <
- }
- private:
- int m_number;
- };
- int main() {
- lua_State *myLuaState = lua_open();
- luabind::open(myLuaState);
- // 使用LuaBind导出NumberPrinter类
- luabind::module(myLuaState) [
- luabind::class_("NumberPrinter")
- .def(luabind::constructor<int>())
- .def("print", &NumberPrinter::print)
- ];
- // 现在Lua中可以使用NumberPinter类了
- luaL_dostring(
- myLuaState,
- "Print2000 = NumberPrinter(2000)"
- "Print2000:print()"
- );
- lua_close(myLuaState);
- }
为了注册一个类,LuaBind提供了class_类。它有一个重载过的成员函数 def() 。这个函数被用来注册类的成员函数、操作符、构造器、枚举和属性。
它将返回this指针,这样我们就可以方便地直接注册更多的成员。
属性
LuaBind 也可以导出类成员变量:- template<typename T>
- struct Point {
- Point(T X, T Y) :
- X(X), Y(Y) {}
- T X, Y;
- };
- template<typename T>
- struct Box {
- Box(Point UpperLeft, Point LowerRight) :
- UpperLeft(UpperLeft), LowerRight(LowerRight) {}
- Point UpperLeft, LowerRight;
- };
- int main() {
- lua_State *myLuaState = lua_open();
- luabind::open(myLuaState);
- // 使用LuaBind导出Point类和Box类
- luabind::module(myLuaState) [
- luabind::class_float> >("Point")
- .def(luabind::constructor<float, float>())
- .def_readwrite("X", &Point<float>::X)
- .def_readwrite("Y", &Point<float>::Y),
- luabind::class_float> >("Box")
- .def(luabind::constructorfloat>, Point<float> >())
- .def_readwrite("UpperLeft", &Box<float>::UpperLeft)
- .def_readwrite("LowerRight", &Box<float>::LowerRight)
- ];
- // 现在Lua中可以使用为些类了
- luaL_dostring(
- myLuaState,
- "MyBox = Box(Point(10, 20), Point(30, 40))"
- "MyBox.UpperLeft.X = MyBox.LowerRight.Y"
- );
- lua_close(myLuaState);
- }
本例中使用def_readwrite定义类成员,我们也可以用def_readonly把类成员定义成只读。
LuaBind还可以把C++类导出成支持getter和setter的属性的Lua类:
- struct ResourceManager {
- ResourceManager() :
- m_ResourceCount(0) {}
- void loadResource(const string &sFilename) {
- ++m_ResourceCount;
- }
- size_t getResourceCount() const {
- return m_ResourceCount;
- }
- size_t m_ResourceCount;
- };
- int main() {
- lua_State *myLuaState = lua_open();
- luabind::open(myLuaState);
- // 导出类,在Lua中调用ResourceCount属性会调用C++中的ResourceManager::getResourceCount
- // 属性定义有点象C++Builder里的__property定义,呵呵
- luabind::module(myLuaState) [
- luabind::class_("ResourceManager")
- .def("loadResource", &ResourceManager::loadResource)
- .property("ResourceCount", &ResourceManager::getResourceCount)
- ];
- try {
- ResourceManager MyResourceManager;
- // 把MyResourceManager定义成Lua的全局变量
- luabind::globals(myLuaState)["MyResourceManager"] = &MyResourceManager;
- // 调用
- luaL_dostring(
- myLuaState,
- "MyResourceManager:loadResource(\"abc.res\")"
- "MyResourceManager:loadResource(\"xyz.res\")"
- ""
- "ResourceCount = MyResourceManager.ResourceCount"
- );
- // 读出全局变量
- size_t ResourceCount = luabind::object_cast<size_t>(
- luabind::globals(myLuaState)["ResourceCount"]
- );
- cout << ResourceCount <
- }
- catch(const std::exception &TheError) {
- cerr << TheError.what() <
- }
- lua_close(myLuaState);
- }