• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-4  評論-40  文章-117  trackbacks-0



            LuaBind --最強大的Lua C++ Bind


            1 介紹
            LuaBind 是一個幫助你綁定C++和Lua的庫.她有能力暴露 C++ 函數和類到 Lua . 她也有
            能力支持函數式的定義一個Lua類,而且使之繼承自C++或者Lua. Lua類可以覆寫從 C++ 基類
            繼承來的虛函數. 她的目標平臺是Lua 5.0 ,不能支持Lua 4.0 .

            她利用模板原編程技術實現.這意味著,你不需要額外的預處理過程去編譯你的工程(編譯器
            會替你完成全部的工作).這還意味著,你也不需要(通常)知道你注冊的每一個函數的精確的簽名.
            因為,LuaBind庫會在編譯時生成所需的代碼.這樣做的不利點是,編譯時間會隨著需要注冊的
            文件的數目增加而增加.因此建議你把所有的需要注冊的東西放到一個cpp文件里面.

            LuaBind 遵循 MIT 協議 發布.

            我們非常希望聽說有工程使用了LuaBind, 請告訴我們,如果你的工程使用了LuaBind.

            主要的反饋渠道是 LuaBind郵件列表 .
            在 irc.freenode.net還可以找到一個IRC頻道 #luabind .

            2 功能

            LuaBind支持:

            * 重載自由函數
            * C++類導入Lua
            * 重載成員函數
            * 操作符
            * 屬性
            * 枚舉
            * Lua函數導入C++
            * Lua類導入C++
            * Lua類(單繼承)
            * 從Lua或C++類繼承
            * 覆寫C++類的虛函數
            * 注冊類型間隱式的類型轉換
            * 最好匹配式簽名匹配
            * 返回值策略和參數策略

            3 可移植性

            LuaBind 已經通過下面的編譯器環境的測試:

            Visual Studio 7.1
            Visual Studio 7.0
            Visual Studio 6.0 (sp 5)
            Intel C++ 6.0 (Windows)
            GCC 2.95.3 (cygwin)
            GCC 3.0.4 (Debian/Linux)
            GCC 3.1 (SunOS 5.8)
            GCC 3.2 (cygwin)
            GCC 3.3.1 (cygwin)
            GCC 3.3 (Apple, MacOS X)
            GCC 4.0 (Apple, MacOS X)


            LuaBind被確認不能在 GCC 2.95.2 (SunOS 5.8) 下工作.
            Metrowerks 8.3 (Windows) 可以編譯LuaBind,但是通不過常量測試.這就意味著常量
            成員函數被視同非常量成員函數.
            如果你測試了LuaBind和其他未列出的編譯器的兼容性,請告訴我們你的結果.

            4 構建LuaBind

            為了抑制LuaBind的編譯時間最好是將其編譯為一個庫. 這意味著你要不編譯并連接LuaBind
            庫要不就添加其所有源碼到你的工程里面.你必須確保LuaBind目錄在你的編譯器包含目錄中.
            LuaBind需要Boost 1.32.0 或者 1.33.0 (只需要頭文件即可). LuaBind還需要Lua.

            官方的構建LuaBind的方式是通過 Boost.Build V2 . 為此,你需要設置兩個環境變量:
            BOOST_ROOT 指向你的Boost安裝目錄
            LUA_PATH 指向你的Lua目錄.編譯系統將假定包含文件和庫文件分別放在
            $(LUA_PATH)/include/ 和 $(LUA_PATH)/lib/.

            為了向后兼容性,LuaBind在根目錄下還保留了一個makefile.這可以構建庫和測試程序.如果
            你正在使用一個UNIX系統(或者 cygwin),他們將使得構建LuaBind靜態庫變得很簡單.如果
            你正在使用 Visual Studio ,很簡單的包含 src 目錄下的文件到你的工程即可.

            構建LuaBind的時候,你可以設定一些選項來使得庫更加符合你的需求.特別重要的是,你的應用
            程序也必須使用和庫一樣的設定.可用的選項的介紹參見 Build options 章節.

            如果你希望改變缺省的設置,推薦你通過修改命令行參數的方式來實現.(在Visual Studio
            的工程設置項里面).

            5 基本使用

            為了使用LuaBind, 你必須包含 lua.h 和 LuaBind 的主要頭文件:
            extern "C"
            {
            #include "lua.h"
            }

            #include

            這些頭文件提供了注冊函數和類的功能. 如果你只是想獲得函數或者類的支持,你可以分開
            包含 luabind/function.hpp 和 luabind/class.hpp:
            #include
            #include
            你需要去做的第一件事是 調用 luabind::open(lua_State*), 由此注冊可以在Lua創建類
            的函數并初始化 LuaBind需要使用的 狀態機全局結構. 如果你不調用這個函數, 你會在后面
            觸發一個 斷言 . 不沒有一個對應的關閉函數.因為,一旦一個類被注冊到Lua,真沒有什么好
            的方法去移除它.部分原因是任何剩余的類實例都將依賴其類. 當狀態機被關閉的時候,所有
            的一切都將被清理干凈.

            LuaBind 的頭文件不會直接包含 Lua.h , 而是透過 . 如果你
            出于某種原因需要包含其他的Lua頭文件,你可以修改此文件.

            5.1 Hello World
            新建一個控制臺DLL工程, 名字是 luabind_test.
            #include
            #include
            #include

            extern "C"
            {
            #include "lua.h"
            #include "lauxlib.h"
            }

            void greet()
            {
            std::cout << "hello world!n";
            }

            extern "C" int luaopen_luabind_test(lua_State* L)
            {
            using namespace luabind;

            open(L);

            module(L)
            [
            def("greet", &greet)
            ];

            return 0;
            }

            把生成的DLL和lua.exe/lua51.dll放在同一個目錄下.
            Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio
            > require "luabind_test"
            > greet()
            Hello world!
            >

            6 作用域

            注冊到Lua里面的所有東西要不注冊于一個名空間下(Lua table)要不注冊于全局作用域(lua module).
            所有注冊的東西必須放在一個作用域里面.為了定義一個模塊, luabind::module 類必須被使用.
            使用方式如下:
            module(L)
            [
            // declarations
            ];
            這將會注冊所有的函數或者類到 Lua 全局作用域. 如果你想要為你的模塊設定一個名空間(類似標準模塊),
            你可以給構造函數設定一個名字,例如:
            module(L, "my_library")
            [
            // declarations
            ];
            這里所有的申明都將被放置在 my_libary 表.

            如果你想要嵌套名空間,你可以用 luabind::namespace_ 類. 它和 luabind::module 類似,除了構造器
            沒有lua_State* 輸入參數.用例如下:
            module(L, "my_library")
            [
            // declarations

            namespace_("detail")
            [
            // library-private declarations
            ]
            ];

            你可能會想到,下面兩個聲明是等價的:
            module(L)
            [
            namespace_("my_library")
            [
            // declarations
            ]

            ];
            module(L, "my_library")
            [
            // declarations
            ];
            每一個聲明必須用逗號分隔,例如:
            module(L)
            [
            def("f", &f),
            def("g", &g),
            class_("A")
            .def(constructor),
            def("h", &h)
            ];

            更多實際的例子請參閱 綁定函數到Lua 和 綁定類到Lua 章節.

            請注意, (如果你對性能有很高的需求)把你的函數放到表里面將增加查找函數的時間.

            7 綁定函數到Lua

            為了綁定函數到Lua,你可以使用函數 luabind::def(). 它的聲明如下:
            template
            void def(const char* name, F f, const Policies&);
            * name 是該函數在Lua里面的名字
            * F 是該函數的指針
            * 策略參數是用來描述怎樣處理該函數參數和返回值的.這是一個可選參數,參見 策略 章節.

            下面的例子演示注冊函數 float std::sin(float):
            module(L)
            [
            def("sin", &std::sin)
            ];

            7.1 重載函數

            如果你有同名函數需要注冊到Lua, 你必須顯示的給定函數的簽名.
            這可以讓C++知道你指定的是哪一個函數. 例如, 如果你有兩個函數,
            int f(const char*) 和 void f(int).
            module(L)
            [
            def("f", (int(*)(const char*)) &f),
            def("f", (void(*)(int)) &f)
            ];

            7.2 簽名匹配

            LuaBind 將會生成代碼來檢查Lua棧的內容是否匹配你的函數的簽名. 它會隱式的在
            派生類之間進行類型轉換,并且它會按照盡量少進行隱式類型轉換的原則經行匹配.在
            一個函數調用中,如果函數是重載過的,并且重載函數的參數匹配分不出好壞的話
            (都經行同樣次數的隱式類型轉換),那么將產生一個二義性錯誤.這將生成一個運行時
            錯誤,程序掛起在產生二義性調用的地方.一個簡單的例子是,注冊兩個函數,一個函數
            接受一個int參數,另外一個函數接受一個float參數. 因為Lua將不區別浮點數和整形數,
            所以他們都是匹配的.

            因為所有的重載是被測試過的,這將總是找到最好的匹配(不是第一個匹配).這樣意味著,
            LuaBind可以處理簽名的區別只是const和非const的重載函數.

            例如,如果如下的函數和類被注冊:
            struct A
            {
            void f();
            void f() const;
            };

            const A* create_a();
            為了正確處理所有權轉移問題,create_a()將用來適配返回值策略.
            參見 策略 章節.
            -Linker Lin 4/5/08 6:32 PM

            struct B: A {};
            struct C: B {};

            void g(A*);
            void g(B*);


            執行以下 Lua 代碼即結果:

            a1 = create_a()
            a1:f() -- 常量版本被調用

            a2 = A()
            a2:f() -- 非常量版本被調用

            a = A()
            b = B()
            c = C()

            g(a) -- calls g(A*)
            g(b) -- calls g(B*)
            g(c) -- calls g(B*)


            7.3 調用Lua函數

            為了調用一個Lua函數, 你可以或者用 call_function() 或者用 一個對象(object).

            template
            Ret call_function(lua_State* L, const char* name, ...)
            template
            Ret call_function(object const& obj, ...)


            call_function()函數有兩個重載版本.一個是根據函數的名字來調用函數,
            另一個是調用一個可以作為函數調用的Lua值.

            使用函數名來調用的版本只能調用Lua全局函數. "..."代表傳遞給Lua函數的
            可變個數的參數. 這使得你可以指定調用的策略.你可以通過 operator[] 來實現
            這個功鞥.你可以同過方括號來指定策略,例如:

            int ret = call_function(
            L
            , "a_lua_function"
            , new complex_class()
            )[ adopt(_1) ];


            如果你想通過引用方式傳遞參數,你必須用Boost.Ref來包裝一下.
            例如:

            int ret = call_function(L, "fun", boost::ref(val));


            如果你想給一個函數調用指定自己的錯誤捕獲處理函數(error handler),可以參閱
            pcall errorfunc 章節的 set_pcall_callback .

            7.4 使用Lua協程

            為了使用Lua協程,你必須調用 lua_resume(),這就意味著你不能用先前介紹的函數
            call_function()來開始一個協程.你必須用這個:

            template
            Ret resume_function(lua_State* L, const char* name, ...)
            template
            Ret resume_function(object const& obj, ...)

            和:

            template
            Ret resume(lua_State* L, ...)


            第一次開始一個協程的時候,你必須給它一個入口函數. 當一個協程返回(yield)的時候,
            resume_fucntion()調用的返回值是 lua_yield()的第一個傳入參數.當你想要繼續一個
            協程的時候,你只需要調用 resume() 在你的 lua_State() 上,因為它已經在執行一個函數
            (即先前出入的入口函數),所以你不需要再次傳入函數.resume()的傳入參數將作為Lua側的
            yield()調用的返回值.

            為了暫停(yielding)C++函數,(不支持在C++側和Lua側傳送數據塊),你可以使用 yield 策略.

            接受 object 參數的resume_function()的重載版本要求對象必須是一個協程對象.(thread)

            lua_State* thread = lua_newthread(L);
            object fun = get_global(thread)["my_thread_fun"];
            resume_function(fun);



            8 綁定類到Lua

            為了注冊一個類,你可以用 class_ 類. 它的名字和C++關鍵字類似是為了比較直觀.它有一個重載
            過的成員函數 def() .這個函數被用來注冊類的成員函數,操作符,構造器,枚舉和屬性.它將返回 this
            指針,從而方便你直接注冊更多的成員.

            讓我們開始一個簡單的例子.考慮下面的C++類:

            class testclass
            {
            public:
            testclass(const std::string& s): m_string(s) {}
            void print_string() { std::cout << m_string << "n"; }

            private:
            std::string m_string;
            };


            為了注冊這個類到Lua環境,可以像下面這樣寫(假設你使用了名空間):

            module(L)
            [
            class_("testclass")
            .def(constructor())
            .def("print_string", &testclass::print_string)
            ];


            這將注冊 testclass 類以及接受一個string參數的構造器以及一個成員叫print_string()的函數.

            Lua 5.0 Copyright (C) 1994-2003 Tecgraf, PUC-Rio
            > a = testclass('a string')
            > a:print_string()
            a string


            還可以注冊自由函數作為成員函數.對這個自由函數的要求是,它必須接受該類的一個指針或常量指針或
            引用或常量引用作為函數的第一個參數.該函數的剩下的參數將在Lua側可見,而對象指針將被賦值給第一個
            參數.如果我們有如下的C++代碼:

            struct A
            {
            int a;
            };

            int plus(A* o, int v) { return o->a + v; }


            你可以注冊 plus() 作為A的一個成員函數,如下:


            plus() 現在能夠被作為A的一個接受一個int參數的成員函數來調用.如果對象指針(this指針)是const,
            這個函數也將表現的像一個常量成員函數那樣(它可以通過常量對象來調用).

            8.1 重載成員函數

            當綁定超過一個以上的重載過的成員函數的時候,或只是綁定其中的一個的時候,你必須消除你傳遞給 def() 的
            成員函數指針的歧義.為此,你可以用普通C風格的類型轉換來轉型匹配正確的重載函數. 為此,你必須知道怎么去
            描述C++成員函數的類型.這里有一個簡短的教程(更多信息請查閱你的C++參考書):
            成員函數指著的語法如下:

            return-value (class-name::*)(arg1-type, arg2-type, ...)

            例如:

            struct A
            {
            void f(int);
            void f(int, int);
            };
            class_()
            .def("f", (void(A::*)(int))&A::f)


            A的第一個成員函數f(int)被綁定了,而第二個沒喲被綁定.

            8.2 屬性

            很容易注冊類的全局數據成員.考慮如下的類:

            struct A
            {
            int a;
            };


            這個類可以這樣注冊:


            這使得成員變量 A::a 獲得了讀寫訪問權. 還可以注冊一個只讀的屬性:


            當綁定成員是一個非原始數據類型的時候,自動生成的 getter 函數將會返回一個它引用.
            這就允許你可以鏈式使用 . 操作符.例如,當有一個結構體包含另外一個結構體的時候.如下:

            struct A { int m; };
            struct B { A a; };


            當綁定B到Lua的時候,下面的表達式應該可以工作:

            b = B()
            b.a.m = 1
            assert(b.a.m == 1)


            這要求 a 屬性必須返回一個A的引用, 而不是一個拷貝. 這樣,LuaBind將會自動使用依賴策略來
            確保返回值依賴于它所在的對象.所以,如果返回的引用的生命長于該對象的所有的引用(這里是b).
            它將保持對象是激活的,從而避免出現懸掛指針.

            你還可以注冊 getter 或者 setter 函數來使得它們看上去像一個 public 的成員.考慮下面的類:

            class A
            {
            public:
            void set_a(int x) { a = x; }
            int get_a() const { return a; }

            private:
            int a;
            };


            可以這樣注冊成一個公共數據成員:


            這樣 set_a() 和 get_a() 將取代簡單的數據成員操作.如果你想使之只讀,你只需要省略最后一個參數.
            請注意, get 函數必須是 const 的,否則不能通過編譯.

            8.3 枚舉

            如果你的類包含枚舉,你可以注冊它們到Lua. 注意,它們不是類型安全的,所有的枚舉在Lua側都是整型的,
            并且所有接受枚舉參數的函數都將接受任何整型.你可以像這樣注冊它們:


            在Lua側,他們可以像數據成員那樣被操作,除了它們是只讀的而且屬于類本身而不是類的實例.

            Lua 5.0 Copyright (C) 1994-2003 Tecgraf, PUC-Rio
            > print(A.my_enum)
            4
            > print(A.another_enum)
            6


            8.4 操作符

            為了綁定操作符,你需要包含頭文件 .
            注冊你的類的操作符的機制非常的簡單.你通過一個全局名字 luabind::self 來引用類自己,然后你就
            可以在def()調用里面直接用操作符表達式. 類如下:

            struct vec
            {
            vec operator+(int s);
            };


            可以這樣注冊:

            module(L)
            [
            class_("vec")
            .def(self + int())
            ];


            不管你的 + 操作符是定義在類里面還是自由函數都可以工作.
            如果你的操作符是常量的(const)(或者,是一個自由函數, 接受一個類的常量的引用)你必須用
            const_self 替代 self. 如下:

            module(L)
            [
            class_("vec")
            .def(const_self + int())
            ];


            支持如下操作符:

            + - * / == < <=


            這意味著,沒有"就地操作符"(in-place)(++ --). 相等操作符(==)有些敏銳;如果引用是相等的就不會
            被調用. 這意味著, 相等操作符的效率非常好.

            Lua不支持操作符包括: !=,>和<=.這是為什么你只能注冊上面那些操作符. 當你調用這些操作符的時候,
            Lua會把調用轉換到支持的操作符上.(譯注:例如:==和!=有邏輯非得關系)

            在上面的示例中,操作數的類型是 int().如果操作數的類型是復雜類型,就不是那么簡單了,你需要用 other<>
            來包裝下.例如:
            為了注冊如下的類,我們不想用一個string的實例來注冊這個操作符.

            struct vec
            {
            vec operator+(std::string);
            };


            取而代之的是,我們用 other<> 包裝下,如下:

            module(L)
            [
            class_("vec")
            .def(self + other())
            ];


            注冊一個應用程序操作符(函數調用):

            module(L)
            [
            class_("vec")
            .def( self(int()) )
            ];


            這里有個特殊的操作符.在Lua里,它叫做 __tostring,它不是一個真正的操作符.它是被用來轉換一個對象到
            string的標準Lua方法.如果你注冊之,可以通過Lua的標準函數 tostring() 來轉換你的對象到一個string.

            為了在C++里實現這個操作符,你需要為 std::ostream 提供 operator<< .像這樣:

            class number {};
            std::ostream& operator<<(std::ostream&, number&);

            ...

            module(L)
            [
            class_("number")
            .def(tostring(self))
            ];



            8.5 嵌套作用域和靜態函數

            可以添加嵌套的作用域到一個類.當你需要包裝一個嵌套類或者一個靜態函數的時候就會很有用.

            class_("foo")
            .def(constructor<>()
            .scope
            [
            class_("nested"),
            def("f", &f)
            ];


            在上面的例子里, f 將表現的像一個類 foo 的靜態函數,而 類 nested 將表現的像類 foo 的嵌套類.

            還可以用同樣的語法添加名空間到類里面.


            8.6 繼承類

            如果你想要注冊一個繼承自其它類的類到Lua, 你可以指定一個模板參數 bases<> 給 class_ 的構造器.
            如下的繼承關系:

            struct A {};
            struct B : A {};


            可以這樣注冊:

            module(L)
            [
            class_("A"),
            class_("B")
            ];


            如果你使用了多繼承,你可以指定多于一個的基類.如果 B 還繼承了類 C , 它可以這樣注冊:

            module(L)
            [
            class_<B, bases >("B")
            ];

            注意,你可以省去 bases<> 當你用的是單繼承的時候.

            注意

            如果你不指定類的繼承關系, LuaBind 將不能在相關的繼承類型間進行隱式類型轉換.



            8.7 智能指針

            當你注冊一個類的時候,你可以告訴 LuaBind 所有的該類的實例應該被某種智能指針持有.(例如: boost::shared_ptr)
            你可通過把一個 持有器類型模板參數 給 class_ 類的構造器來實現該功能.例如:

            module(L)
            [
            class_<A, boost::shared_ptr >("A")
            ];


            你還必須為你的智能指針提供兩個函數.一個返回常量版本的智能指針類型(這里是: boost:shared_ptr< const A >).
            另一個函數要可以從智能指針萃取流指針(raw pointer). 之所以需要第一個函數是因為,LuaBind 允許
            非常量 -> 轉換在傳遞Lua值到C++的時候.之所以需要第二個函數是因為,當Lua調用一個被智能指針持有
            的類的成員函數的時候,this 指針必須是一個流指針.還有一個原因是,從Lua轉換到C++的時候,需要實現
            智能指針到普通指針的轉換.看上去像這樣:

            namespace luabind {

            template
            T* get_pointer(boost::shared_ptr& p)
            {
            return p.get();
            }

            template
            boost::shared_ptr*
            get_const_holder(boost::shared_ptr*)
            {
            return 0;
            }
            }


            第二個函數只在編譯時用于映射 boost::shared_ptr到其常量版本 boost::shared_ptr.
            它從來不會被調用,所以返回值是無所謂的(返回值的類型才是關鍵).

            這個轉換將這樣工作(假定 B 是A的基類):
            從Lua到C++
            Source Target
            holder_type A*
            holder_type B*
            holder_type A const*
            holder_type B const*
            holder_type holder_type
            holder_type holder_type<A const>
            holder_type<A const> A const*
            holder_type<A const> B const*
            holder_type<A const> holder_type<A const

            從C++到Lua
            Source Target
            holder_type holder_type
            holder_type<A const> holder_type<A const>
            holder_type const& holder_type
            holder_type<A const> const& holder_type<A const>

            當使用持有器類型的時候,知道指針是不是合法(例如:非空)是很有用的.例如,當使用 std::auto_ptr 的時候,
            持有器通過一個參數傳遞給函數的時候將會變得無效. 為了這個目的,所有的對象實例都有一個成員叫: __ok.

            struct X {};
            void f(std::auto_ptr);

            module(L)
            [
            class_<X, std::auto_ptr >("X")
            .def(constructor<>()),

            def("f", &f)
            ];
            Lua 5.0  Copyright (C) 1994-2003 Tecgraf, PUC-Rio
            > a = X()
            > f(a)
            > print a.__ok
            false


            當注冊一個繼承樹的時候,所有的實例被智能指針持有的地方,所有的類必須包含持有器類型.例如:

            module(L)
            [
            class_<base, boost::shared_ptr >("base")
            .def(constructor<>()),
            class_<derived, base, boost::shared_ptr >("base")
            .def(constructor<>())
            ];


            在內部, LuaBind 將會做必要的轉換于萃取自持有器的流指針之上.

            8.8 拆分類注冊

            在某些情況下,可能需要分開注冊一個類在不同的編譯單元. 部分原因可能是節約重編譯時間,而某些編譯器的
            限制可能要求不得不分開注冊一個類.其實很簡單.考慮下面的示例代碼:

            void register_part1(class_& x)
            {
            x.def(/*...*/);
            }

            void register_part2(class_& x)
            {
            x.def(/*...*/);
            }

            void register_(lua_State* L)
            {
            class_ x("x");

            register_part1(x);
            register_part2(x);

            module(L) [ x ];
            }


            這里,類X被分兩步注冊.兩個函數 register_part register_part2 可能被放到不同的編譯單元里.

            關于分開注冊一個模塊的信息請參閱: 分開注冊 章節.



            9 對象

            因為函數必須能夠接受Lua值作為參數,我們必須包裝之. 這個包裝被稱作
            luabind::object. 如果你注冊的函數
            接受一個對象,那它就可以匹配任何Lua值.為了使用它,你需要包含頭文件: .

            摘要

            class object
            {
            public:
            template
            object(lua_State*, T const& value);
            object(from_stack const&);
            object(object const&);
            object();

            ~object();

            lua_State* interpreter() const;
            void push() const;
            bool is_valid() const;
            operator safe_bool_type () const;

            template
            implementation-defined operator[](Key const&);

            template
            object& operator=(T const&);
            object& operator=(object const&);

            bool operator==(object const&) const;
            bool operator<(object const&) const;
            bool operator<=(object const&) const;
            bool operator>(object const&) const;
            bool operator>=(object const&) const;
            bool operator!=(object const&) const;

            template
            implementation-defined operator[](T const& key) const

            void swap(object&);

            implementation-defined operator()();

            template
            implementation-defined operator()(A0 const& a0);

            template
            implementation-defined operator()(A0 const& a0, A1 const& a1);

            /* ... */
            };


            當你需要一個Lua對象的時候,你可以通過=操作符給它賦一個新值.當你這么做的時候,default_policy
            會被用來轉換C++值到Lua. 如果你的 luabind::object 是一個table,你可以通過 []操作符或者迭代器
            來訪問它的成員.[]操作符的返回值是一個代理對象,這個對象可以用于讀寫表里的值(通過=操作符).

            注意,沒有辦法知道一個Lua對象是否可以索引化訪問( lua_gettable 不會失敗,要不成功,要不崩潰 ).
            這意味著,如果你在一個不可以索引化訪問的東西上進行索引,你就只能靠自己了.Lua將會調用它的 panic()
            函數.

            還有一些自由函數可以用來索引一張table,參閱 相關函數 章節.

            那個接受 from_stack 對象作為參數的構造器是用來初始化一個關聯Lua棧值的對象的. from_stack 類型
            有如下的構造器:

            from_stack(lua_State* L, int index);


            index參數就是原始的Lua棧的索引,負值是從棧頂開始索引的.你可以這樣用:

            object o(from_stack(L, -1));


            這將會創建一個 object的實例 o,并拷貝Lua棧頂的對象的值.

            interpreter() 函數返回保存object實例的Lua狀態機.如果你想要直接用Lua函數操作object對象的實例,你
            可以通過調用 push() 來把它壓入Lua棧.

            ==操作符將會在操作數上調用 lua_equal()并返回它的結果.

            is_valid() 函數會告訴你object的實例是否已經初始化過了.通過默認構造器來初始化的實例是非法的.要使之
            合法,你可以給其賦一個值.如果你想使一個 object 不合法,最簡單的辦法就是給它賦一個非法的 object.

            operator safe_bool_type() 和 to is_valid() 是等價的.這意味著,下面的代碼片段是等價的:

            object o;
            // ...
            if (o)
            {
            // ...
            }

            ...

            object o;
            // ...
            if (o.is_valid())
            {
            // ...
            }


            應用程序操作符() 將會像對待一個函數那樣來調用綁定的值. 你可以給它任何數量的參數
            (目前,
            default_policy 將被用于轉換 ).返回的對象將代表函數的返回值(當前只支持一個返回值).該操作符
            可能會拋出
            luabind::error ,如果函數調用失敗.如果你想指定一個特殊的函數調用策略,你可以通過在函數
            調用時使用 []操作符來指定策略.像這樣:

            my_function_object(
            2
            , 8
            , new my_complex_structure(6)
            ) [ adopt(_3) ];


            這告訴 LuaBind 讓 Lua 接受所有權和負責傳入給lua函數的指針.

            重要的是當Lua狀態機關閉的時候,所有的 object 的實例都會被析構.object實例會持有Lua狀態機的指針,并在
            自己析構的時候釋放它的Lua對象.

            這里有一個函數怎樣使用 table 的例子:

            void my_function(object const& table)
            {
            if (type(table) == LUA_TTABLE)
            {
            table["time"] = std::clock();
            table["name"] = std::rand() < 500 ? "unusual" : "usual";

            std::cout << object_cast(table[5]) << "n";
            }
            }


            如果函數接受一個object作為參數,那么任何Lua值都將匹配這個參數.這就是為什么,我們必須保證入參是一個table

            的原因.

            std::ostream& operator<<(std::ostream&, object const&);


            流操作符可以把object實例借由 boost::lexical_cast 轉換到string或者方便打印輸出.這將會使用Lua的string

            轉換函數.如果你用 tostring 去轉換一個C++對象,對應類型的流操作符將會被使用.

            9.1 迭代器

            有兩種迭代器. 普通迭代器將會使用對象的原方法(如果存在)來獲取值. 普通迭代器被稱為 luabind::iterator. 另一個

            迭代器被稱為 luabind::raw_iterator ,它將忽略原方法而直接給出表里的真實內容. 它們具有相同的接口, 都實現了

            ForwardIterator 概念.大部分標準迭代器都有如下的成員和構造器:

            class iterator
            {
            iterator();
            iterator(object const&);

            object key() const;

            standard iterator members
            };


             

             

            接受一個 luabind::object 的構造器實際上是一個用于操作 object 的模板.通過傳入一個 object 給構造器來構造出

            一個指向 object 里的第一個元素的迭代器.

            缺省的構造器將會初始化迭代器為一個指向最后一個元素的后面位置的迭代器.這可以用來測試是否抵達了序列的末端.


            迭代器的值類型是一個支持和 luabind::object 相同的操作的代理類型.這意味著,大部分情況下你可以當它就是一個原始
            的 object 實例. 它們之間的不同之處在于,任何對代理的賦值操作都會導致值被插入到表中迭代器所指的位置.

            key() 成員返回迭代器用于索引表的鍵.

            一個迭代器的例子如下:

            for (iterator i(globals(L)["a"]), end; i != end; ++i)
            {
            *i = 1;
            }


            end 迭代器是一個缺省的指向序列末尾的迭代器.在這個例子里,我們簡單的迭代了表 a 里面所有的實體,并將之賦值為 1.


            9.2 相關函數

            這里介紹些用于 對象 和 表 操作的函數.

            int type(object const&);


            這個函數將會返回lua類型索引.例如: . LUA_TNIL, LUA_TNUMBER 等.

            template
            void settable(object const& o, K const& key, T const& value);
            template
            object gettable(object const& o, K const& key);
            template
            void rawset(object const& o, K const& key, T const& value);
            template
            object rawget(object const& o, K const& key);


            這些函數是用來索引 table 用的. settable 和 gettable 函數分別翻譯調用到 lua_settable lua_gettable 函數.
            這意味著,你可以在對象上使用索引操作符.

            rawset 和 rawget 將會翻譯調用到 lua_rawset 和 lua_rawget. 所以他們可以繞開任何原方法而給你表里實體的
            真實值.

            template
            T object_cast(object const&);
            template
            T object_cast(object const&, Policies);

            template
            boost::optional object_cast_nothrow(object const&);
            template
            boost::optional object_cast_nothrow(object const&, Policies);


            object_cast 函數轉型對象的值到C++值.你可以給這個從lua到C++的轉換提供一個轉換策略.如果轉型失敗,
            cast_failed 異常將被拋出. 如果你已經定義了
            LUABIND_NO_ERROR_CHECKING (參閱 編譯選項)宏,就不會
            進行任何檢查,如果轉型非法,應用程序將會徹底崩潰. 不拋出異常的版本會返回一個沒有初始化的
            boost::optional 對象,由此來指出轉型不能進行.

            上面的函數的簽名確實是模板化的 object 參數,但是這里你應該只傳遞 object 對象.

            object globals(lua_State*);
            object registry(lua_State*);


             

             

            這些函數分別返回全局環境表和Lua注冊表.

            object newtable(lua_State*);


             

             

            這個函數創建一個新的 table 并以一個 object 來返回它.




            10 在Lua里定義類

            作為一個附加功能,LuaBind還提供了一個 Lua側OO系統來綁定C++函數和對象.

            class 'lua_testclass'

            function lua_testclass:__init(name)-- 譯注:這個風格類似Python的OO語法
            self.name = name
            end

            function lua_testclass:print()
            print(self.name)
            end

            a = lua_testclass('example')
            a:print()


             

            在Lua類之間可以使用繼承:

            class 'derived' (lua_testclass)

            function derived:__init() super('derived name')
            end

            function derived:print()
            print('Derived:print() -> ')
            lua_testclass.print(self)-- 譯注:注意這里 : 和 . 的區別
            end


             

             

            這里的 super 關鍵字用來初始化基類.用戶必須在構造器里面第一個調用 super.

            正如你在這個例子里看到的,你可以調用基類的成員函數.你可以找到所有的基類成員,但是你必須把 this指針(self)

            做為函數的第一個參數.

            10.1 在Lua里繼承

            你還可以從Lua側繼承一個C++類,并用Lua函數來覆寫虛函數.為了實現這個,我們必須為C++基類創建一個封裝類.

            當我們實例化一個Lua類的時候,這個封裝類將持有Lua對象.

            class base
            {
            public:
            base(const char* s)
            { std::cout << s << "n"; }

            virtual void f(int a)
            { std::cout << "f(" << a << ")n"; }
            };

            struct base_wrapper : base, luabind::wrap_base
            {
            base_wrapper(const char* s)
            : base(s)
            {}

            virtual void f(int a)
            {
            call("f", a);
            }

            static void default_f(base* ptr, int a)
            {
            return ptr->base::f(a);
            }
            };

            ...

            module(L)
            [
            class_ ("base")
            .def(constructor())
            .def("f", &base::f, &base_wrapper::default_f)
            ];


             

             


            重要

            因為MSVC6.5不支持成員函數的顯示模板參數化,作為成員函數 call()的替代, 你可以調用自由函數 call_member()并把 this指針作為第一個參數傳入該函數.


             

             

            注意,如果你同時綁定 base 類 和 base類封裝,你必須把基類和基類的封裝一起作為模板參數提供給 class_

            (就像上面的例子中所做的一樣).你指定它們的順序并不重要.你必須還要從wrapper注冊靜態版本的和虛函數版

            本的封裝函數,這是讓LuaBind實現動態和靜態分派函數調用的必須.


            重要

            極其重要的是靜態(缺省)函數的簽名必須和虛函數一致.




            Linker Lin翻譯此文檔只為提供更多信息,轉載請保留 原文鏈接.

            posted on 2008-05-08 13:52 李陽 閱讀(3631) 評論(1)  編輯 收藏 引用 所屬分類: LUA

            評論:
            # re: LuaBind --最強大的Lua C++ Bind 2008-07-07 02:34 | Linker.Lin
            日本久久久久亚洲中字幕| 香蕉99久久国产综合精品宅男自 | 国产99精品久久| 精品亚洲综合久久中文字幕| 青青青青久久精品国产h| 久久国产热这里只有精品| 国产亚洲精品久久久久秋霞| 91久久婷婷国产综合精品青草 | 蜜臀久久99精品久久久久久小说| 久久国产乱子精品免费女| 欧美激情精品久久久久久久| 97久久久精品综合88久久| 一本大道久久东京热无码AV| 久久精品国产91久久综合麻豆自制| 亚洲国产成人乱码精品女人久久久不卡 | 久久露脸国产精品| 久久天天躁狠狠躁夜夜网站| 久久伊人五月天论坛| 久久99精品国产| 久久人人爽人人爽人人AV| 久久精品亚洲福利| 999久久久免费国产精品播放| 久久综合国产乱子伦精品免费| 三级韩国一区久久二区综合| 成人a毛片久久免费播放| 国产精品岛国久久久久| 久久精品国产亚洲精品2020| 日韩乱码人妻无码中文字幕久久| 最新久久免费视频| 欧美黑人激情性久久| 亚洲va久久久久| 精品久久久无码21p发布| 伊人久久大香线蕉综合网站| 欧美精品福利视频一区二区三区久久久精品 | 精品999久久久久久中文字幕| 久久人人妻人人爽人人爽| 亚洲精品无码久久久久| 无码日韩人妻精品久久蜜桃| 欧美一区二区三区久久综合| 国产精品久久久久国产A级| 久久99国产综合精品女同|