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

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*)類(lèi)型。這很不方便,對(duì)于上層程序員來(lái)說(shuō)更不方便。
    因此我們要做的是,實(shí)現(xiàn)一個(gè)綁定器,可以把任意prototype的函數(shù)綁定到LUA腳本當(dāng)中,并且封裝取參數(shù)和壓返回值時(shí)
的諸多細(xì)節(jié)。
    確實(shí),世界上已經(jīng)有很多庫(kù)做了這件事情。但是,我們這里的需求很簡(jiǎn)單,我們只需要綁定函數(shù),而不需要綁定C++類(lèi)之
類(lèi)的東西,自己實(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)單的例子,目的是展示我之前說(shuō)的局限性,以及,恩,丑陋性。


How

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

Implementing...

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

    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的值!綜合起來(lái),lua_bind函數(shù)主要作用就是接受用戶(hù)層函數(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)沒(méi)有我們想象的那么簡(jiǎn)單。adapter函數(shù)中毫無(wú)實(shí)現(xiàn),重要的是,該如何去實(shí)現(xiàn)?我們面對(duì)的首個(gè)問(wèn)題是:上層
函數(shù)參數(shù)個(gè)數(shù)不一樣,那么我們的adapter該調(diào)用多少次lua_to*去從LUA棧中獲取參數(shù)?
    解決該問(wèn)題的辦法是,恩,很笨,但是這可以工作:為不同參數(shù)個(gè)數(shù)的函數(shù)都實(shí)現(xiàn)一個(gè)對(duì)應(yīng)的adapter。沒(méi)有參數(shù)的函數(shù)對(duì)
應(yīng)一個(gè)adapter,一個(gè)參數(shù)的函數(shù)對(duì)應(yīng)另一個(gè)adapter,依次類(lèi)推。
    (穿插一下: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類(lèi)似于:

    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)在是否可以寫(xiě)下諸多的lua_to*函數(shù)去獲取參數(shù)了?你覺(jué)得可以嗎?假設(shè)現(xiàn)在要獲取棧頂?shù)谝粋€(gè)參數(shù),你
該調(diào)用lua_tonumber還是lua_tostring?
    問(wèn)題就在于,我們并不知道該調(diào)用哪個(gè)函數(shù)。
    解決辦法是:根據(jù)不同的參數(shù)類(lèi)型,調(diào)用對(duì)應(yīng)的lua_to*函數(shù)。
    不同的類(lèi)型擁有不同的行為,這一點(diǎn)讓你想起什么?那就是模板世界里的type traits,類(lèi)型萃取。我想,完成本文的
綁定器,更多的是對(duì)你模板編程能力的考驗(yàn)。
    lua_to*系列函數(shù)是有限的,因此我們也只需要實(shí)現(xiàn)幾個(gè)類(lèi)型的行為即可。我們這個(gè)時(shí)候的目的就是,根據(jù)不同的類(lèi)型,
