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()