• <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>
            隨筆-90  評論-947  文章-0  trackbacks-0

            目錄:

            C++ 下 Function 對象的實(shí)現(xiàn)(上)
            C++ 下 Function 對象的實(shí)現(xiàn)(下)

            起因在上一篇已經(jīng)說過了。現(xiàn)在讓我們直接進(jìn)入主題。本文的目標(biāo)是,讓以下代碼能順利跑起來:

            int intfun0()
            {
                return 1;
            }

            struct _intfunctor0
            {
                int operator()()
                {
                    return 2;
                }

            } intfunctor0;

            struct Test
            {
                int intmem0()
                {
                    return 3;
                }

            } test;

            int main()
            {
                Function<int ()> f1(&intfun0);
                Function<int ()> f1_(intfun0);
                Function<int ()> f2(intfunctor0);
                Function<int ()> f3(&test, &Test::intmem0);

                f1();
                f1_();
                f2();
                f3();

                return 0;
            }

            除了上述例子中顯示的,還要支持有返回值的函數(shù)和沒返回值的函數(shù),以及有0個(gè)、1個(gè)、2個(gè)、……、MAX 個(gè)參數(shù)的函數(shù),參數(shù)類型無限制。最后實(shí)現(xiàn)的 Function 對象僅僅可以執(zhí)行就好。(至于是否可拷貝、是否可判斷相等 等問題,都是小事,本文暫不考慮。)最后,Bind 概念也不在本文討論范圍之內(nèi)。

            對于這個(gè)問題,我們一開始考慮的可能是怎樣統(tǒng)一三種不同形式。有兩個(gè)選擇,第一,使用 C++ 的多態(tài)機(jī)制,最后統(tǒng)一到基類指針的類型;第二,允許類內(nèi)部有冗余變量以及必要的 Flag,用于判斷是哪種形式的函數(shù),要如何執(zhí)行。這樣看起來,第一種方案比第二種爽一點(diǎn)。于是,最初想到的實(shí)現(xiàn)有可能是這樣的:

            先定義一個(gè)虛基類:

            template <typename R>
            class FunctionBase0
            {
            public:
                virtual R Invoke() = 0;
                virtual ~FunctionBase0() {}
            };

            然后實(shí)現(xiàn)一個(gè)普通函數(shù)/仿函數(shù)的版本:

            template <typename R, typename T>
            class Function0 : public FunctionBase0<R>
            {
            public:
                R Invoke()
                {
                    return m_Fun();
                }

            public:
                Function0(const T &fun)
                    : m_Fun(fun)
                {

                }

            private:
                T m_Fun;
            };

            這里需要說明的是,如果是普通函數(shù),T會被特化成 R() 或者 R (&)() 或者 R(*)(),取決于使用的時(shí)候傳入 fun 還是傳入 &fun。所以不必另外實(shí)現(xiàn)針對 R(*)() 的版本。Loki (姑且就以作品名稱乎 Loki 的作者吧,他那個(gè)真名實(shí)在是太長)在他的書中稱之為“做一個(gè),送一個(gè)”。不過對于他書中所說的,我有一個(gè)疑惑。Loki 說傳入 fun,模版參數(shù) T 會被特化成 R (&)(),于是一切順利。可是我在操作過程中發(fā)現(xiàn) T 一直被特化成 R (),于是上述 class 中的 m_Fun 被認(rèn)為是成員函數(shù)而不是成員變量。不知道是為什么,有知道者請不吝指教哈。因?yàn)橐陨显颍疚闹形乙恢庇?&fun 的形式對待普通函數(shù)。

            再實(shí)現(xiàn)一個(gè)成員函數(shù)的版本:

            template <typename R, typename T>
            class MemberFunction0 : public FunctionBase0<R>
            {
            public:
                R Invoke()
                {
                    return (m_pObj->*m_pMemFun)();
                }

            public:
                MemberFunction0(T *pObj, R (T::*pMemFun)())
                    : m_pObj(pObj), m_pMemFun(pMemFun)
                {

                }

            private:
                R (T::*m_pMemFun)();
                T *m_pObj;
            };

            最后是一個(gè)包裝類。如果你可以接受 Function<int> 表示 int(), Function<int, int> 表示 int (int),…,那么這里沒有多少技巧可言。boost 的那個(gè) function 使用的是函數(shù)簽名作為模版參數(shù),即 Function<int()>,F(xiàn)unction<int (int)> 等形式。如果不太研究語法,可能會像我一樣,一開始會對尖括號里的 int (int) 之類的玩意兒不太熟悉,覺得很牛逼。可是了解了以后,不過是個(gè)函數(shù)類型而已,沒什么大不了的。Loki 的 Functor 的使用方式是 Functor<int, TYPELIST_0()>,F(xiàn)unctor<int, TYPELIST_1(int)>。其中第一個(gè)模版參數(shù)始終是返回值,第二個(gè)模版參數(shù)是參數(shù)類型列表,Loki 使用了他創(chuàng)造的玩意兒 TypeList 使得所有函數(shù)參數(shù)只占一個(gè)坑,這在等下的支持多參數(shù)的擴(kuò)展中能夠帶來一些美觀。我比較喜歡 boost 的使用方式,讓使用者直接以語言規(guī)定的形式填入函數(shù)簽名,而不是一些額外的約定(“第一個(gè)模版參數(shù)表示返回值”,“第二個(gè)到最后的模版參數(shù)表示參數(shù)”,“第二個(gè)模版參數(shù)以 TypeList 形式表示函數(shù)參數(shù)”等)。

            為了達(dá)到這個(gè)目標(biāo),我們要玩一些偏特化技巧。關(guān)于偏特化,我一直以來的膚淺認(rèn)識都是錯(cuò)誤的。我原以為,對于模版類:

            template <typename T0, typename T1>
            class Foo;

            我如果特化其中一個(gè)參數(shù) T1:

            template <typename T0>
            class Foo<T0, int>
            {

            }

            我以為只有這樣才叫偏特化,以為偏特化的過程總是減少模版參數(shù)的。而實(shí)際上,只要用某個(gè)/些類型占據(jù)原始模版參數(shù)的位置,就可以了。比如,對于上述 Foo,我可以特化一個(gè) class<T0, std::map<U0, U1>>,消去一個(gè) T1,而新增 U0、U1:

            template <typename T0, typename U0, typename U1>
            class Foo<T0, std::map<U0, U1>>
            {

            }

            原來 T1 的位置被 std::map<U0, U1> 占據(jù)了,這也是偏特化。當(dāng)然最后的模版參數(shù)數(shù)量也可以不變,如:

            template <typename T0, typename U>
            class Foo<T0, std::vector<U>>
            {

            }

            以及

            template <typename T0, typename U>
            class Foo<T0, U*>
            {

            }

            其中后者是實(shí)現(xiàn)類型萃取的主要方式。只要特化以后,這個(gè)類依然帶有至少一個(gè)模版參數(shù),就是偏特化。如果最后產(chǎn)生了 template<> 的形式,那就是完全特化。

            回到我們剛才的主題,我們要提供給用戶的是這樣一個(gè)類:

            template <typename Signature>
            class Function;

            其中參數(shù) Signature 會被實(shí)際的函數(shù)類型所特化。但是我們只知道整體的一個(gè) Signature 并沒有用,我們必須知道被分解開來的返回值類型、參數(shù)類型。于是,引入一個(gè)偏特化版本:

            template <typename R>
            class Function<R ()>

            這里使用 R () 特化原始的 Signature,引入一個(gè)新的參數(shù) R。于是返回值類型 R 就被萃取出來了。實(shí)現(xiàn)如下:

            template <typename R>
            class Function<R ()>
            {
            public:
                template <typename T>
                Function(const T &fun)
                    : m_pFunBase(new Function0<R, T>(fun))
                {
                   
                }

                template <typename T>
                Function(T *pObj, R (T::*pMemFun)())
                    : m_pFunBase(new MemberFunction0<R, T>(pObj, pMemFun))
                {

                }

                ~Function()
                {
                    delete m_pFunBase;
                }

                R operator ()()
                {
                    return m_pFunBase->Invoke();
                }

            private:
                FunctionBase0<R> *m_pFunBase;
            };

            如果對上面說的“普通函數(shù)的使用方式必須是函數(shù)指針而不是函數(shù)本身”耿耿于懷,可以再引入一個(gè)的構(gòu)造函數(shù):

            typedef R (FunctionType)();

            Function(const FunctionType &fun)
                : m_pFunBase(new Function0<R, FunctionType &>(fun))
            {

            }

            這里 FunctionType 是 R(&)() 類型,強(qiáng)制使用它來特化 Function0 中的 T。該構(gòu)造函數(shù)在重載決議中會取得優(yōu)先權(quán)從而使普通函數(shù)本身的傳入成為可能。不過,以函數(shù)本身形式傳入的普通函數(shù)會喪失一些特性,比如 Function<int()> 只能接受 int() 類型的普通函數(shù)而不能接受 char () 型的普通函數(shù),因?yàn)檫@種情況下不會走我們剛才新定義的構(gòu)造函數(shù)。

            還有一種做法,就是針對全局函數(shù),強(qiáng)制特化出模版參數(shù)為其引用類型的類。定義如下元函數(shù):

            template <typename Signature>
            struct FunctionTraits
            {
                typedef Signature ParamType;
            };
               
            template <typename RetType>
            struct FunctionTraits<RetType ()>
            {
                typedef RetType (&ParamType)();
            };

            然后構(gòu)造函數(shù)改為:

                template <typename T>
                Function(const T &fun)
                    : m_pFunBase(new Function0<R, typename FunctionTraits<T>::ParamType>(fun))
                {
                   
                }

            用以上方法,所有的特性都不會丟失。

            到這兒,我們的 Function 已經(jīng)可以小試牛刀了:

            Function<int ()> f1(&intfun0);

            Function<int ()> f1_(intfun0);
            Function<int ()> f2(intfunctor0);
            Function<int ()> f3(&test, &Test::intmem0);

            f1();
            f1_();
            f2();
            f3();

            上面這段代碼已經(jīng)能夠正常運(yùn)行了。

            來,繼續(xù)做一個(gè),送一個(gè)。下面的代碼居然也能跑(voidfun0、voidfunctor0、Test::voidmem0類似int版本定義):

            Function<void ()> f4(&voidfun0);
            Function<void ()> f4_(voidfun0);
            Function<void ()> f5(voidfunctor0);
            Function<void ()> f6(&test, &Test::voidmem0);

            f4();
            f4_();
            f5();
            f6();

            這說明了,在類里面寫一個(gè)返回值為該類型的函數(shù),并在里面寫下 return XXX; 然后以 void 為模版參數(shù)傳入該模版類,是符合語法的。驗(yàn)證一下:

            template <typename T>
            class Foo
            {
            public:
                T Bar()
                {
                    printf("%s invoked\n", __FUNCTION__);
                    return T();
                }
            };

            int main()
            {
                Foo<void> f1;
                f1.Bar();

                Foo<int> f2;
                int i = f2.Bar();

                return 0;
            }

            運(yùn)行結(jié)果:

            Foo<void>::Bar invoked
            Foo<int>::Bar invoked

            到此為止,我們已經(jīng)實(shí)現(xiàn)了 0 個(gè)參數(shù)的函數(shù)支持,也即 R () 類型的所有函數(shù)的支持。接下來還要實(shí)現(xiàn)對具有 1 個(gè)、2 個(gè)、3 個(gè)直至任意有限個(gè)參數(shù)的函數(shù)支持。也許您也發(fā)現(xiàn)了,接下來的工作可以是體力活,我們可以照葫蘆畫瓢,搞出一堆 FunctionBaseN、FunctionN、MemberFunctionN,并在最后的 Function 中再實(shí)現(xiàn) N 個(gè)偏特化版本。是,不錯(cuò),大致上原理就是這樣。限于篇幅,我想暫時(shí)寫到這里,下篇將繼續(xù)談?wù)労辍ypeList,以及怎樣少花點(diǎn)力氣實(shí)現(xiàn)其余 N 個(gè)版本。最終達(dá)到的效果是,只要改一個(gè)宏定義,就可以提高參數(shù)上限。

            在本文所涉及的內(nèi)容中,我比較糾結(jié)的是,可否在不用多態(tài)機(jī)制的情況下達(dá)到比較優(yōu)雅的形式統(tǒng)一?

            歡迎討論。

            posted on 2011-01-16 22:17 溪流 閱讀(7302) 評論(55)  編輯 收藏 引用 所屬分類: C++

            評論:
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-16 22:32 | zhaoyg
            幫頂  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-16 22:43 | cexer
            不說MFC那種陳舊的消息機(jī)制,用boost::function,boost::bind,boost::signal之類的來實(shí)現(xiàn)消息機(jī)制,也算有點(diǎn)out了,而且這種實(shí)現(xiàn)已經(jīng)有框架使用(比如SmartWin++),不再具有新意了,其實(shí)強(qiáng)大的C++可以完成遠(yuǎn)比它們更高級的功能,列舉三點(diǎn):

            1 框架可以給同一個(gè)消息源添加任意多個(gè)個(gè)完全不同的消息處理器,類似函數(shù)重載,可以將這個(gè)功能命名為處理器重載。僅僅是類似boost:function和boost:signal那種固定簽名的工具,是無法完成這樣的功能的,如下所示:

            // 自由函數(shù)
            void global_handler(){}

            // 一段腳本
            Script script_handler = create_script( /*這是一段腳本*/ );

            // 系統(tǒng)函數(shù)
            int WINAPI MessageBox( HWND,LPCTSTR,LPCTSTR,UINT );

            // 具有不同參數(shù)的成員函數(shù)
            viod Class:memeber_handler_void(){}
            void Class::member_handler_size( Size size ){}
            void Class::member_handler_int( int x,int y ){}

            // 消息映射
            Window window;
            Class object;
            window.on_resized += message_handler( global_handler );
            window.on_resized += message_handler( script_handler );
            window.on_resized += message_handler( &Class::member_handler_void,&object );
            window.on_resized += message_handler( &Class::member_handler_size,&object );
            window.on_resized += message_handler( &Class::member_handler_int ,&object );
            window.on_resized += message_handler( &::MessageBox,NULL,"Hello World",MB_OK );


            2 框架能夠進(jìn)行返回值檢查(眾所周知的,在windows消息系統(tǒng)中,很多消息需要進(jìn)行特殊的返回值檢查,強(qiáng)WM_ERASEBKGND之類的),比如一個(gè)closing消息,如果處理器返回非void類型,并且能夠強(qiáng)制轉(zhuǎn)換為true,則表明它同意關(guān)閉窗口,如果能夠強(qiáng)制轉(zhuǎn)換為false,則表示它阻止框架進(jìn)行關(guān)閉,如果處理器返回值類型是void,則表示它不關(guān)心關(guān)閉與否,讓框架進(jìn)行默認(rèn)處理。這個(gè)功能可以命名為返回值智能檢測。如下所示:

            // 這個(gè)處理器不關(guān)心窗口是否要關(guān)閉
            void handle_closing_dont_care( Window* window )
            {
            }

            // 這個(gè)處理器告訴框架不要關(guān)閉
            LRESULT handle_closing_no()
            {
            return 0;
            }

            // 這個(gè)處理器告訴框架進(jìn)行關(guān)閉
            bool handle_closing_yes()
            {
            return true;
            }

            // 消息映射,窗口最終是否關(guān)閉取決于最后一個(gè)處理器的決定。
            Window window
            window.on_closing += message_handler( handle_closing_dont_care );
            window.on_closing += message_handler( handle_closing_no );
            window.on_closing += message_handler( handle_closing_yes );


            3 框架提供消息分解者,允許使用者自己組合自己的處理器參數(shù),類似boost::asio的handler綁定bytes_transfered和error_code的方式,可以把這個(gè)功能命名為消息參數(shù)任意組合,比如同一個(gè)消息resized,有的處理器需要當(dāng)前窗口的大小,有的處理器需要當(dāng)前窗口的標(biāo)題,有的處理器同時(shí)需要兩者,則消息映射時(shí)手動組合消息分解者即可。更進(jìn)一步,對于某些預(yù)注冊的簽名,框架可以智能提供分解者,免去使用者手動提供的麻煩,例如簽名 (Window*,Size) 已經(jīng)預(yù)先注冊,則具有這個(gè)簽名的處理器不用手動指定消息分解器
            示例代碼如下:

            // 這個(gè)處理器需要當(dāng)前窗口大小
            void handle_resized_size( Size size ){}

            // 這個(gè)處理器需要窗口標(biāo)題
            void handle_resized_text( String caption ){}

            // 這個(gè)處理器都需要
            void handle_resized_both( Size size,String caption )


            // 消息映射的時(shí)候,同時(shí)指示自己需要什么參數(shù)
            Window window
            window.on_resized += message_handler( handle_resized_size,Window::Argument::size )
            window.on_resized += message_handler( handle_resized_text,Window::Argument::text )
            window.on_resized += message_handler( handle_resized_both,Window::Argument::size,Window::Argument::text )


            void handle_resized( Window* window,Size size ){}
            window.on_resized += message_handler( handle_resized );

            4 但是。。。。。在C++.0x.lamda的面前,以上一切都成了浮云。把lamda作為消息處理器,它自身就同時(shí)提供了以上1,2,3的所有優(yōu)點(diǎn),簡潔優(yōu)雅使用方便,物美價(jià)廉童叟無欺,以上例說明:

            // 這個(gè)處理器需要當(dāng)前窗口大小
            window.on_resized += []( Size size ){
            }( window::Argument::size );

            // 這個(gè)處理器需要窗口標(biāo)題
            window.on_resized += []( String caption ){
            }( Window::Argument::text );

            // 這個(gè)處理器都需要
            window.on_resized += []( Size size,String caption ){
            }( window::Argument::size,Window::Argument::text );

            我正在寫的就是這么一個(gè)框架,上面列舉的消息機(jī)制都完成了,正在進(jìn)行博主所指第4步封裝控件和第5步(寫文檔,作為一個(gè)目標(biāo)是開源的框架,是最重要的步驟),目前代碼已經(jīng)有兩萬多行,消息機(jī)制的其實(shí)占很少部分,大多代碼是控件的封裝。

              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-17 12:37 | 飛舞的煙灰缸
            @cexer
            function的多播實(shí)現(xiàn)還是有點(diǎn)麻煩。
            += 倒是簡單,但是要實(shí)現(xiàn) -=這樣的操作就需要判斷function相等,麻煩就比較大了  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-17 12:52 | 溪流
            @飛舞的煙灰缸
            求不借助于typeid的function相等的判別方法~  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-17 12:53 | 溪流
            @cexer
            很有啟發(fā)性的提示,謝謝分享~!頂了再細(xì)讀。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 02:01 | OwnWaterloo
            typelist 不給力的, 它沒辦法展開為一個(gè)參數(shù)列表。
            最終還是需要boost.pp 那樣的東西。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 02:17 | OwnWaterloo
            借寶地請教一下~ 對于handler, 是否需要如此的動態(tài)?

            A. 完全動態(tài)

            比如, 一個(gè)具體的 window 類。
            window w; // 具體的一個(gè)類
            w.add_handler( ON_CREATE, my_on_create, ... ); // 動態(tài)的添加一些handler。

            具體語法不重要, w.add_handler<ON_CREATE>(my_on_create, ...);
            關(guān)鍵點(diǎn)是, window是一個(gè)具體類, 動態(tài)的(運(yùn)行時(shí)而非編譯時(shí))添加一些handler。

            if (some_condition)
            /*run time*/w.add_handler( ON_CREATE, my_on_create, ... );


            B. 完全靜態(tài)

            與之相對的, 是編譯時(shí)就決定一個(gè)類型, 比如 my_window。
            然后讓它處理 ON_CREATE。 如何讓W(xué)ndProc分派到這個(gè)handler同樣不是重點(diǎn)。
            重點(diǎn)是my_window的每個(gè)instance肯定都會處理這個(gè)event, 沒有例外。


            C. 兩者結(jié)合, 動態(tài)部分自己實(shí)現(xiàn)。

            兩者的折中可能是這樣:
            1. 依然是一個(gè)具體的, 編譯時(shí)決定處理哪些event的window類, 比如叫y_window
            2. 在設(shè)計(jì)y_window時(shí), 認(rèn)為有"部分"(而非window那樣的全部)行為需要在運(yùn)行時(shí)定制
            3. 在event中留一些stub, 用于運(yùn)行時(shí)添加。

            比如 y_window::on_create( param ){
            /*同樣, wndproc 如何分派到這里不重要*/
            /**/for (it=this->on_create_handlers.begin(); it!=this->on_create_handlers.end(); ++it) *it(param);
            }
            y_window::add_on_create( f , ... ) {
            /**/this->on_create_handlers.push_back(f, ... );
            }


            如上, 需要A那種靈活性么? 缺之不可?
            因?yàn)槲易约捍_實(shí)沒寫過幾個(gè)gui程序, "作為一個(gè)庫的用戶", 沒有足夠的這方面的使用經(jīng)驗(yàn)。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 11:47 | yrj
            @OwnWaterloo
            動態(tài)方式解耦庫的實(shí)現(xiàn)與庫的用戶,可看看下面這篇文章
            http://www.elpauer.org/stuff/a_deeper_look_at_signals_and_slots.pdf  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 16:53 | zhaoyg
            "可是我在操作過程中發(fā)現(xiàn) T 一直被特化成 R (),于是上述 class 中的 m_Fun 被認(rèn)為是成員函數(shù)而不是成員變量"

            這個(gè)會不會是編譯器理解成你試圖在定義一個(gè)函數(shù)的變量,于是才...


              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 16:56 | zhaoyg
            如果 m_Fun是T* 而不是T , 可能就沒有這個(gè)問題了  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 17:28 | cexer
            @OwnWaterloo
            【借寶地請教一下~ 對于handler, 是否需要如此的動態(tài)?
            A. 完全動態(tài)
            B. 完全靜態(tài)
            C. 兩者結(jié)合, 動態(tài)部分自己實(shí)現(xiàn)。
            如上, 需要A那種靈活性么? 缺之不可?
            因?yàn)槲易约捍_實(shí)沒寫過幾個(gè)gui程序, "作為一個(gè)庫的用戶", 沒有足夠的這方面的使用經(jīng)驗(yàn)。】


            說說我的看法:A并不是必須的,比如說MFC,可以算是完全靜態(tài)的,其所有的消息映射代碼都是用宏堆死的選擇語句,提供的動態(tài)功能基本上無,優(yōu)勢很明顯,一個(gè)類的所有實(shí)例共享一份映射數(shù)據(jù),具有空間優(yōu)勢。但是一個(gè)獨(dú)立的對象不能有它自己處理事件的方式,這明顯不是面向?qū)ο蟮姆庋b方式,運(yùn)行時(shí)靈活性大打折扣,這樣說起來很模糊,仍然舉例MFC來說明:

            比如說使用MFC運(yùn)行時(shí)創(chuàng)建的一個(gè)按鈕,怎么樣才能把這個(gè)按鈕的消息加到消息映射里去呢?對不起,不能。因?yàn)榘粹oID是動態(tài)生成的,那些映射宏什么的到運(yùn)行時(shí)都成了浮云。解決辦法當(dāng)然也有:回到原始時(shí)代--要么在父窗口的消息回調(diào)函數(shù)里檢查WM_COMMAND和BN_CLICK,二是任勞任怨地去自己重寫一個(gè)按鈕類,并在自己的消息回調(diào)里響應(yīng)WM_COMMAND的反射消息并檢測BN_CLICK,然后進(jìn)行處理--看看有多麻煩。

            所以我覺得一個(gè)好的庫,應(yīng)該能夠同時(shí)提供兩者,就是你說的C。同時(shí)支持靜態(tài)和動態(tài)的映射方式,關(guān)鍵是庫要實(shí)現(xiàn)得好,在沒有使用動態(tài)映射的時(shí)候,這個(gè)動態(tài)映射的功能就好像不存在一樣,這樣大多數(shù)時(shí)候既能夠享受空間優(yōu)勢,在需要的時(shí)候又能發(fā)揮它的動態(tài)優(yōu)勢。免得用戶要在在空間優(yōu)勢和靈活性之間做出痛苦的選擇。
              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 17:38 | cexer
            @飛舞的煙灰缸
            【function的多播實(shí)現(xiàn)還是有點(diǎn)麻煩。
            += 倒是簡單,但是要實(shí)現(xiàn) -=這樣的操作就需要判斷function相等,麻煩就比較大了】

            我想你指的麻煩,可能是你想實(shí)現(xiàn)這樣的功能:
            // 加入
            on_event += message_handler( handler );
            // 刪除
            on_event -= message_handler( handler );

            // 加入
            on_event += message_handler ( &Window::handle_resized,this );
            // 刪除
            on_event -= message_handler ( &Window::handle_resized,this );

            這樣的方式實(shí)現(xiàn)其實(shí)也不難,但是絕對需要 RTTI 那種東西的支持,另外還有一個(gè)問題就是這樣的方式使用用起來太麻煩,比如:

            // 加入
            on_event += message_handler ( &Window::handle_resized,this );
            // 刪除
            on_event -= message_handler ( &Window::handle_resized,this );

            如果庫提供了類似 boost::asio 那種參數(shù)綁定的功能,看看有多麻煩:
            // 加入
            on_event += message_handler ( &Window::handle_resized,this,Argument::size,Argument::caption );
            // 刪除
            on_event -= message_handler ( &Window::handle_resized,this,Argument::size,Argument::caption );


            其實(shí)這個(gè)功能有比你個(gè)簡單得多的實(shí)現(xiàn)方式,給一個(gè) handler 綁定一個(gè)名字,到時(shí)刪除這個(gè)名字即可,見下例:
            // 加入
            on_event += message_handler ( name,handler );
            // 刪除
            on_event -= message_handler( name );


              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 17:59 | cexer
            實(shí)現(xiàn)一個(gè)functor,除了眾所周知的功能,我建議樓主再考慮以下這兩點(diǎn):

            1 給返回void(無返回值)的functor綁定一個(gè)有返回值的可調(diào)用體,這個(gè)貌似boost::function是支持的。這功能是需要的,因?yàn)閷?shí)際上很多時(shí)候,我們的functor不關(guān)心返回值,就好像某個(gè)消息無論你返回什么系統(tǒng)都執(zhí)行相同的操作,這種情況下,如果綁定的可調(diào)用體返回了什么值,functor應(yīng)該檢測到并使用某種屏蔽手段,而不是報(bào)錯(cuò)“對不起,返回類似不匹配”。

            2 給返回非void(有返回值)的functor綁定一個(gè)返回void(無返回值)的可調(diào)用體,這個(gè)貌似boost::function不支持。這功能是需要的,因?yàn)橛袝r(shí)候functor需要一個(gè)明確的返回值,就像WM_ERASEBKGND消息,根據(jù)返回TRUE或FALSE的不同,系統(tǒng)會決定是否自己來畫窗口背景。如果可調(diào)用體返回void(無返回值),那么functor應(yīng)該檢測到它,并在返回時(shí)提供一個(gè)默認(rèn)的返回值,而不是在綁定時(shí)就報(bào)錯(cuò)“對不起,返回類似不匹配”。

            以上兩點(diǎn)都可以通過模板元編程實(shí)現(xiàn),通過typetraits檢測返回值,并根據(jù)functor和可調(diào)用體之兩者返回值的不同組合,來選擇不同的返回策略。

            另外還有,如果想綁定系統(tǒng)函數(shù)如Windows API,或者其它第三方提供的函數(shù),你還需要考慮調(diào)用約定的問題,因?yàn)?__stdcall,__cdecl,__fastcall的不同,都會使函數(shù)簽名不同。有多少種調(diào)用約定,functor的綁定函數(shù)的數(shù)量就需要乘以多少,這是個(gè)體力活,可以用預(yù)處理元和文件包含來減少體力消耗。
              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 18:27 | OwnWaterloo
            @cexer
            感謝感謝~ 果然是經(jīng)驗(yàn)豐富的大神。

            我想說的就是對象模型不同導(dǎo)致的"空間和效率"上的差異。
            C++ 其實(shí)不是"面向?qū)ο?quot;語言, 而是"面向類" 語言。
            真正的面向?qū)ο笳Z言, 每個(gè)instance都是相互獨(dú)立的。
            而C++,每一個(gè)type的所屬instance共享一部分行為。


            C++其實(shí)就是將"可能擁有一組相同行為"的instance們, 歸屬到同一個(gè)類里,
            共享許多信息, 并提高空間與時(shí)間效率。
            但是, 當(dāng)instance們的行為復(fù)雜(靈活)到一定層次時(shí), 極端情況, 就是一個(gè)class, 只需要一個(gè)instance。


            這時(shí)候, 如果依然遵守靜態(tài)類型的桎梏, 為每種行為組合編寫一個(gè)類, 就會非常繁瑣, 遠(yuǎn)不如動態(tài)類型語言來得方便。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 18:37 | OwnWaterloo
            @cexer
            上面所說的"一個(gè)類只需要產(chǎn)生一個(gè)instance"是非常極端的情況。
            實(shí)際上是否真的會產(chǎn)生這樣的情況?


            比如那個(gè)動態(tài)產(chǎn)生button的問題。
            如果依然遵守靜態(tài)類型的方式, 既然"預(yù)見" 會產(chǎn)生多個(gè)button, 是否可以先在on_command里留下stub, 然后把on_command這部分動態(tài)化。
            每個(gè)button被按下的行為不同, 但這些行為是否依然是"可預(yù)見"的?
            是否依然可以將它們歸入到同一類型中, 比如使用類多態(tài)機(jī)制?


            "A完全動態(tài)" 的方案給我的感覺是: 這已經(jīng)不是在用C++, 而是在C++中實(shí)現(xiàn)一個(gè)微型動態(tài)類型系統(tǒng), 然后用動態(tài)類型的方式編程。
            如果是我個(gè)人作出選擇的話, 當(dāng)需要這種靈活性時(shí), 直接把C++丟了, 選其他動態(tài)類型語言來編程-_-  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 18:39 | 溪流
            @zhaoyg
            是的,T*自然沒問題,就是為了同時(shí)也支持傳入 T。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 18:51 | OwnWaterloo
            @cexer
            >> 同時(shí)支持靜態(tài)和動態(tài)的映射方式,關(guān)鍵是庫要實(shí)現(xiàn)得好,在沒有使用動態(tài)映射的時(shí)候,這個(gè)動態(tài)映射的功能就好像不存在一樣,這樣大多數(shù)時(shí)候既能夠享受空間優(yōu)勢,在需要的時(shí)候又能發(fā)揮它的動態(tài)優(yōu)勢。免得用戶要在在空間優(yōu)勢和靈活性之間做出痛苦的選擇。


            說得我口水都流了…… (其實(shí)是開飯了~)
            有沒有機(jī)會能先睹為快呢~


            假設(shè), 這部分能做到上述所說。
            但還是有別的問題。

            比如參數(shù)綁定(boost.bind), 在其他范疇里叫partial apply。
            其他語言要實(shí)現(xiàn)這個(gè)簡直太容易了, 語言直接支持, 不需要一個(gè)額外的庫。
            也不需要關(guān)心被bind的參數(shù)的生命周期問題。

            比如匿名函數(shù), boost.lambda應(yīng)該也是有不少限制的, C++0x目前也不算太成熟 —— 還有這么多人用VC6呢…… 能讓他們進(jìn)化到VC8就很感謝了, VC10感覺是奢望。


            還有你上面提到"給handler一個(gè)名字, 以方便從handler們中刪除" —— 這完全就是動態(tài)類型語言常用的方式嘛……
            比如lua:
            handlers = {}
            添加:
            handlers[name] = handler
            調(diào)用:
            for _,v in pairs(handlers) do v( ... ) end
            刪除:
            handlers[name] = nil


            還有樓主提到的typelist, 其實(shí)是無法展開為參數(shù)列表的。
            這功能在動態(tài)類型語言里也太容易了……
            最簡單的元編程方式, 就是產(chǎn)生所需代碼的字符串, 然后加載之……


            總之呢, 當(dāng)需要靈活性時(shí), C++就缺這缺那的, 什么都要自己去造……
            造的時(shí)候, 又有各種問題。


            造的時(shí)候肯定是很有成就感的, 編程的樂趣啊!
            但就我個(gè)人而言, 已經(jīng)懶惰了……  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 18:58 | OwnWaterloo
            @cexer
            來說一個(gè)具體的C++問題吧, 就是這個(gè):
            >> 你還需要考慮調(diào)用約定的問題,因?yàn)?__stdcall,__cdecl,__fastcall的不同,都會使函數(shù)簽名不同。有多少種調(diào)用約定,functor的綁定函數(shù)的數(shù)量就需要乘以多少,這是個(gè)體力活,可以用預(yù)處理元和文件包含來減少體力消耗。

            絕對的體力活啊! 干過一次絕對不想干第二次……

            而且, 你有試過g++么?
            g++中這問題很麻煩。
            函數(shù)重載目前是通過mangling實(shí)現(xiàn)的, 而g++對一個(gè)函數(shù)(free or member)指針類型的mangling只包含普通的signature, 不包含調(diào)用約定。

            偽代碼:
            binder bind( __cdecl f, arg )
            binder bind( __stdcall f, arg )

            倆f在g++中是不同類型, boost::is_same是false。
            但這倆bind函數(shù)"不構(gòu)成重載" , 是bind的重復(fù)定義……
            原因就是上面提到的, f被mangling時(shí), 它的調(diào)用約定信息被忽略了。
            倆bind最終產(chǎn)生的符號是相同的。


            用C++, 就要和這些許多細(xì)枝末節(jié)的問題作斗爭……
            懶了…… 想逃避之……  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 19:24 | OwnWaterloo
            @cexer
            同時(shí), 我還發(fā)現(xiàn)C++程序員很容易產(chǎn)生一種傾向:
            嗯, 我要設(shè)計(jì)一個(gè)程序庫, 這個(gè)庫很smart, 無論怎么使用, 都是可以的。

            想怎么用, 就怎么用 —— 以前我也是這樣想的。


            就舉這個(gè)帖里出現(xiàn)的一個(gè)例子: 某處理器需要一個(gè)返回值。 是否可以讓程序員在需要默認(rèn)返回時(shí), 編寫一個(gè)返回void的處理器?

            就我個(gè)人而言 ( 僅僅是個(gè)人的看法, 與技術(shù)無關(guān)了, 兩者都是對的), 類似WTL那樣也是"完全可用"的。
            ... frame work ...
            case WM_XXX :
            R result = default_result;
            user_handler(&result, ... );
            return result;

            簽名必須寫為:
            (R* response, ... ); // 其實(shí)是個(gè)引用, 只是指針的話, 上面的frame work中的代碼意圖更明顯。

            "有且僅有這一種做法" , 沒有第二選項(xiàng)。
            若用戶不關(guān)心, 不去修改response的值即可。


            其實(shí)我也覺得傳出參數(shù)是很難看的設(shè)計(jì)。
            但它可工作, 也可以避免去編寫一堆meta programming 代碼, 而且這堆代碼可能又需要同C++的各種陰暗作斗爭。
            例如boost, 若不需要"照顧" 這么多編譯器的話, 其實(shí)代碼還是可讀的。
            但需要照顧這么多編譯器, 那無數(shù)的workaround, 使得代碼變得……
            不是天書也是爛泥……



            "嗯, T是一個(gè)原始指針可以, 當(dāng)它是個(gè)smart pointer時(shí)也應(yīng)該要被支持"。
            很多時(shí)候, C++ 程序員不是去考慮這種需求是否存在, 實(shí)現(xiàn)這種需求的復(fù)雜度如何, 維護(hù)代價(jià)如何。
            僅僅是因?yàn)樗麄兡埽?所以他們就要這樣做……

            其實(shí)有時(shí)候, 這些需求僅僅是美學(xué)上的, 而不是技術(shù)上的。
            比如上面那個(gè)返回值的例子。
            再比如:

            f(char const* s, ... ); // primary function
            f(std::string const& s, ... ) { return f(s.c_str(), ...); }
            f(CString const& s, ... ) { return f(static_cast<char const*>(s), ...); }
            ...

            如果不要后兩者, 任何工作依然可以完成。
            后兩者僅僅是提供一種方便, 而不是"核心"接口。

            也不是說這種方便不好…… 我的觀點(diǎn)是一定需要有某種方式告之用戶, 哪部分是核心接口。
            用戶優(yōu)先去掌握這部分, 然后整個(gè)庫的功能他就可以完全掌握了。
            其他的, 學(xué)得越多, 使用越方便。

            否則, 若沒有這種通告, 一個(gè)f不要緊, 若全體函數(shù)都這樣搞, 就會增加學(xué)習(xí)成本。
            本來15個(gè)核心函數(shù), 如果擴(kuò)展為45個(gè), 75個(gè), 就不僅僅是量上的變化, 而是質(zhì)上的變化 —— 讓人失去耐心。
            這同為什么要抑制warning一樣, 不是說不可以產(chǎn)生warning, 而是說當(dāng)warning多到一定程度時(shí), 會超過人的處理極限, 會不自覺的將他們?nèi)w忽略掉。
            所以發(fā)現(xiàn)warning就要干掉, 讓新的warning能引起人注意。


            至于通告方式…… 感覺手寫文檔是最靠譜的……
            docxgen 什么的…… 產(chǎn)生的文檔太死氣沉沉, 無法感受到當(dāng)中的輕重緩急。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 19:37 | OwnWaterloo
            舉個(gè)實(shí)際例子 (我就一話癆……) :
            最近在教人C語言……

            條件包含部分: #if, #elif, #else, #endif, defined 就完全ok了。
            所有功能都可以實(shí)現(xiàn)。
            但C語言還提供了 #ifdef, #ifndef…… 而且人們通常用的就是這個(gè)方便的接口……
            而且很多時(shí)候連#elif, #else都沒有 —— 比如頭文件保護(hù)符, 我就是在給別人解釋這個(gè)事情。

            為什么有#ifdef, #ifndef后, 還要有 #if 與 defined?
            若不給初學(xué)者解釋 #elif , 是肯定理解不了的。


            如果C語言一開始就只為條件包含提供一種方法: #if, #elif, #else, #endif, defined。
            大家也就只能這么寫。
            那介紹頭文件保護(hù)符時(shí), 就先介紹#if, defined, #endif就完了, 暫時(shí)不需要牽扯出其他部分, 其他部分可以慢慢了解。
            人腦沒法一下子容納太多信息的。



            再聲明一下, 以上都是個(gè)人觀點(diǎn), 話癆犯了, 聊聊天而已。
            完全沒有"其他人也必須這樣", "這就是事實(shí)"的意思……  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 19:39 | OwnWaterloo
            @yrj
            感謝~
            其實(shí)我困惑的不是"如何實(shí)現(xiàn)", 而是"是否需要在C++中實(shí)現(xiàn)"。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-18 23:49 | 溪流
            @cexer
            @OwnWaterloo
            關(guān)于調(diào)用約定,貌似不用復(fù)制N份(VS2010下測試),函數(shù)會自動被識別為 R (__stdcall *)(...)  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:00 | 溪流
            @OwnWaterloo
            很多時(shí)候, C++ 程序員不是去考慮這種需求是否存在, 實(shí)現(xiàn)這種需求的復(fù)雜度如何, 維護(hù)代價(jià)如何。
            僅僅是因?yàn)樗麄兡埽?所以他們就要這樣做……
            其實(shí)有時(shí)候, 這些需求僅僅是美學(xué)上的, 而不是技術(shù)上的。

            “僅僅是因?yàn)樗麄兡埽?所以他們就要這樣做……”有時(shí)候確實(shí)這樣,但一般實(shí)現(xiàn)代價(jià)一般很小的情況下才會出現(xiàn)這種沖動,想要順手做了。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:17 | 溪流
            @cexer
            謝謝分享這么多想法。

            給 handler 起名字確是刪除 handler 的好辦法,我可以不用糾結(jié)要不要用 typeid 啦。

            另外關(guān)于你說的不同類型的handler的綁定,作為 Function,我覺得不該加入這樣的特性;作為GUI專用的Event,是可以接受有的特性的。

            但是另一點(diǎn)疑問是,框架告訴用戶的規(guī)則——“返回 void 表示讓框架決定是否繼續(xù)處理,返回bool表示用戶想讓框架繼續(xù)處理或者拒絕用戶繼續(xù)處理”,與WTL的bHandled告訴用戶的——“設(shè)置bHandled用于指示框架是否繼續(xù)處理”相比,優(yōu)勢大嗎?都存在一種約定。而用返回void與bool的形式,可能僅僅是更加炫一點(diǎn)?

            實(shí)現(xiàn)上,不管用戶看到的參數(shù)是怎樣的,框架最初獲得的肯定是 WPARAM 和 LPARAM,這樣,就算不修改 Function,似乎也可以在控件中給出不同的處理函數(shù)形式,例如給出一組重載的 OnClick:
            OnClick(Function<void (int, int)>)
            OnClick(Function<bool (int, int)>)
            又例如給ClickEvent實(shí)現(xiàn)重載的operator+=?

            前兩天我在猶豫框架要不要幫用戶解析 WPARAM、LPARAM,你的例子讓我堅(jiān)定了想法,解析之!。。讓使用者不需要查 MSDN。。。或許做不到這種程度吧。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:22 | OwnWaterloo
            @溪流
            貌似你誤會了。

            cexer所說的應(yīng)該是這個(gè)情況:
            比如需要實(shí)現(xiàn)bind(或者是別的功能, 這個(gè)不是重點(diǎn), 只是用于舉例)。
            重點(diǎn)是: 庫的用戶會用不同的調(diào)用約定的函數(shù)傳入。

            還是上面的偽代碼:
            binder bind( f , arg ) { ... }

            R0 __cdecl F0_of_client ( ... );
            R1 __stdcall F1_of_client ( ... );

            bind(F0_of_client, ... );
            bind(F1_of_client, ... );

            只有兩者之一會成功。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:24 | 溪流
            @OwnWaterloo
            歡迎聊天~~~

            warning神馬的,感覺就是要見一次殺一次,不然絕對破窗效應(yīng)。

            “當(dāng)需要這種靈活性時(shí), 直接把C++丟了,選其他動態(tài)類型語言來編程。”有一群糾結(jié)的人(當(dāng)然不是我),他們的信仰就是用 C++ 做任何事,或者說他們的興趣就是用C++ 來做任何事,那怎么辦呢?呵呵。且不說個(gè)人興趣導(dǎo)向。從很理性的結(jié)果主義的角度考慮,這種時(shí)候確實(shí)得換,應(yīng)該要換,但也不是每個(gè)這樣的場合都有條件換,比如團(tuán)隊(duì)里沒有會python的且不想學(xué)python,但有一群蛋疼的Cpper,這種事情也會發(fā)生吧。

            最后,你說你在教C?求教,求升華~!·^_^  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:25 | OwnWaterloo
            @溪流
            >> 但一般實(shí)現(xiàn)代價(jià)一般很小的情況下才會出現(xiàn)這種沖動,想要順手做了。

            以前是不管代價(jià)如何就很沖動去順手了……
            現(xiàn)在想來……
            也算是練手吧……
              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:29 | OwnWaterloo
            @溪流
            >> 前兩天我在猶豫框架要不要幫用戶解析 WPARAM、LPARAM,你的例子讓我堅(jiān)定了想法,解析之!

            解析WPARAM和LPARAM確實(shí)無比無比蛋疼啊……
            當(dāng)然, 最初的那些C程序員也不是沒辦法了, 有個(gè) windowx.h 里面的宏有一些幫助。

            我也覺得, 作為一個(gè)有責(zé)任的庫, 應(yīng)該做之。
            如果能提供沒有解析的原始層次, 供那些有經(jīng)驗(yàn)的程序員使用, 似乎也可以?


            似乎程序員都有實(shí)現(xiàn)gui的沖動?
            我也沖動過的…… 但就是被WPARAM, LPARAM擊敗了……
            本想以此當(dāng)作熟悉各種消息的方式, 最終還是沒堅(jiān)持下來……
            沒毅力的人飄過……
              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:31 | OwnWaterloo
            @溪流
            嗯嗯, 這個(gè)確實(shí)符合破窗效應(yīng)。

            但15個(gè)api與45、 75個(gè)api應(yīng)該用什么效應(yīng)解釋呢?
            反正就我的感覺, 15個(gè)會很有耐心的去看; 而45個(gè)就沒耐心了; 75個(gè)就絕對不想去看了, 并給自己一個(gè)心理安慰:這玩意太復(fù)雜, 沒設(shè)計(jì)好……  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:33 | 溪流
            @OwnWaterloo
            bind的時(shí)候會遇到怎樣的情形我目前不知道。但是就 function 的階段來說,我剛剛自認(rèn)為比較仔細(xì)地確認(rèn)了代碼能夠跑的樣子:

            void __cdecl f1(int)
            {

            }

            void __stdcall f2(int)
            {

            }

            int main()
            {
            Function<void (int)> g1(&f1);
            Function<void (int)> g2(&f2);
            g1(0);
            g2(0);

            return 0;
            }

            觀察匯編結(jié)果,f1的ret出來后有 add esp 4動作,f2的ret 4出來后沒有  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:34 | OwnWaterloo
            @溪流
            嗯, 理想是一回事。 有時(shí)候還是得向現(xiàn)實(shí)屈服……
            作為個(gè)人的話, 還是可以學(xué)學(xué)python(perl, ruby) 什么的, 日常編程工作就可以擺脫C/C++了……  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:35 | OwnWaterloo
            @溪流
            你會C++還怕C么……

            說是教, 也就是以前的死黨指點(diǎn)指點(diǎn)。
            都是灰常基礎(chǔ)的東西……  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:47 | OwnWaterloo
            @溪流

            template<typename R>
            R call( R (*f)() ) { return f(); }

            void __cdecl f() { printf("cdecl\n"); }
            void __stdcall g() { printf("stdcall\n"); }

            VC8默認(rèn)情況: call(f) 是可以的, 而 call(g)不行。
            因?yàn)?call 中的 f 參數(shù)沒有指定調(diào)用約定。
            如果顯式使用 /Gr , 那就call(f)不行, call(g) 可以。



            如果使用重載:
            template<typename R>
            R call( R ( __cdecl *f)() ) { return f(); }


            template<typename R>
            R call( R ( __stdcall *f)() ) { return f(); }

            兩者都可以, 但在g++下, 只要兩者被同時(shí)使用, 就是重定義。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:50 | 溪流
            @OwnWaterloo
            多學(xué)點(diǎn)自然是應(yīng)該的,但我覺得不應(yīng)該追求日常編程脫離C/C++啊。。。那是件多么可怕的事,關(guān)系到飯碗。當(dāng)然不是歧視腳本語言什么的,但是從現(xiàn)狀看來,越來越多的人在用更加簡單的語言,業(yè)界最稀有的的還是C/C++的人啊。(vczh說的生存斗爭觀點(diǎn)我很贊同。)越簡單意味著入門越容易,競爭者多,于是想勝出就難了。所以我覺得往復(fù)雜方面靠的大方向還是要的。所以。。。用API不如造API,用輪子不如造輪子,不如用造輪子的輪子。。。臨了的時(shí)候,也許可以居高臨下的跟別人說:你,不就是會很多API么?這些API我都造過。你,不就是會用很多輪子嗎?這些輪子我都造過。你,不就是會用很多語言嗎?這些語言我也都造過。。。哈哈,扯淡了  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:55 | OwnWaterloo
            @溪流
            誤會誤會…… 我說的"日常"是指工作飯碗以外的……

            比如, 用metablog 轉(zhuǎn)移blog文章, 好像就是你寫過的吧?
            這用C/C++就蛋疼了……

            就是一些個(gè)人事務(wù), 有重復(fù)與機(jī)械的味道, 而用C/C++去做太麻煩。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:56 | 溪流
            @OwnWaterloo
            但是只要不要把返回值拆出來,就是可以的

            template<typename F>
            void call(F f ) { return f(); }

            void __cdecl f() { printf("cdecl\n"); }
            void __stdcall g() { printf("stdcall\n"); }
              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 00:59 | 溪流
            @OwnWaterloo
            呵呵,那玩意兒我本想改成一個(gè)博客客戶端,一直沒動。。。
            可是,工作范圍以外,就更要玩感興趣的語言了呀~
            我寫轉(zhuǎn)移文章的東西,不是為了轉(zhuǎn)移文章,是為了寫。
            當(dāng)然,碰到一些具有真實(shí)需求的并且有緊迫性的事情,那是要追求效率的。。。只不過這種狀況跟工作無異了  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 01:15 | OwnWaterloo
            @溪流
            嗯, 這是一個(gè)繞開的方法, 然后通過別的機(jī)制去獲得F的返回類型與參數(shù)列表還有調(diào)用約定。

            在我的那個(gè)case里, 返回類型、參數(shù)列表、調(diào)用約定都要獲取, 并對不同的調(diào)用約定做不同的事。
            嗯, 就是thunk啦, 你懂的。

            最終還是要獲取那些信息, 所以我直接用
            template<R>
            R call ( R (*f) )
            來獲取了。 這機(jī)制叫啥名一下子想不起來了……


            但這對g++依然不行。
            產(chǎn)生的兩個(gè)函數(shù)相互依然是重定義。
              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 01:18 | OwnWaterloo
            @溪流
            舉個(gè)例子嘛……

            不行就再舉個(gè), 比如登錄, 批量下載圖片……
            嗯, 就是校內(nèi)…… 我就是因?yàn)檫@個(gè)學(xué)python的。

            最開始是打算用 libcurl, 但發(fā)現(xiàn)C/C++處理字符串太蛋疼……
            又打算將 libcurl綁定到lua, 算練手(因?yàn)槟菚r(shí)候python還不熟)
            最終發(fā)現(xiàn)還是麻煩, 直接用python了……

            嗯, wget 什么的我不熟……
            用python做也算順便練習(xí)吧……  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 01:22 | OwnWaterloo
            就批量下載圖片這個(gè)case來說, C++能提高什么效率?
            網(wǎng)絡(luò)傳輸才是大頭, 這C++是提升不了的。
            也就提高解析html的效率…… 但那是多么蛋疼的事情……

            5秒傳輸是死的。
            0.5秒python解析, 與0秒(算極端情況)C++解析
            體會不到差異……
              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 01:23 | 溪流
            @OwnWaterloo
            呵呵

            這種寫法

            template <typename S>
            class C;

            template <typename R>
            class C<R (__stdcall)()>
            {

            };

            發(fā)現(xiàn)通不過。。。

            還有這個(gè):

            template <typename T>
            class C;

            template <typename U>
            class C<SomeTraits<U>::Result>
            {

            };

            偏特化的尖括號里不能進(jìn)行摸板元運(yùn)算?


              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 01:24 | 溪流
            @OwnWaterloo
            嗯,從實(shí)際角度上來說確實(shí)這樣~~  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 01:28 | OwnWaterloo
            @溪流
            template <typename S>
            class C;

            template <typename R>
            class C<R (__stdcall*)()> {};

            template <typename R>
            class C<R (__stdcall&)()> {};

            "函數(shù)類型"是一個(gè)很灰色的地帶……  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 10:50 | 飛舞的煙灰缸
            @cexer
            妙啊,想不到解決方案這么簡單。還為此郁悶了很久呢。只是看起來稍微有點(diǎn)冗余,我這樣有代碼潔癖的人的確不太容易想到。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 11:15 | 飛舞的煙灰缸
            @溪流
            前兩天我在猶豫框架要不要幫用戶解析 WPARAM、LPARAM,你的例子讓我堅(jiān)定了想法,解析之!。。讓使用者不需要查 MSDN。。。或許做不到這種程度吧。

            這也是一個(gè)兩面的問題。用戶自己解析他至少可以去查MSDN,熟悉GUI的用戶可以很方便的上手;框架提供解析那要做到怎樣的程度呢,如果全部由框架解析那至少等大部分常用的消息都解析完成后這個(gè)框架才跑的起來,之后還要不斷的完善直到把所有非常用的消息也解析完畢,這是一個(gè)浩瀚的工程。
            一個(gè)折中的辦法是提供常用消息的解析,同時(shí)用戶可以自己得到并處理WPARAM、LPARAM,就像WTL的用法。我自己反而覺得一堆使用WPARAM、LPARAM相同參數(shù)的處理函數(shù)看著整齊更賞心悅目。
              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 11:18 | 飛舞的煙灰缸
            GUI框架的復(fù)雜度主要是來源于實(shí)際要處理的問題的復(fù)雜度,這個(gè)是現(xiàn)實(shí)世界的復(fù)雜度沒辦法的事。框架做的少用起來復(fù)雜,框架做的多了學(xué)起來復(fù)雜用起來也未必簡單多少,個(gè)人觀點(diǎn)。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 12:32 | 溪流
            @飛舞的煙灰缸
            我覺得做或者不做都是一個(gè)方案,但是不同意折中。如果折中,框架沒法跟用戶解釋,為什么WM_LBUTTONUP做了額外的工作,WM_DESTROY就沒做呢?框架沒有理由認(rèn)為哪些是常用的。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 16:16 | yrj
            @OwnWaterloo
            共同提高吧。

            我覺得是需求的多樣性導(dǎo)致了實(shí)現(xiàn)的多樣性。雖然 C++ 語言不太適合動態(tài)消息綁定,但在有些情況下有這樣的需求,有這樣的需求就有人用 C++ 實(shí)現(xiàn)這種功能。

            曾看到另外一篇相關(guān)的文章
            function/bind的救贖
            http://blog.csdn.net/myan/archive/2010/10/09/5928531.aspx  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 18:47 | OwnWaterloo
            @yrj
            孟巖的是吧?
            當(dāng)時(shí)這篇文章在buzz上被分享是我就不予好評。
            理由就是上面和cexer提到的, 完全的動態(tài), 純面向?qū)ο蠖皇敲嫦騝lass是有代價(jià)的, 而且是C++必定不能承受的代價(jià)。
            只知道說C++這不好, 那不好, 也不想想為什么。
            他的文章(至少C++相關(guān)那些)在我看來都是水多料少。
            也許時(shí)代不同了吧……  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 22:19 | yrj
            受教了。

            我并不執(zhí)著于"是否需要在C++中實(shí)現(xiàn)"動態(tài)或者"C++必定不能承受的代價(jià)"。我想要知道的是每種方法的優(yōu)缺點(diǎn)和它的適用條件,應(yīng)用時(shí)根據(jù)不同的需求選擇適合的方法。在嵌入式GUI應(yīng)用中強(qiáng)調(diào)運(yùn)行效率及省電等情況下,會采用完全靜態(tài)的方法;如運(yùn)行效率等條件可以放松的情況下,則采用一些動態(tài)的方法提升開發(fā)效率。
              回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 22:28 | OwnWaterloo
            @yrj
            >> 我想要知道的是每種方法的優(yōu)缺點(diǎn)和它的適用條件,應(yīng)用時(shí)根據(jù)不同的需求選擇適合的方法。

            嗯, 這是王道。
            根據(jù)問題選合適的方案, 而不是將自己熟悉的方案去套各種問題。

            關(guān)鍵是, 孟巖只提一方面(靈活性) 不提其代價(jià)。 片面的給C++抹黑。
            仿佛能給C++抹黑就顯得自己多高水平似的。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-19 23:24 | 陳梓瀚(vczh)
            @溪流
            你可以去看看我的代碼哈,判斷兩個(gè)function是否==我做了。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-26 11:18 | guest
            GCC可以通過,但VC2008報(bào)錯(cuò):Function0(const T &fun) 說fun是“引用的引用”  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上) 2011-01-26 12:02 | 溪流
            @guest
            08沒試過,就不知道了。不過后來有一個(gè)改進(jìn)(在下篇最后說的),就是 Function<R()> 的第一個(gè)構(gòu)造函數(shù)的參數(shù)去掉 const &,改成 T fun,同時(shí) FunctionTraits 也不要了。這樣可能08下也能過了。  回復(fù)  更多評論
              
            # re: C++ 下 Function 對象的實(shí)現(xiàn)(上)[未登錄] 2011-02-21 17:31 | C++愛好者
            @OwnWaterloo
            完全同意你的觀點(diǎn),其實(shí)就讓大家按著一種方法寫那不是更好嗎?為啥還要搞那么多寫法,弄得大家都不統(tǒng)一,這不是畫蛇添足嗎?真不知道那些人是怎么想的。

            我順便也發(fā)泄下我對C/C++有些不滿意的地方:
            關(guān)于C++對象的隱式轉(zhuǎn)換,我覺得這個(gè)功能只有害處沒有好處(如果真要說有好處的話那就是可以少打點(diǎn)字)。比如說:
            class Test
            {
            public:
            Test(int a);
            };

            void func(const Test &t)
            {
            }

            調(diào)用1:func(Test(1)); // 手動構(gòu)造一個(gè)Test對象
            調(diào)用2:func(1); // 自動進(jìn)行隱式轉(zhuǎn)換

            雖然調(diào)用2和調(diào)用1的操作方式可以說完全一樣,但調(diào)用2的可讀性不強(qiáng),給人閱讀代碼帶來負(fù)擔(dān),這又是何必呢?莫非真的是為了省那么幾個(gè)字母?

            還有
            void func()
            {
            }

            void func(void)
            {
            }
            這兩種寫法我更贊同前一種寫法。沒參數(shù)就沒參數(shù)嘛,為啥非得弄個(gè)void在括號里,看著多難看,當(dāng)然返回值為void另當(dāng)別論(我承認(rèn)自己有點(diǎn)主觀,但是可以二選一,大家統(tǒng)一一下總可以吧)。  回復(fù)  更多評論
              
            久久久久亚洲AV成人网人人网站| 亚洲中文字幕无码久久精品1| 91精品国产色综久久| 97超级碰碰碰碰久久久久| 国产精品免费看久久久香蕉| 久久人人爽人人澡人人高潮AV| 无码人妻久久一区二区三区蜜桃| 少妇久久久久久久久久| 久久精品国产精品青草| 欧美激情精品久久久久久| 色综合久久中文字幕无码| 国产成人久久777777| 欧美色综合久久久久久| 久久无码人妻一区二区三区| 成人午夜精品久久久久久久小说| 久久国产色av免费看| 伊人久久综在合线亚洲2019| 精品综合久久久久久97| 青青草国产精品久久久久| 欧美亚洲国产精品久久| 久久综合欧美成人| 精品久久久一二三区| 999久久久免费国产精品播放| 久久亚洲AV无码精品色午夜| 99久久免费只有精品国产| 香蕉久久夜色精品升级完成| 久久久久久久国产免费看| 国产精品久久久久9999高清| 国产精品久久久久蜜芽| 97精品国产97久久久久久免费| 99久久无色码中文字幕人妻| 久久毛片免费看一区二区三区| www久久久天天com| 无码伊人66久久大杳蕉网站谷歌| 欧美午夜A∨大片久久| 色成年激情久久综合| 久久精品一本到99热免费| 人妻无码αv中文字幕久久琪琪布| 国产激情久久久久影院| 香港aa三级久久三级| 国产亚洲精久久久久久无码|