調(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類(lèi)型太多,也許concept可以解決這個(gè)問(wèn)題),其他特化版本處理其他
類(lèi)型。這樣,在adapter里,就可以根據(jù)參數(shù)類(lèi)型獲取到相應(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ì)的問(wèn)題同取參數(shù)一樣,我
們需要根據(jù)不同的返回值類(lèi)型,調(diào)用對(duì)應(yīng)的lua_push*函數(shù)壓入返回值。
    同樣的type traits技術(shù),你應(yīng)該自己寫(xiě)得出來(lái),例如:

    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。是的,它表示沒(méi)有返回值。在用戶(hù)層函數(shù)返回值為void類(lèi)型時(shí)(原諒
這矛盾的說(shuō)法),我們這里需要返回0。
    你意識(shí)到了什么?是的,我們需要根據(jù)返回值類(lèi)型是否是void來(lái)設(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?

    我很高興我能流暢地寫(xiě)到這里,同樣我希望我不僅向你展示了某個(gè)應(yīng)用的實(shí)現(xiàn),而是展示了模板編程的思想。
    但是,問(wè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類(lèi)型。
    是的,好好省視下你的代碼,當(dāng)你的binder_type::result_type為void時(shí),在adapter函數(shù)中,你基本上也就寫(xiě)下了
void r = something 的代碼。這是個(gè)語(yǔ)法錯(cuò)誤。

    同樣的問(wèn)題還有:當(dāng)返回值是void時(shí),我們也沒(méi)有必要調(diào)用return_traits的set_result函數(shù)。
    我想你覺(jué)察出來(lái),又一個(gè)type traits技術(shù)。我們將根據(jù)result_type決定不同的處理方式。于是,我寫(xiě)了一個(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ù)不同的返回值類(lèi)型決定如何去回調(diào)_func。比較遺憾的是,我們需要為每一個(gè)lua_binder編寫(xiě)這么一個(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)題不帶問(wèn)號(hào),所以這就結(jié)束了。下載看看我的代碼吧,為了給不同參數(shù)個(gè)數(shù)的函數(shù)寫(xiě)binder,始終需要
粘貼復(fù)制的手工勞動(dòng)。
    值得注意的是,在最終的代碼里,我使用了
以前實(shí)現(xiàn)的functor,將函數(shù)類(lèi)型泛化。這樣,lua_binder就可以綁定類(lèi)
成員函數(shù),當(dāng)然,還有operator()。

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

    下載完整的lua_binder

posted on 2008-08-13 09:33 Kevin Lynx 閱讀(7029) 評(píng)論(15)  編輯 收藏 引用 所屬分類(lèi): 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),開(kāi)源之后,很少人會(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)[未登錄](méi) 2008-08-14 22:52 清風(fēng)徐來(lái)

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

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

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

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

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

1) 文中寫(xiě)道:

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è)類(lèi)型而沒(méi)有使用的話是不會(huì)產(chǎn)生任何代碼的。上面的那句 typedef 編譯器不會(huì)為 _func 分配存儲(chǔ)空間,我為此還特意寫(xiě)了個(gè)簡(jiǎn)單程序驗(yàn)證了一下。或者這里是指別的意思?

