luabind 0.9.1版本尝试
http://www.rasterbar.com/products/luabind
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/
2. 尝试第一个example,就发现了自己版本的lua5.1 binary不支持loadlib函数
print(loadlib())
解决办法是在lua src/luaconf.h文件中,将LUA_COMPAT_LOADLIB激活,然后重新编译lua binary
3. 编写C++对LUA的扩展库:
解决2之后,编写如下的C++代码(helloworld.cc) :
#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编译:
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):
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"
如此一来,你可以少敲很多字符:
g++ -o helloworld.so helloworld.cc `./gcccmd.sh`
在当前目录下会有一个helloworld.so文件产生。进一步你可以用ldd/nm来看下helloworld.so文件包含了些什么。
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())
];
- self是luabind命名空间中的全局变量,代表类本身;
- + 是operator +,对应LUA中的+操作
- int()是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()