• <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>

            loop_in_codes

            低調(diào)做技術(shù)__歡迎移步我的獨(dú)立博客 codemaro.com 微博 kevinlynx

            實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn)

            實(shí)現(xiàn)LUA綁定器

            author : Kevin Lynx

            Preface


                當(dāng)LUA腳本調(diào)用我們注冊(cè)的C函數(shù)時(shí),我們需要逐個(gè)地從LUA棧里取出調(diào)用參數(shù),當(dāng)函數(shù)返回時(shí),又需要一個(gè)一個(gè)地往LUA
            棧壓入返回值,并且我們注冊(cè)的函數(shù)只能是int()(lua_State*)類型。這很不方便,對(duì)于上層程序員來說更不方便。
                因此我們要做的是,實(shí)現(xiàn)一個(gè)綁定器,可以把任意prototype的函數(shù)綁定到LUA腳本當(dāng)中,并且封裝取參數(shù)和壓返回值時(shí)
            的諸多細(xì)節(jié)。
                確實(shí),世界上已經(jīng)有很多庫(kù)做了這件事情。但是,我們這里的需求很簡(jiǎn)單,我們只需要綁定函數(shù),而不需要綁定C++類之
            類的東西,自己實(shí)現(xiàn)的才是輕量級(jí)的。

            What we usually do

                先看下我們平時(shí)是怎么做這些事的。首先注冊(cè)函數(shù)很簡(jiǎn)單:

                lua_pushcfunction( L, to_string );
                lua_setglobal( L,
            "tostr"
            );


                然后是func的具體處理:

                /** 假設(shè)to_string在腳本中的原型是: string ()( number ) */
               
            static int to_string( lua_State *L )
               
            {
                   
            static char buf[512
            ];
                   
            /* 從LUA棧中取參數(shù) */

                   
            int n = (int)lua_tonumber( L, -1 );
                    sprintf( buf,
            "%d"
            , n );
                   
            /* 壓入返回值 */

                    lua_pushstring( L, buf );
                   
            return 1;
                }


                這是個(gè)簡(jiǎn)單的例子,目的是展示我之前說的局限性,以及,恩,丑陋性。


            How

                要讓事情變得優(yōu)美,我們就得隱藏丑陋。
                首先,我們看看如何改進(jìn)to_string的處理,使其看起來干凈。最直接也是最通用的做法是,我們自己做一個(gè)粘合層,
            充當(dāng)LUA與應(yīng)用層之間的粘合劑。也就是說,LUA直接回調(diào)的不再直接是應(yīng)用層的函數(shù),而是我們實(shí)現(xiàn)的這一層中的函數(shù),
            我們的函數(shù)整理調(diào)用參數(shù),然后回調(diào)到上層函數(shù),上層返回后,我們收集上層的返回值,然后整理給LUA,最后返回。
                這就是思路,具體實(shí)現(xiàn)時(shí)更為有趣。

            Implementing...

                直覺告訴我,我需要使用C++模板來實(shí)現(xiàn)。模板和宏都是個(gè)好東西,因?yàn)樗鼈兪欠盒缘模鼈兘o程序員帶來自動(dòng)性。
                另一個(gè)直覺告訴我,盡量不要讓上層保存任何東西。通過模板的實(shí)例化,編譯器已經(jīng)為我們添加了很多東西,我也不
            想讓上層理會(huì)我太多。
                因?yàn)椋覀冎辽傩枰4嫔蠈拥暮瘮?shù)指針(我們暫時(shí)只考慮C式的函數(shù)),我們至少還需要一個(gè)粘合層函數(shù)用以被LUA
            直接回調(diào),所以,我得到了以下類模板:

                template <typename Prototype>
               
            class lua_binder
               
            {
               
            public
            :
                    typedef Prototype func_type;
               
            public
            :
                   
            static int adapter( lua_State *
            L )
                   
            {
                       
            return 0
            ;
                    }
             

               
            public
            :
                   
            static
            func_type _func;
                }
            ;
                template
            <typename Prototype> typename lua_binder<Prototype>::func_type lua_binder<Prototype>::_func = 0
            ;


                這樣,泛化了Prototype后,lua_binder可以保存任意原型的函數(shù)指針。例如:

             

            typedef lua_binder<const char*(int)> binder_type;

              
                借助于模板技術(shù),即使上層只是這樣一個(gè)簡(jiǎn)單的看似不會(huì)產(chǎn)生任何代碼的typedef,實(shí)際上也會(huì)產(chǎn)生出一個(gè)static的
            函數(shù)指針變量:_func。

                這個(gè)時(shí)候,我們也該考慮下注冊(cè)函數(shù)部分了。注意,事實(shí)上我們總共需要干兩件事:封裝粘合層函數(shù)、封裝注冊(cè)函數(shù)
            部分。同樣,我們得到一個(gè)最直觀的注冊(cè)函數(shù)模板:

             

                template <typename binder_type>
               
            void lua_bind( lua_State *L, typename binder_type::func_type func, const char *name )
               
            {
                    binder_type::_func
            =
            func;
                    lua_pushcfunction( L, binder_type::adapter );
                    lua_setgloabl( L, name );
                }


                為什么模板參數(shù)是binder_type而不是Prototype?(最直接的想法可能會(huì)想到Prototype)因?yàn)槲覀冃枰@取func_type
            以及最重要的:設(shè)置_func的值!綜合起來,lua_bind函數(shù)主要作用就是接受用戶層函數(shù)指針,并相應(yīng)的將粘合層函數(shù)注冊(cè)
            到LUA中。注意,lua_pushcfunction注冊(cè)的是binder_type::adapter函數(shù)。

                那么,理論上,我們現(xiàn)在可以這樣注冊(cè)一個(gè)函數(shù):

                typedef lua_binder<const char*(_cdecl*)(int)> binder_type;
                lua_bind
            <binder_type>( L, to_string, "tostr"
            );


                (這個(gè)時(shí)候to_string為:const char* to_string( int ) )


            處理函數(shù)參數(shù)的個(gè)數(shù)

                事情遠(yuǎn)沒有我們想象的那么簡(jiǎn)單。adapter函數(shù)中毫無實(shí)現(xiàn),重要的是,該如何去實(shí)現(xiàn)?我們面對(duì)的首個(gè)問題是:上層
            函數(shù)參數(shù)個(gè)數(shù)不一樣,那么我們的adapter該調(diào)用多少次lua_to*去從LUA棧中獲取參數(shù)?
                解決該問題的辦法是,恩,很笨,但是這可以工作:為不同參數(shù)個(gè)數(shù)的函數(shù)都實(shí)現(xiàn)一個(gè)對(duì)應(yīng)的adapter。沒有參數(shù)的函數(shù)對(duì)
            應(yīng)一個(gè)adapter,一個(gè)參數(shù)的函數(shù)對(duì)應(yīng)另一個(gè)adapter,依次類推。
                (穿插一下:ttl(tiny template library)庫(kù)中使用了一個(gè)很強(qiáng)大的宏技術(shù),可以自動(dòng)生成這些代碼,但是具體原理
            我不懂。所以只能使用這個(gè)笨辦法了。)
                這樣,我們就需要區(qū)分不同參數(shù)個(gè)數(shù)的函數(shù)原型。很顯然,我們需要改進(jìn)lua_binder。行之有效的技術(shù)是:模板偏特化。
            改進(jìn)后的lua_binder類似于:

                template <typename Prototype>
               
            class lua_binder;

                template
            <typename R, typename P1>

               
            class lua_binder<R ( P1 )>
               
            {
               
            public
            :
                    typedef R result_type;
                    typedef P1 p1_type;
                    typedef result_type (
            *
            func_type)( P1 );
               
            public
            :
                   
            static int adapter( lua_State *
            L )
                   
            {
                       
            return 0
            ;
                    }

               
            public:
                   
            static
            func_type _func;
                }
            ;
                template
            <typename R, typename P1>
             
                typename lua_binder
            <R( P1 )>::func_type lua_binder<R( P1 )>::_func = 0
            ;

               
            //



                lua_binder主體已經(jīng)是一個(gè)單純的聲明而已,它的諸多特化版本將分別對(duì)應(yīng)0個(gè)參數(shù),1個(gè)參數(shù),2個(gè)參數(shù)等。例如以上
            列舉的就是一個(gè)參數(shù)的偏特化版本。

            Now, we can try ??

                那么,我們現(xiàn)在是否可以寫下諸多的lua_to*函數(shù)去獲取參數(shù)了?你覺得可以嗎?假設(shè)現(xiàn)在要獲取棧頂?shù)谝粋€(gè)參數(shù),你
            該調(diào)用lua_tonumber還是lua_tostring?
                問題就在于,我們并不知道該調(diào)用哪個(gè)函數(shù)。
                解決辦法是:根據(jù)不同的參數(shù)類型,調(diào)用對(duì)應(yīng)的lua_to*函數(shù)。
                不同的類型擁有不同的行為,這一點(diǎn)讓你想起什么?那就是模板世界里的type traits,類型萃取。我想,完成本文的
            綁定器,更多的是對(duì)你模板編程能力的考驗(yàn)。
                lua_to*系列函數(shù)是有限的,因此我們也只需要實(shí)現(xiàn)幾個(gè)類型的行為即可。我們這個(gè)時(shí)候的目的就是,根據(jù)不同的類型,
            調(diào)用對(duì)應(yīng)的lua_to*函數(shù)。例如,對(duì)于number(int, long, double, float, char等等),我們就調(diào)用lua_tonumber。
                于是得到:

                template <typename _Tp>
               
            struct param_traits
               
            {
                   
            static _Tp get_param( lua_State *L, int
            index )
                   
            {
                       
            return static_cast<_Tp>
            ( lua_tonumber( L, index ) );
                    }

                }
            ;

                template
            <>

               
            struct param_traits<const char*>
               
            {
                   
            static const char *get_param( lua_State *L, int
            index )
                   
            {
                       
            return
            lua_tostring( L, index );
                    }

                }
            ;
               
            //others



                param_traits主體處理所有的number(因?yàn)閚umber類型太多,也許concept可以解決這個(gè)問題),其他特化版本處理其他
            類型。這樣,在adapter里,就可以根據(jù)參數(shù)類型獲取到相應(yīng)的參數(shù)了,例如:

                P1 p1 = param_traits<P1>::get_param( L, -1);
                到這個(gè)時(shí)候,我們的adapter函數(shù)變?yōu)椋海ㄒ砸粋€(gè)參數(shù)的函數(shù)舉例)

                static int adapter( lua_State *L )
               
            {
                    p1_type p1
            = param_traits<p1_type>::get_param( L, -1
            );
                    _func( p1 );

                   
            return 0
            ;
                }
             


            And how about the result ??

                是的,我們還需要處理函數(shù)返回值。我們暫時(shí)假設(shè)所有的函數(shù)都只有一個(gè)返回值。這里面對(duì)的問題同取參數(shù)一樣,我
            們需要根據(jù)不同的返回值類型,調(diào)用對(duì)應(yīng)的lua_push*函數(shù)壓入返回值。
                同樣的type traits技術(shù),你應(yīng)該自己寫得出來,例如:

                template <typename _Tp>
               
            struct return_traits
               
            {
                   
            static void set_result( lua_State *
            L, lua_Number r )
                   
            {
                        lua_pushnumber( L, r );               
                    }

                }
            ;
                template
            <>

               
            struct return_traits<const char*>
               
            {
                   
            static void set_result( lua_State *L, const char *
            r )
                   
            {
                        lua_pushstring( L, r );
                    }

                }
            ;


                到這個(gè)時(shí)候,我們的adapter函數(shù)基本成型了:

                static int adapter( lua_State *L )
               
            {
                    p1_type p1
            = param_traits<p1_type>::get_param( L, -1
            );
                    result_type r
            =
            _func( p1 );
                    return_traits
            <result_type>
            ::set_result( L, r );
                   
            return 0
            ;
                }
             


            The last 'return' ???

                最礙眼的,是adapter最后一行的return。LUA手冊(cè)上告訴我們,lua_CFunction必須返回函數(shù)返回值的個(gè)數(shù)。我們已經(jīng)
            假設(shè)我們只支持一個(gè)返回值,那么,很好,直接返回1吧。
                關(guān)鍵在于,C/C++的世界里還有個(gè)關(guān)鍵字:void。是的,它表示沒有返回值。在用戶層函數(shù)返回值為void類型時(shí)(原諒
            這矛盾的說法),我們這里需要返回0。
                你意識(shí)到了什么?是的,我們需要根據(jù)返回值類型是否是void來設(shè)置這個(gè)return的值:1或者0。又是個(gè)type traits的
            小技術(shù)。我想你現(xiàn)在很熟悉了:

                template <typename _Tp>
               
            struct return_number_traits
               
            {
                   
            enum

                   
            {
                        count
            = 1

                    }
            ;
                }
            ;
                template
            <>

               
            struct return_number_traits<void>
               
            {
                   
            enum

                   
            {
                        count
            = 0

                    }
            ;
                }
            ;


                于是,我們的adapter變?yōu)椋?br>

                static int adapter( lua_State *L )
               
            {
                    p1_type p1
            = param_traits<p1_type>::get_param( L, -1
            );
                    result_type r
            =
            _func( p1 );
                    return_traits
            <result_type>
            ::set_result( L, r );
                   
            return return_number_traits<result_type>
            ::count;
                }





            Is everything OK?

                我很高興我能流暢地寫到這里,同樣我希望我不僅向你展示了某個(gè)應(yīng)用的實(shí)現(xiàn),而是展示了模板編程的思想。
                但是,問題在于,當(dāng)你要注冊(cè)一個(gè)返回值為void的函數(shù)時(shí):

                typedef lua_binder<void(int)> binder_type;
                lua_bind
            <binder_type>( L, my_fn, "fn"
            );


                你可能會(huì)被編譯器告知:非法使用void類型。
                是的,好好省視下你的代碼,當(dāng)你的binder_type::result_type為void時(shí),在adapter函數(shù)中,你基本上也就寫下了
            void r = something 的代碼。這是個(gè)語(yǔ)法錯(cuò)誤。

                同樣的問題還有:當(dāng)返回值是void時(shí),我們也沒有必要調(diào)用return_traits的set_result函數(shù)。
                我想你覺察出來,又一個(gè)type traits技術(shù)。我們將根據(jù)result_type決定不同的處理方式。于是,我寫了一個(gè)caller:

                template <typename _Tp>
               
            struct caller
               
            {
                   
            static void call( lua_State *L, p1_type &
            p1 )
                   
            {
                        result_type r
            =
            _func( p1 );
                        return_traits
            <result_type>
            ::set_result( L, r );
                    }

                }
            ;

                template
            <>

               
            struct caller<void>
               
            {
                   
            static void call( lua_State *L, p1_type &
            p1 )
                   
            {
                        _func( p1 );
                    }

                }
            ;


                caller將根據(jù)不同的返回值類型決定如何去回調(diào)_func。比較遺憾的是,我們需要為每一個(gè)lua_binder編寫這么一個(gè)
            caller,因?yàn)閏aller要調(diào)用_func,并且_func的參數(shù)個(gè)數(shù)不同。
                那么現(xiàn)在,我們的adapter函數(shù)變?yōu)椋?br>

                static int adapter( lua_State *L )
               
            {
                    P1 p1
            = lua::param_traits<P1>::get_param( L, -1
            );
                    caller
            <result_type>
            ::call( L, p1 );
                   
            return return_number_traits<result_type>
            ::count;
                }


            END

                最后一段的標(biāo)題不帶問號(hào),所以這就結(jié)束了。下載看看我的代碼吧,為了給不同參數(shù)個(gè)數(shù)的函數(shù)寫binder,始終需要
            粘貼復(fù)制的手工勞動(dòng)。
                值得注意的是,在最終的代碼里,我使用了
            以前實(shí)現(xiàn)的functor,將函數(shù)類型泛化。這樣,lua_binder就可以綁定類
            成員函數(shù),當(dāng)然,還有operator()。

                開源代碼某個(gè)時(shí)候還需要勇氣,因?yàn)殚_源意味著你的代碼會(huì)被人們考驗(yàn)。不過我對(duì)我代碼的風(fēng)格比較自信。:D

                下載完整的lua_binder

            posted on 2008-08-13 09:33 Kevin Lynx 閱讀(6976) 評(píng)論(15)  編輯 收藏 引用 所屬分類: c/c++lua

            評(píng)論

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2008-08-13 09:47 dophi

            kevin哥哥真厲害啊~  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2008-08-13 22:45 陳梓瀚(vczh)

            根據(jù)經(jīng)驗(yàn),開源之后,很少人會(huì)真的給你反饋什么。特別是在這里。  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2008-08-14 15:29 白云哥

            模板用的很精彩,學(xué)習(xí)  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn)[未登錄] 2008-08-14 22:52 清風(fēng)徐來

            nice work  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn)[未登錄] 2008-08-17 23:04 Gohan

            從你的文章中了解到了類型萃取,又是一個(gè)不小的收獲。  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2008-09-11 19:32 czc

            謝謝共享代碼,有兩個(gè)疑問:

            1) 文中寫道:

            typedef lua_binder<const char*(int)> binder_type;

            借助于模板技術(shù),即使上層只是這樣一個(gè)簡(jiǎn)單的看似不會(huì)產(chǎn)生任何代碼的typedef,實(shí)際上也會(huì)產(chǎn)生出一個(gè)static的 函數(shù)指針變量:_func。

            =================================================
            如果只是用 typedef 定義一個(gè)類型而沒有使用的話是不會(huì)產(chǎn)生任何代碼的。上面的那句 typedef 編譯器不會(huì)為 _func 分配存儲(chǔ)空間,我為此還特意寫了個(gè)簡(jiǎn)單程序驗(yàn)證了一下。或者這里是指別的意思?

            2) _func 被聲明為靜態(tài)類型,意味著每種函數(shù)類型只能bind一個(gè)實(shí)例函數(shù)?如果要bind 多個(gè)同類型的函數(shù),那么只有最后被bind的可以被調(diào)用,前面的被后面的覆蓋了。 比如在你的測(cè)試程序里再注冊(cè)一個(gè)函數(shù) void my_fun_2(), 在 test.lua 中調(diào)用

            a = fn()
            a = fn_2()

            結(jié)果會(huì)打印出兩個(gè) my_fn_2, 而不是一個(gè)my_fn, 一個(gè)my_fn_2


              回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2008-09-12 09:28 Kevin Lynx

            @czc
            非常高興有人可以給我提出如此寶貴的意見!我覺得很少有人會(huì)把我寫的東西認(rèn)真讀過,甚至相關(guān)代碼。

            1)我覺得你是對(duì)的,我認(rèn)為只要類模板被實(shí)例化,就相當(dāng)于產(chǎn)生了一個(gè)類,那么就會(huì)產(chǎn)生這個(gè)static變量。但是typedef很可能沒有實(shí)例化類模板,所以我覺得你是對(duì)的,但是如何去證明這一點(diǎn)?

            2)你說的完全正確。當(dāng)lua_binder被用于實(shí)際項(xiàng)目時(shí),我也發(fā)現(xiàn)了這個(gè)問題,所以最后我只得用一個(gè)ID去區(qū)分這些binder:
            template <typename Prototype, long id>
            class lua_binder;
            上層代碼就不得不自己提供一個(gè)唯一的ID,很丑陋,但是我沒有想到優(yōu)雅的解決辦法,不知道你有沒有什么看法?
              回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2008-12-25 12:12 天堂的隔壁

            似乎沒有考慮大量參數(shù)是數(shù)據(jù)結(jié)構(gòu)的情況?
            不過思路已經(jīng)很有啟發(fā)作用了,3ks  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2009-02-27 16:19 劍神一笑

            先鼓勵(lì)下樓主的共享精神 :-)
            不過這樣的實(shí)現(xiàn)自己研究玩還可以,但是實(shí)際項(xiàng)目中基本上不會(huì)用到。
            原因很簡(jiǎn)單,違反了kiss原則,在我們這些UNIX程序員的眼中,你舉的第一個(gè)“丑陋”的例子才是美的,因?yàn)轫?xiàng)目中其它成員看到,也都很容易理解代碼。
            C++及模板都是強(qiáng)大的工具,但也容易做出過度設(shè)計(jì)  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2009-02-28 14:54 Kevin Lynx

            @劍神一笑

            你說的有道理。我也越來越喜歡UNIX的東西了。:)
              回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2010-04-12 00:05 anonymous

            原則就是用來違反的,不違反原則就不能進(jìn)步。  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2010-05-06 14:10 小時(shí)候可靚了

            來頂一個(gè)哦!  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2012-08-31 10:10 asdf

            對(duì)于bool, BOOL, BOOLEAN, 請(qǐng)問樓主如何解決……  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2013-09-12 17:06 yzm

            樓主屌爆了  回復(fù)  更多評(píng)論   

            # re: 實(shí)現(xiàn)自己的LUA綁定器-一個(gè)模板編程挑戰(zhàn) 2015-04-23 11:50 yk

            @Kevin Lynx

            為何要typedef lua_binder呢?

            binder中的共性已經(jīng)很好的抽象到adapter里面了,那么個(gè)性完全可以由成員變量來體現(xiàn)吧?

            直接辦法是去掉static,讓functor直接成為binder的一個(gè)成員
            然后bind的時(shí)候不在傳static那個(gè)指針了,binder定義一個(gè)對(duì)象出來,把這個(gè)對(duì)象傳給bind

            這么做不知道行不行?  回復(fù)  更多評(píng)論   

            www亚洲欲色成人久久精品| 无码人妻久久一区二区三区蜜桃| 99精品久久久久久久婷婷| 一本色综合久久| 999久久久国产精品| 日本人妻丰满熟妇久久久久久 | 日本高清无卡码一区二区久久| 亚洲中文字幕无码久久综合网| 久久这里只有精品首页| 无码人妻精品一区二区三区久久久 | 亚洲成色999久久网站| 97精品伊人久久久大香线蕉| 久久精品这里只有精99品| 99久久精品国产麻豆| 伊人久久大香线蕉综合影院首页| 久久青青国产| 精品无码久久久久久久动漫| 国产精品免费福利久久| 久久精品国产免费观看| 亚洲精品视频久久久| 精品国产青草久久久久福利| 久久久青草久久久青草| 国产精品久久久久无码av| 国内精品伊人久久久久AV影院| 久久精品国产99国产精品亚洲 | 72种姿势欧美久久久久大黄蕉| 国产精品久久新婚兰兰| 亚洲国产一成久久精品国产成人综合| 热久久这里只有精品| 青青热久久综合网伊人| 亚洲成人精品久久| 66精品综合久久久久久久| 中文字幕亚洲综合久久| 一本伊大人香蕉久久网手机| 伊人久久大香线蕉精品| 99久久精品免费| 久久激情亚洲精品无码?V| 久久精品成人免费观看97| 久久婷婷色综合一区二区| 午夜精品久久影院蜜桃| 无码国内精品久久综合88|