luabind 0.9.1版本尝试
http://www.rasterbar.com/products/luabindlinux
1. 编译luabind 0.9.1 linux版本
编译luabind须要bjam binary。
直接copy boost/1.37.0目录中编译好的bjam binary到~/bin目录,而后在luabin根目录中运行bjam。
编译以前须要设置环境变量BOOST_ROOT=~/mylibs/boost/1.37.0/ 和LUA_PATH=~/mylibs/lua/5.1/ios
2. 尝试第一个example,就发现了本身版本的lua5.1 binary不支持loadlib函数shell
print(loadlib())
解决办法是在lua src/luaconf.h文件中,将LUA_COMPAT_LOADLIB激活,而后从新编译lua binary函数
3. 编写C++对LUA的扩展库:
解决2以后,编写以下的C++代码(helloworld.cc) :this
#include <iostream> #include <luabind/luabind.hpp> void greet() { std::cout << "hello world - my luabind try\n"; } extern "C" int init(lua_State* L) { using namespace luabind; open(L); module(L) [ def("greet", &greet) ]; return 0; }
而后gcc编译:lua
g++ -o helloworld.so helloworld.cc -fPIC -shared -I/home/zeli/mylibs/boost/1.37.0/include -I/home/zeli/mylibs/luabind-0.9.1/ -I/home/zeli/mylibs/lua/5.1/include -L/home/zeli/mylibs/luabind-0.9.1/lib -L/home/zeli/mylibs/lua/5.1/lib -lluabind -llua
你能够将后面一坨编译选项让一个shell脚原本生成(gcccmd.sh + x):spa
echo "-fPIC -shared -I/home/zeli/mylibs/boost/1.37.0/include -I/home/zeli/mylibs/luabind-0.9.1/ -I/home/zeli/mylibs/lua/5.1/include -L/home/zeli/mylibs/luabind-0.9.1/lib -L/home/zeli/mylibs/lua/5.1/lib -lluabind -llua"
如此一来,你能够少敲不少字符:3d
g++ -o helloworld.so helloworld.cc `./gcccmd.sh`
在当前目录下会有一个helloworld.so文件产生。进一步你能够用ldd/nm来看下helloworld.so文件包含了些什么。code
4. 在LUA中调用C++的代码:
若是你在luabind目录中直接运行lua binary,而后loadlib将会发现以下错误对象
[zeli@p03bc luabind]$ lua Lua 5.1 Copyright (C) 1994-2006 Lua.org, PUC-Rio > loadlib('helloworld.so', 'init')() stdin:1: attempt to call a nil value stack traceback: stdin:1: in main chunk [C]: ? >
这是由于lua binary放在~/bin目录下,而helloworld.so不跟它在同一目录。loadlib函数返回nil.
直接描述so文件的全路径能够解决这个问题:
>loadlib('/home/zeli/code/luabind/helloworld.so', 'init')() > greet() hello world - my luabind try >
init是定义在so中的函数,符合lua的C-API规范。
在loadlib以后须要直接运行这个函数,才能将里面借助luabind的函数/类注册到lua vm环境中。
完成以后,lua vm中便有了greet C函数。咱们能够在lua环境中直接运行。
这是lua => C/C++的经典写法:
经过C/C++语言编写的外部库的方法来扩展lua的功能,从而让lua 代码能够调用到C/C++的函数。
5. 对overloaded functions的支持
若是你有2个overloaded自由函数,须要luabind帮忙注册到lua vm环境中,那么你须要显式地告诉luabind那个绑定函数对应的函数签名
<!-- lang: cpp --> int f(const char*) void f(int)
luabind注册代码须要这样下:
<!-- lang: cpp --> module(L) [ def("f", (int(*)(const char*)) &f), def("f", (void(*)(int)) &f) ];
6. 对class成员函数包括构造函数的支持
<!-- lang: cpp --> class testclass { public: testclass(const std::string& s): m_string(s) { } void print_string() { std::cout << m_string << "\n"; } private: std::string m_string; };
module(L) [ class_<testclass>("testclass") .def(constructor<const std::string&>()) // 这是构造函数的注册 .def("print_string", &testclass::print_string) // 这是成员函数的注册 ];
借用LUA的语言糖: a::dotask(...) <==> a.dotask(a, ...)
luabind支持将类成员函数以自由函数的方式进行注册.
若是类成员函数有重载的状况,你仍然须要向重载的自由函数同样,描述函数的签名
<!-- lang: cpp --> struct A { void f(int a) { std::cout << a << "\n"; } void f(int a, int b) { std::cout << a << ", " << b << "\n"; } }; module(L) [ class_<A>("A") .def(constructor<>()) .def("plus", &plus) .def("f", (void (A::*)(int))&A::f) .def("f", (void (A::*)(int, int))&A::f) ];
须要注意的是,注册到LUA VM中的函数,能够避开C++的访问限制,譬如说你能够注册一个non-public域的成员函数.
7. 数据成员注册
对于public数据成员,若是你想让它public给LUA vm,那么,你能够这样作:
<!-- lang: cpp --> struct A { int a; }; module(L) [ class_<A>("A") .def_readwrite("a", &A::a) // &A::a 是a在class A中偏移量 ];
若是要求数据只能读,那么得这样注册
<!-- lang: cpp --> module(L) [ class_<A>("A") .def_readonly("a", &A::a); ];
若是数据不是C++基本数据类型,那么gett function将会返回一个引用,以方便LUA code用点操做符来操做。
若是你已经对一个数据写了get/set函数,那么能够针对这个变量操做,注册这2个函数:
<!-- lang: cpp --> struct A { int a; int geta() const { return a; } void seta(int x) { a = x; } }; module(L) [ class_<A>("A") .property("a", &A::geta, &A::seta) ];
对于那个注册的函数或变量的名字,不必跟C++的同样,你能够取另一个名字,譬如.property("data", &A::geta, &A::seta)
须要说明的是geta函数最好是const函数。若是不是好像也没有问题,可是官方文档说会存在问题。
8. enum类型数据的注册
若是你在一个类中定义了一些enum类型,你也能够把它们注册到LUA中。
<!-- lang: cpp --> struct A { enum { enum1 = 3, enum2 = 4, enum3 = 20 }; }; module(L) [ class_<A>("A") .enum_("constants") [ value("enum1", 3) value("enum2"), 4) value("enum3"), 6) ] ]
这样的话,在LUA中能够这样操做那3个enum类型数据
>= A.enum1 3 >= A.enum2 4
经过实验发现,在注册的时候,能够将那3个enum对应的值改变以后注册到LUA中,譬如
<!-- lang: cpp --> class_<A>("A") .enum_("constants") [ value("enum1", 300) ... ]
这样A.enum1获得就是300,而不是C++定义的3。同时还发现,LUA中是能够修改A.enum1的值的。
不过我以为LUA这样操做应该不是很好的方式。
9. 操做符的注册
在注册类的操做符函数以前,须要include头文件<luabind/operator.hpp>
<!-- lang: cpp --> struct A { A& operator +(int) { ... } } module(L) [ class_<A>("A") .def(self + int()) ];
若是其它自定义类的参数不是很容易实例化,须要借用other<>模板类。譬如: other<const std::string&>()
若是操做符是const类型的,那么你须要用const_self变量。
若是是A& operator(int)操做符,你能够这样注册.def(self(int()))
可是若是没有任何参数的operator(),该如何进行注册呢?
借用LUA print时隐式调用的__tostring,咱们能够将一个类注册luabind::tostring函数,从而直接print这个类。
要作的事情值须要提供operator <<便可。
<!-- lang: cpp --> friend std::ostream& operator <<(std::ostream&, A&); module(L) [ class_<A>("A") .def(tostring(self)) ];
10. 静态数据的注册
静态数据的注册包括静态数据成员和静态函数。对于静态函数的注册,须要借助一个特别的语法.scope
.
<!-- lang: cpp --> class A { public: static int m_sa; static void print() { ... } }; module(L) [ class_<A>("A") .def(constructor<>()) .scope [ // def_readwrite("a", &A::m_sa), // ERROR: cannot compile def("print", &A::print) ] ];
上面代码中,我将静态数据的注册划掉了,是由于当前luabind不支持(编译错误)。
有2种解决方案:
针对每个静态数据,用静态函数进行封装;彷佛有点麻烦?!
借用一篇博客文章的方法(http://www.codeproject.com/Articles/22638/Using-Lua-and-luabind-with-Ogre-3d)
// Some helpful macros for defining constants (sort of) in Lua. Similar to this code: // object g = globals(L); // object table = g["class"]; // table["constant"] = class::constant; #define LUA_CONST_START( class ) { object g = globals(L); object table = g[#class]; #define LUA_CONST( class, name ) table[#name] = class::name #define LUA_CONST_END }
借用luabind提供的global和object组件。由于类的静态变量注册到LUA是类表的一个域,直接在全局表中对于的类表中,添加一个域。
样例以下:
LUA_CONST_START( Vector3 ) LUA_CONST( Vector3, ZERO); LUA_CONST( Vector3, UNIT_X); LUA_CONST( Vector3, UNIT_Y); LUA_CONST( Vector3, UNIT_Z); LUA_CONST( Vector3, NEGATIVE_UNIT_X); LUA_CONST( Vector3, NEGATIVE_UNIT_Y); LUA_CONST( Vector3, NEGATIVE_UNIT_Z); LUA_CONST( Vector3, UNIT_SCALE); LUA_CONST_END;
若是你的类静态变量可写,那么你本身能够定义一套宏,叫作LUA_STATIC_BEGIN/LUA_STATIC_ENTRY/LUA_STATIC_END
从上面的代码中看出,彷佛须要先要注册类到LUA中,才能注册这样的静态变量。
在lua环境中调用用以下的方式: (用dot来操做类表,而不是colon,那时用来操做对象的)。
>= A.a >A.a = 200 >A.print()