luabind 0.9.1版本嘗試

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())
];
  • 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種解決方案:

  1. 針對每個靜態數據,用靜態函數進行封裝;彷佛有點麻煩?!

  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()
相關文章
相關標籤/搜索