青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

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)有很多庫做了這件事情。但是,我們這里的需求很簡(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)庫中使用了一個(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è)語法錯(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 閱讀(7029) 評(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)論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久嫩草精品久久久精品一| 国产日韩一级二级三级| 久久人人爽人人爽| 久久av资源网| 亚洲最快最全在线视频| 久久精品99国产精品日本| 免费成人黄色片| 亚洲综合欧美| 亚洲精品欧美日韩专区| 伊人天天综合| 亚洲深夜影院| 99精品热视频| 欧美网站在线观看| 欧美日韩精品免费| 老鸭窝毛片一区二区三区| 欧美在线资源| 久热精品视频在线| 亚洲精品欧美| 欧美成人国产| 欧美极品在线视频| 久久综合色婷婷| 米奇777在线欧美播放| 欧美一区二区三区四区在线观看地址| 日韩视频在线播放| 欧美激情视频在线播放| 久久免费视频在线观看| 欧美中文字幕久久| 久久精品导航| 看片网站欧美日韩| 欧美国产免费| 亚洲美女免费精品视频在线观看| 亚洲精品无人区| 亚洲性视频h| 国产精品久久久久久久午夜| 国产一区二区三区视频在线观看| 精品动漫3d一区二区三区免费版| 国产中文一区二区三区| 亚洲高清在线播放| 亚洲视频中文字幕| 91久久精品国产91性色tv| 一区二区av在线| 欧美日韩国产美| 国产精品一区二区久久久| 国产精品日韩一区| 亚洲一区二区三区久久| 亚洲黄色免费| 欧美日本不卡视频| 亚洲一区在线播放| 亚洲免费在线视频| 国产精品普通话对白| 亚洲欧美日韩一区二区三区在线观看| 亚洲激情欧美| 国产精品久久久久久久久久尿| 久久精品卡一| 亚洲国产导航| 日韩一区二区免费看| 在线精品在线| 一二三区精品| 蜜桃av久久久亚洲精品| 91久久午夜| 噜噜噜噜噜久久久久久91 | 亚洲欧美日韩国产综合精品二区| 久久手机精品视频| 一卡二卡3卡四卡高清精品视频| 久久久人人人| 亚洲欧美日韩在线综合| 久久久久国产精品午夜一区| 99re6热只有精品免费观看| 免费不卡中文字幕视频| 一区二区三区精品在线| 国产精品视频xxxx| 久久久蜜桃一区二区人| 欧美日韩成人激情| 另类图片综合电影| 国产一区再线| 亚洲视频在线观看免费| 亚洲大片免费看| 欧美日韩视频免费播放| 亚洲综合日韩在线| 另类天堂视频在线观看| 香蕉av777xxx色综合一区| 国内揄拍国内精品少妇国语| 亚洲作爱视频| 亚洲性视频网站| 欧美搞黄网站| 久久激情综合网| 国产日韩成人精品| 日韩亚洲欧美综合| 久久在线免费观看| 亚洲精品中文字幕女同| 亚洲六月丁香色婷婷综合久久| 国产一区二区久久精品| 亚洲一区二区三区精品动漫| 亚洲视频一区二区免费在线观看| 国产精品第一页第二页第三页| 亚洲国产精品悠悠久久琪琪| 在线观看日韩av| 麻豆九一精品爱看视频在线观看免费| 亚洲一区二区三区高清不卡| 欧美午夜精品一区二区三区| 欧美专区在线播放| 亚洲一级一区| 久久福利毛片| 亚洲国产日韩欧美在线99 | 亚洲裸体视频| 欧美1区免费| 一区二区av在线| 久久久久国产免费免费| 亚洲美女黄网| 亚洲伦理网站| 亚洲高清二区| 亚洲精品中文字幕在线| 国产精品vvv| 久久激五月天综合精品| 欧美丰满少妇xxxbbb| 亚洲精品久久久久久久久久久久久| 欧美在线观看www| 日韩一级欧洲| 免费成人黄色av| 欧美成人午夜激情在线| 久久深夜福利| 亚洲激情成人在线| 亚洲午夜一二三区视频| 亚洲欧洲日本mm| 欧美体内she精视频| 老鸭窝毛片一区二区三区| 久久久精品999| 久久一区二区三区国产精品| 亚洲欧美中文在线视频| 亚洲高清视频一区| 欧美成熟视频| 欧美不卡在线视频| 欧美激情第1页| 亚洲精品视频在线观看网站| 99国产欧美久久久精品| 欧美在线亚洲在线| 午夜精品久久99蜜桃的功能介绍| 媚黑女一区二区| 麻豆国产精品777777在线 | 欧美另类在线观看| 久久视频一区| 久久久久久久综合| 欧美日韩视频一区二区| 永久91嫩草亚洲精品人人| 亚洲一区二区三区在线播放| 久久综合久久综合久久| 美女久久网站| 免费看的黄色欧美网站| 中国女人久久久| 欧美精品久久99| 亚洲国产精品激情在线观看| 久久久久久免费| 亚洲欧美日韩人成在线播放| 亚洲欧美影音先锋| 欧美日韩精品一区二区在线播放| 国产视频在线一区二区| 亚洲欧美一区二区三区久久| 乱中年女人伦av一区二区| 欧美一站二站| 国产亚洲欧洲一区高清在线观看| 亚洲国产精品久久久久久女王| 欧美在线播放高清精品| 欧美一级播放| **欧美日韩vr在线| 亚洲精品黄色| 久久综合九色综合欧美就去吻| 亚洲麻豆av| 欧美风情在线观看| 一区二区三区成人精品| 欧美国产一区视频在线观看| 欧美精品七区| 在线看片日韩| 亚洲国产成人在线| 欧美精品一二三| 久久精品亚洲乱码伦伦中文 | 亚洲国产日韩在线| 亚洲一线二线三线久久久| 日韩亚洲视频在线| 美日韩免费视频| 亚洲激情在线激情| 亚洲精品一线二线三线无人区| 国产精品chinese| 亚洲国产精品久久久久| 欧美激情综合亚洲一二区| 亚洲美女中文字幕| 亚洲性夜色噜噜噜7777| 亚洲激情av在线| 欧美淫片网站| 午夜精品国产更新| 久久精品一区二区三区中文字幕 | 久久久人成影片一区二区三区| 欧美一区二区三区四区视频| 亚洲国产日日夜夜| 午夜精品视频一区| 亚洲视频精选| 免费av成人在线| 一本一本久久| 亚洲免费av观看| 久久一区二区三区国产精品 | 亚洲综合色婷婷|