2) _func 被聲明為靜態(tài)類(lèi)型,意味著每種函數(shù)類(lèi)型只能bind一個(gè)實(shí)例函數(shù)?如果要bind 多個(gè)同類(lèi)型的函數(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
非常高興有人可以給我提出如此寶貴的意見(jiàn)!我覺(jué)得很少有人會(huì)把我寫(xiě)的東西認(rèn)真讀過(guò),甚至相關(guān)代碼。

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

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

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

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

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

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

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

@劍神一笑

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

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

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

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

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

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

對(duì)于bool, BOOL, BOOLEAN, 請(qǐng)問(wèn)樓主如何解決……  回復(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è)性完全可以由成員變量來(lái)體現(xiàn)吧?

直接辦法是去掉static,讓functor直接成為binder的一個(gè)成員
然后bind的時(shí)候不在傳static那個(gè)指針了,binder定義一個(gè)對(duì)象出來(lái),把這個(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精品国产一区二区青青牛奶| 欧美va亚洲va香蕉在线| 亚洲第一中文字幕在线观看| 免费欧美在线| 欧美刺激性大交免费视频| 久久久久久有精品国产| 久久国产精品毛片| 欧美一级免费视频| 久久国产精品久久久久久电车| 午夜精品美女久久久久av福利| 亚洲欧美乱综合| 欧美中文字幕精品| 久久嫩草精品久久久精品一| 久久久噜噜噜久噜久久| 免费不卡在线观看| 亚洲国产精品一区| 亚洲日本在线观看| 亚洲一区二区三区精品在线观看| 在线视频你懂得一区二区三区| 亚洲一区二区三| 久久乐国产精品| 欧美激情精品久久久久久免费印度| 欧美精品 国产精品| 国产精品国产三级国产普通话三级 | 狠狠色噜噜狠狠色综合久| 国语自产偷拍精品视频偷| 国产在线精品自拍| 亚洲精品国产系列| 亚洲宅男天堂在线观看无病毒| 欧美一级电影久久| 欧美r片在线| av72成人在线| 久久国产精品一区二区| 欧美日韩dvd在线观看| 国产精品日韩一区二区| 影音先锋中文字幕一区| 一区二区欧美精品| 老牛国产精品一区的观看方式| 亚洲欧洲日本国产| 午夜精品久久久久久久久久久久久| 亚洲美女免费视频| 亚洲欧美99| 宅男在线国产精品| 女女同性精品视频| 亚洲激情另类| 牛牛影视久久网| 欧美国产精品一区| 欧美88av| 亚洲电影免费在线| 日韩小视频在线观看专区| 亚洲欧美日韩精品一区二区| 一本一本久久a久久精品综合妖精| 亚洲美女啪啪| 亚洲欧美日韩一区| 久久久之久亚州精品露出| 欧美jizz19性欧美| 日韩午夜在线观看视频| 亚洲伊人网站| 你懂的一区二区| 国产精品五月天| 亚洲精品日韩在线观看| 午夜精品久久久久久久男人的天堂 | 国产亚洲精品bt天堂精选| 黄色成人av网| 一个色综合导航| 老司机精品久久| 亚洲综合999| 欧美猛交免费看| 在线成人av.com| 午夜亚洲伦理| 一本久道久久综合中文字幕| 美女免费视频一区| 国产亚洲欧美一级| 亚洲一区二区三区乱码aⅴ蜜桃女| 久久综合一区二区| aa国产精品| 免费观看成人网| 国产专区精品视频| 亚洲一区二区在线看| 亚洲国产日韩一区二区| 久久精品亚洲一区| 国产一区二区三区的电影 | 篠田优中文在线播放第一区| 久久精品国产清高在天天线| 亚洲日本一区二区| 美女久久一区| 国产综合久久久久久| 亚洲在线免费| 亚洲肉体裸体xxxx137| 久久婷婷人人澡人人喊人人爽 | 亚洲一区在线观看免费观看电影高清| 欧美成人精品一区二区三区| 国内精品模特av私拍在线观看| 亚洲伊人一本大道中文字幕| 亚洲福利在线看| 可以看av的网站久久看| 亚洲高清久久| 亚洲高清免费| 欧美成人按摩| 99精品视频网| 99一区二区| 国产精品免费视频观看| 欧美一区二区三区另类 | 亚洲国产美女久久久久| 亚洲第一综合天堂另类专| 久久久午夜精品| 亚洲高清久久网| 亚洲欧洲在线一区| 欧美日本不卡高清| 亚洲在线观看视频网站| 亚洲影院高清在线| 国内一区二区三区在线视频| 欧美成人自拍视频| 欧美人与性动交a欧美精品| 亚洲欧美成人一区二区三区| 午夜精品国产精品大乳美女| 国语自产精品视频在线看8查询8| 久久综合免费视频影院| 久久蜜桃香蕉精品一区二区三区| 亚洲国产精品一区二区www在线| 亚洲片在线资源| 国产美女精品视频| 牛夜精品久久久久久久99黑人| 欧美精品一区二区三区在线看午夜| 一区二区三区四区五区精品视频 | 亚洲一区二区三区四区中文| 国产日韩一区二区三区| 亚洲国产美女| 国产精品系列在线播放| 欧美96在线丨欧| 欧美日韩免费观看一区| 久久精品盗摄| 欧美精品久久天天躁| 香蕉成人伊视频在线观看| 农夫在线精品视频免费观看| 久久精品视频网| 亚洲黄色免费电影| 亚洲高清不卡在线观看| 久久综合九色欧美综合狠狠| 国产精品捆绑调教| 亚洲精品美女在线观看| 亚洲欧美999| 日韩亚洲在线观看| 久久国产精品72免费观看| 中文av字幕一区| 久热精品在线| 久久精品国产一区二区电影 | 亚洲电影在线| 欧美亚洲一区三区| 亚洲在线免费视频| 国产精品乱子久久久久| 久久久久久电影| 国产精品家庭影院| 日韩亚洲欧美一区| 亚洲精品国产精品久久清纯直播| 欧美一区二区性| 国产精品一区免费视频| 久久成人综合视频| 牛夜精品久久久久久久99黑人| 亚洲精品中文字幕在线观看| 午夜在线电影亚洲一区| 国产在线高清精品| 小嫩嫩精品导航| 久久久999精品免费| 国产精品国产三级国产专播品爱网 | 国产精品久久久久9999| 中文网丁香综合网| 国产一区二区0| 日韩视频亚洲视频| 亚洲国产欧洲综合997久久| 午夜精品国产| 亚洲国产日韩在线一区模特| 日韩视频免费在线| 国产伦精品一区二区三区四区免费| 亚洲成色www8888| 黄色欧美日韩| 亚洲免费高清视频| 一区二区三区视频在线| 欧美一二三视频| 91久久精品国产91性色tv| 日韩写真在线| 亚洲精品永久免费| 欧美va天堂在线| 亚洲国产精品专区久久| 亚洲最新色图| 亚洲清纯自拍| 红桃视频成人| 国产午夜精品一区二区三区欧美 | 噜噜噜久久亚洲精品国产品小说| 亚洲午夜国产成人av电影男同| av成人国产|