• <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>
            posts - 16,  comments - 34,  trackbacks - 0
            共10頁: 1 2 3 4 5 6 7 8 9 Last 
            re: GUI框架:消息檢查者 OwnWaterloo 2009-11-23 02:21
            @cexer
            確實…… cppblog的評論做得不咋嘀……
            所以代碼我也沒仔細看…… 意思差不多明白了。
            WndProc可以用這些參數表達任意多的參數,所以我們也可以……
            對不需要多余參數的,可以直接使用這幾個。
            對需要的,再引入多態與自由存儲的開銷,并將其包裹在一個值語意的類型中。
            是這樣嗎?

            這思路不錯。 預留幾個常用的參數,免得需要參數就要動用動態內存……



            說實話我一直對“框架”和“類庫”的概念分不大清楚,我想“框架”從名字上來說多了一個“可以擴展,可以填充”的意思,沒有你說的“限制你做事的方法”這種感覺。

            哎…… 因果弄反了……
            為什么需要【擴展】框架? —— 就是因為框架限制了做事的方式,不擴展就沒法做自己需要但框架又不提供的功能了。

            說得好聽點,叫擴展了框架;說得不好聽,叫順了框架的意;再難聽點,就是被框架qj了……


            以你這篇文章里提到框架來說吧:
            1個listener 有 1個m_map
            1個m_map 有若干個HanlderPair
            1個HandlerPair 包含一個 Checker和HandlerVector
            1個HandlerVector 包含若干個Handler

            這就是一種限制。MessageChecker也算。

            設:從源頭WndProc到實現某個功能所需要做的全部事情為S。
            【必須按這個框架制定的規則,將S拆分為S1、S2等等,然后安排到框架中的預定位置。】
            比如將事情分為監聽、檢查、映射、處理等工作。

            這就是我說的限制的意思。【框架規定了編程的模型】。

            假設,有一項需求框架沒有提供,比如Checker需要更多的參數。
            如果還想繼續使用這個框架,就必須設計一個MessageData。
            假設MessageData用戶而非框架提供的,是為【擴展】了框架。
            假設MessageData是框架自身提供的,但如果它又有另一個需求沒有滿足呢? 它必須提供一些【可擴展點】讓用戶【大部分按框架的思路編程】,并在需要的時候,擴展一下框架。否則用戶就會放棄這個框架了。

            如果框架的規定在大部分時候合理,框架就是合理的。
            這個尺度很難把握……

            總之,用框架編程,框架是主體。或者說底層的實現不用用戶操心,只需要注重業務邏輯。
            用類庫編程,程序員自己是主體。




            倒是沒考慮過使用函數來實現消息檢查者,我更喜歡它有對象的感覺一點,OOP語言嘛,而且類是肯定比函數有大得多的靈活性的。

            這個,其實也是反的……
            OO是思想,class只是它的一種實現方式。也許使用會比較方便。
            但靈活性是不如函數+數據的。

            1.
            比如C++、java、C#都沒有提供多重分派(multi-method)。
            所以,很明顯的,o.method(...); 只是method( o , ... );在1個對象下的簡便形式。
            如果需要method( o1, o2, ... ) —— 根據o1和o2的類型來決定做什么事。這3門語言是不提供支持的,也必須用函數來表達。
            我知道有個xxx模式…… 但3重分派它又解決不了了…… 治標不治本。

            當然,某種支持多分派的語言可以繼續提供 o1&o2.method( ... ); 的語法……

            2. OO將算法埋葬到自己的類里。
            如果這個算法很通用,更好的是將它實現為一個函數,而不是委身到一個class中。
            呃,這種情況在gui中可能不多見。

            re: Void and void pointer OwnWaterloo 2009-11-22 18:54
            1.
            C/C++大多數時候是可以混著說的,有時候不行,比如void*到T*的隱式轉換。
            int* pi;
            void* pv;
            pv = pi; /* C and C++ ok */
            pi = pv; /* C ok, C++ error */


            2.
            如果一個函數:
            1. 可以被C編譯器編譯
            2. 參數列表為空

            確實在參數列表中加上void是一個好習慣。可以作為一個規范。
            但不是為了自注釋與可讀性。
            自注釋與可讀性是瞎扯 —— 連這都不知道的C程序員就別寫C代碼射自己的腳。

            對其他情況,加上void也不是自注釋。
            struct C
            {

            C(void);
            /* 嘿,其實C()是C(void)的意思! */

            static void operator new(size_t);
            /* 嘿,其實這里加不加static它都只能是static! */

            };
            而是羅嗦。 連這都不知道的C++程序員更去別寫C++代碼炸自己的腳。


            3. void* pv; ++pv;
            GCC這個擴展很多時候還是很方便的……
            但是,如果想將程序移植到非GCC上,應該克制使用這個擴展。
            檢測出這個擴展的方法是:
            3.1 使用其他不支持這個擴展的編譯器編譯……
            3.1 使用GCC編譯,并使用-Wpointer-arith 或者 -Werror=pointer-arith選項


            4.

            void * pvoid;
            (char *)pvoid++; //ANSI:正確;GNU:正確
            (char *)pvoid += 1; //ANSI:錯誤;GNU:正確

            這段代碼…… 要一句話說清楚挺困難……
            總的來說,遵守ANSI的編譯器上,2行都是錯的。
            GCC上如果正確總是因為GCC的擴展。

            4.1 (char *)pvoid++;
            它的意思是 (char*)(pvoid++); 利用了GCC對void*進行算術操作的擴展。
            所以在GCC上是正確的,在ANSI上是錯誤的。

            4.2 (char *)pvoid += 1; 以及:
            ++(char*)pvoid;
            ((char *)pvoid)++; /* 使用括號改變優先級 */

            在ANSI上也是錯的。因為后綴前綴++,還有復合賦值的操作數必須是左值,而轉型操作符只能得到右值。
            GCC較低版本在此處又有擴展…… 可以在右值上執行上述操作,所以可以編譯通過。
            但這個擴展被標記為廢棄,在GCC高版本中被移除。

            re: GUI框架:消息檢查者 OwnWaterloo 2009-11-22 15:06
            將_HandlerMapIter改為HandlerMapIter或者HandlerMapIter_真的沒有壞處。
            本來就是一個private,庫用戶不可見的名字。
            屬于Handler,也不會和其他重名。
            還避免了(可能微乎其微)與_HandlerMapIter宏沖突。
            re: GUI框架:消息檢查者 OwnWaterloo 2009-11-22 15:01
            @cexer
            好像沒有,我是宅男……

            boost有自己的苦衷。它需要一個簡短的東西來做占位符。
            這樣寫就太繁瑣了:
            bind( f, first, second , ... ) ();
            而且first,second估計很容易和程序員沖突。
            1、2又不是合法的標識符。 所以干脆搞成_1, _2,至少不會和程序員沖突了。
            bind( f, _1, _2 ... )( ... ); 直觀

            而_HandlerMapIter改為HandlerMapIter或者HandlerMapIter_并不會影響什么,而且也符合與C/C++定下的契約。


            boost將_1,_2改為1_,2_是不合法的,不能以數字開頭。
            改為其他,不太簡潔……



            也不能算完全的學究。它們不會永遠被保留。
            C99已經開始動用這些保留的標識符了。
            _LongLong, _Complex,_Bool等。
            C++也會跟進。

            @唐風
            后來又回味了一下,用SFINAE,故意讓使用某些類型實例化模板時產生錯誤,從而從重載候選中剔除,確實是一個很好的辦法~_~

            boost的enable_if也確實抓住并滿足了大量需求。

            好東西啊,再回味回味...

            re: GUI框架:消息檢查者 OwnWaterloo 2009-11-22 14:38
            @cexer
            說說我對gui框架的需求吧……
            以用戶的身份…… 用戶的需求總得聽聽吧……


            從這里說起:
            【目前沒遇到id,wparam,lparam 三個參數解決不了問題的情況】
            我編寫gui的經驗不多。所以對"這3個參數是否能解決所有問題"是一點概念都沒有。

            不過我想了一個例子,比如我要在xxx-yyy時間段內,檢查這個消息,返回真,這個時間段外,就返回假。
            這個checker就做不到了。 肯定要放到其他地方做,比如hanlder中。


            對,這就是我對"框架"感到反感的地方之一 —— 它"限制"你做事的方法。
            它會將可能本來是一件完整的工作,拆散,然后分散到框架的一些地方去。

            為了完成這個工作,你要按框架所拆分的那樣 —— 需要順著框架的思路,而非我自己的思路 ——只需要id,wp,lp就能check的,放到checker中。其他即使也是check,也得放到handler中。
            如果框架拆分得恰當,就很好,比如 CommandChecker等。
            如果框架拆分得不恰當,就很糟……
            所以我對mfc一點好感都沒有。


            作為一個了解且不排斥win32api的用戶(當然,gui框架大多著眼的都不是這種用戶囧……),我需要的僅僅是彌補一下WndProc,使得它可以找到this —— 這個工作幾乎是必須做的,即使是用C編寫gui的家伙。
            然后,就可以從hwnd,msg,wp,lp開始干活了 —— 不需要再學任何關于框架的知識,比如要將check放在哪,hanlder放在哪。
            我希望框架提供一個,在window上add(掛)多個listener的機制就差不多了…… 如何分配checker和handler還有cracker的工作,還是將它們揉在一起,用戶可以自行決定。

            所以我對lambda表達式特別渴望……
            比如上面的代碼,對應的偽代碼可能是這樣:

            window w;
            w.add_listener( void (const msg& m) { if (m.msg==WM_CREATE) cout<<"create\n"<<endl; } );


            然后,在每天編程閑暇的時候,發現庫(而非框架)里面有command_check,我就可以這樣寫代碼了:
            w.add_listener( void (const msg&m) { if (is_command(m,id) cout<<command_crack(m).id<<endl; } );

            如果我沒發現command_check,我也可以開始我的工作,只是代碼更繁瑣一些。

            這就是庫和框架的一個很大的區別。
            你可以使用熟悉的方式工作,然后慢慢熟悉這個庫,慢慢用它干善自己的工作,并提高效率。
            而框架,不對它熟悉到一定程度,就沒法工作……



            作為程序員……
            我也了解用戶的需求是很惡心的…… 就像你有一篇blog里寫的那樣……
            所以,推己及人……
            對我提出的這個惡心的需求,聽過之后就當是廢話好了~_~

            re: GUI框架:消息檢查者 OwnWaterloo 2009-11-22 14:04
            @cexer
            剛醒…… 一邊吃飯一邊先把不涉及風格的東西解釋一下……

            不說不變式了。我也覺得這詞太學究,所以在后面補了一句:
            【調用的是子類的函數,使用的是父類的數據(被切割了)……】

            而且,我說錯了……
            編譯器會在復制(以及切割)的同時修正虛指針 —— 忘了……
            要memcpy( &b, &d, sizeof(b) ); 才行……


            關于【for (_HandlerMapIter it = m_map.begin(); it!=m_map.end(); ++it)】
            我指的不是for each。而是遍歷。
            不明白為什么只需要遍歷,卻用了一個map。
            然后…… 我再看了看這行代碼之上的代碼,這是一個vector……
            我又看錯了…… 代碼看得不仔細……



            關于coding-style。其實我是最不講究這個的了……
            但有些東西不屬于coding-style,而是屬于底線 —— 使用c/c++語言必須遵守的 —— 不能使用語言保留的標識符。
            語言保留的標識符還分了幾種, 統一起來就是以下劃線開始的程序員全都不能使用。
            這在很多書里面都有說,又在很多書(比如Gof,不知道算不算經典)中犯錯。

            re: GUI框架:消息檢查者 OwnWaterloo 2009-11-22 05:14
            貌似沙發又得被我占領……
            作為一個對oo不屑一顧的c(pp)er,看到這樣的代碼相當不適……




            比如MessageChecker和它的子類……
            不適啊……

            1. 切割與多態
            從msvc和gcc的實現來看,虛指針是切割不掉的,它在父類中。
            虛表就更不存在切割一說……
            不能多態的原因不是因為切割,而是因為 —— 沒有使用引用或指針類型進行調用……
            所以啊,對這種情況 …… 可以惡心點……
            按值保存(會被切割),同時對每個值也保存一個對應的指針,指向該值。
            通過指針而非值進行調用,誘使編譯器使用虛指針。

            為什么感到惡心呢……
            是因為這樣可以多態調用,但不能保證子類的不變式……
            調用的是子類的函數,使用的是父類的數據(被切割了)……


            2. class vs struct
            當然,對這個問題蠻適合的。
            因為樓主的方案在子類中同樣不能塞新的數據,否則一樣會被切割。
            然后,調用那個函數指針時,就會訪問到無效內容。


            為什么感到不適呢……
            上面說了,只要是按值存儲,子類就不可能添加數據成員 —— 否則就會被切割。
            也就是說,無論怎樣繼承,數據成員就那么幾個……
            這里需要的其實不是class, 而是struct ……
            強行使用class, 會使得需要定制的時候,就需要定義一個類 —— 其實僅僅需要定制一個函數就可以了。

            struct message_checker
            {

            id;
            wp;
            lp;
            int (*is_ok) (const message_checker&,const message& );

            };

            message_checker ButtonClickingChecker(WORD id);
            message_checker xxxChecker( ... );

            所以,怎么看怎么別扭……
            當然,這是口味問題…… 不值得爭個你死我活……


            3. 其他
            有些不需要id,wp,lp的checker該怎么辦呢……
            需要比id,wp,lp更多參數的checker又該怎么辦呢……


            for (_HandlerMapIter it = m_map.begin(); it!=m_map.end(); ++it)
            這個效率是很低的……
            _HandlerMapIter的命名也是不符合c/c++規范的…… 都被Gof帶壞了……
            @唐風
            C++老豆的書得看~_~ 確實是對C++理解最深的人。
            本地化在《TC++PL》的附錄(好像特別版才有附錄)中有,不過好像沒想象中的那么好用……


            關于log…… 特指debug log 我不是說它的效率…… 而是為什么需要這種東西……
            我的想法是,能測試就不要用調試器,能用調試器就不要寫debug log,只有不得已的時候,才會用那個東西。
            為什么不把bug都查出來?
            而是將它隱藏到軟件中,等它出現后 —— 交付給用戶使用后 —— 再從debug log中研究哪出的bug?

            當然,我也沒什么大型軟件的經驗,也沒有用戶邏輯復雜的軟件的經驗……
            可能那時候會理解debug log的用處……

            re: 函數調用棧初探 OwnWaterloo 2009-11-21 17:56
            @唐風

            能不能設置一個類似“勾子”的東西,在CPU進行壓棧的時候記錄下某些信息呢?

            這個就不知道了……
            查查vs的那個性能測試是怎么做到的? 也是非侵入的。
            或者其他profile工具是怎么做的?

            re: 函數調用棧初探 OwnWaterloo 2009-11-21 17:55
            @唐風

            但可以肯定的是每個函數調用都會有壓棧出棧

            問題就在于這個前提是沒有保證的。
            編譯器處理尾調用時可以使用jmp而不用call。
            這種情況在msvc和gcc上都存在。

            編譯器不一定會生成序言部分 —— push ebp mov ebp esp。
            msvc肯定有這種情況,我見過。
            gcc好像會嚴格生成這部分代碼。



            vs2005 team suit好像有這個功能。 在性能測試中。 但沒這么靈活……
            好像只能在程序跑完之后才能輸出分析的結果,不能任意兩個斷點之間。
            輸出結果中記得是包含了調用樹的。
            可以玩玩看,也許有輸出2個斷點之間的調用樹呢?

            @唐風
            這方法好,學習了~

            我覺得《the c++ programming language》中21章將流的架構講得很清楚。
            具體實現么。。。
            有2本書可能有這方面的內容,我沒細看……
            1本是《C++ Templates》
            1本是《標準C++輸入輸出流與本地化》
            http://www.china-pub.com/3274


            代替io流么…… 有個iconv可以做編碼轉換。
            代替io流的場合我沒遇見過……
            其實我對很多項目都要自己寫一個log模塊很不理解……
            沒往這方面想過……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-21 02:04
            @Loaden
            同學生日,聚會喝了點酒…… 看代碼很暈…… 像不認識C++似的……

            關于你在csdn上的問題。

            函數模板可以通過實際參數來推導類型。
            template<typename T>
            T max(T v1,T v2);

            max(1212 , 326); // max<int> T=int
            max(1212.0 , 326.0); // max<double> T=double

            max(1212, 326.0); // 歧義 T=int or double
            max<double>(1212, 326.0); // 顯示指定 T=double


            在模板參數列表中,最后一個不能被推導的參數以及它之前的所有參數都必須顯示指定:
            template<typename D,typename S>
            D union_cast(S src)
            {

            union
            {
            S src;
            D dst;
            } u = { src };
            return u.dst;

            }

            union_cast<long long>(&C::f); // D= 一個成員指針,但S不能通過實際參數推導出來,必須顯式指定。


            操作符重載,其實也是一個函數,只是通常不以函數的形式調用。
            所以你那個需求,可能真的只能這么寫:
            a.operator[]<T,U>( index );


            如果真的想實現:
            T* p = a[ i ];
            肯定能實現,不過很惡心…… 也可能很危險……

            class A;

            struct proxy
            {
            A* a_;

            template<typename D>
            operator D*() const { return new D; }

            };

            class A
            {

            template<typename T>
            proxy operator[](T i)
            {
            proxy p = { this };
            return p;
            }

            };

            A a;
            proxy p = a[ i ]; // 調用a.operator[]<int>; 返回一個proxy
            E<TBase>* o = p; // proxy.operator<D>

            E<TBase>* o = a[ i ]; // 連起來就是這樣……


            template<typename T>
            operator T() 是很邪惡的東西,盡量少用……



            是的。因為這樣才能兼容x64平臺:x64前8個參數通過寄存器傳遞。

            確實,i386是個特例,主要通過棧來傳遞參數。
            這樣,無論被partial application的函數的參數如何 —— 只要不是返回大結構體 —— 插入一個指針參數都可以使用相同的thunk結構體。

            我考慮過ppc(我唯一能搞到的非i386的機器),主要通過寄存器傳遞。
            視被partial application的函數的參數,插入一個指針需要不同的thunk結構體。
            但替換某個參數,thunk結構體就和其他參數的數目無關了。

            如果被partial application的函數的參數已經確定,為其設計一個thunk結構體,插入一個指針參數也應該是可行的。

            boost.lambda好像只是表達式。 沒仔細研究過。
            要保存可以有auto。。。 或者boost.typeof。

            本來我需要的僅僅是一個函數,卻不得不定義一個類…… 很……
            被其他語言慣壞了,就覺得c++寫gui非常不方便……

            我打算等c++0x流行之后再考慮是不是要用c++寫gui了~_~
            在哪之前,如果有得選擇,c++就不是第1人選……
            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 17:19
            @cexer
            那篇文章我晚點再看看。

            我覺得是函數指針還是函數對象關系都不大,我并不排斥函數指針~_~

            關鍵是那個共用的模板定義不出來啊……

            假設,一個xxx控件,用來調節一個整數,它提供這么一個通知:

            void xxx::on_change_add( void (*listener)(void* context,int pos), void* context );

            我覺得最好的語法:
            void f1()
            {

            xxx x;
            int i = 0;
            x.on_change_add( void (void* c,int pos){ *(int*)c = pos; }, &i );
            wait();
            printf("%d\n",i);

            }
            應該只有等C++0x的lambda才可以這樣寫了。


            次之:

            void f2()
            {

            xxx x;
            int i = 0;
            struct local { static void set_pos(void* c,int pos) { *(int*)c = pos; } };
            x.on_change_add( &local::set_pos, &i );
            wait();
            printf("%d\n",i);

            }

            這是符合C++語法的,比較丑陋了。
            但如果on_change_add是函數模板,就會很麻煩。
            函數模板的實際類型參數不能是這種內嵌的類型。


            最難看的:
            void set_pos(void* c,int pos) { *(int*)c = pos; }
            void f3()
            {

            xxx x;
            int i = 0;
            x.on_change_add( &set_pos, &i );
            wait();
            printf("%d\n",i);

            }


            set_pos只是一個示例而已。
            代表的是一簇簡單(代碼就2、3行),但邏輯多變 ——根本不可能預定義一些函數對象或者函數模板 —— 只能是隨寫隨用、一處使用、不被復用的代碼來表現需要的邏輯。

            類似的例子, std::for_each如果沒有lambda的支持, 就是雞肋。


            當然,這只是個審美的問題……
            也有人覺得第3種(定義到外部)還不錯……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 16:57
            @cexer
            GetClassInfo( DIALOG , &wc );
            wc.lpProc = my_proc;
            不修改wc.name
            RegClass( &wc );

            這樣?

            這……

            如果在修改wc并注冊之前,已經創建了一些窗口呢?
            會怎樣? 也會跟著被修改? 不會吧……?

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 16:52
            @cexer
            超類化?


            可以創建嵌套類,嵌套類中可以有靜態或非靜態成員。比較間接一點。
            直接寫嵌套函數是不行的。

            跟是否可以函數對象沒什么關系。
            主要需求是定義這個回調(或者函數對象)的地方最好和使用這個回調的地方比較接近。

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 15:16
            也不算匿名函數吧。
            就是,如果一個調用需要傳入一個函數作為回調,如何在調用點上創建這個回調函數,而不是在調用點所在函數外。

            void handle_xxx( callback );

            void f()
            {


            handle_xxx
            ( /* 1. 如何就地創建一個函數,傳遞給handle_xxx */
            { ... }
            /* 估計C++0x才行 */
            );


            /* 2. 或者次一點,再handle_xxx的調用點所在函數中創建 */
            my_handler () { ... }
            handle_xxx( my_handler );

            }


            /* 3.最次的,就是在調用處所在函數外定義 */
            my_handler() { ... }
            void f()
            {

            handle_xxx( my_handler );

            }

            C++可以在函數內創建一個嵌套class, 并定義該class的函數 —— 可以是static的,但就是不能直接定義一個嵌套函數。
            這可能是C++98、03的唯一手段了。

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 15:08
            @cexer
            白天思維很亂…… 晚上思維清晰一些。
            在想C++中怎么弄出匿名函數…… 無果……

            如果沒有這種東西, gui的語法是不會漂亮的……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 15:06
            @cexer

            GetWindowLongPtr 確實有比較大的限制。除了你所說的,對話框,按鈕這類的已經注冊過的窗口類,其 cbWndExtra 都已經是固定的,要想使用 GetWindowLongPtr 來存取額外數據的話,就必須要超類化,這樣的就又麻煩了。所以綜合考慮,SetProp 和 thunk 是最優選擇。


            已經注冊過的窗口類,如果要塞context,都需要subclassing。
            SetWindowLongPtr( hwnd, GWLP_WNDPROC, insert_context );
            然后在insert_context中將context塞進去,SetProp或thunk。
            只是GetWindowLongPtr塞不了。
            對窗口類注冊不由自己控制的情況,GetWindowLongPtr確實不適合。


            對這些預定義的窗口類需要做一些什么工作,有什么需求,我沒怎么細想……
            用win32 api寫gui的事,我沒做幾回……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 14:47
            @cexer
            關于tss+table, 我理解錯了……

            SendMessage( hwnd , .... );
            如果調用SendMessage所屬線程T1和hwnd所屬線程T2不是同一個線程,則依然是由T2,而不是T1去執行hwnd的wndproc。
            我想成T1去了……

            哦,還有個GetWindowThreadProcessId……
            如果tss真有問題,可以不用那么自動的tss,手動+GetWindowThreadProcessId也行。

            那mfc又是怎么掛掉的呢……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 01:53
            關于GetWindowLongPtr。


            1. 系統定義index
            有配套的GWLP_xxx系列…… 以前真沒注意……
            在32位上GWL_xxx和GWLP_xxx的值可能相同…… 但還是應該使用配套的……
            自從我用Get/SetWindowLongPtr開始,就沒寫對一次過……
            囧……

            幸好cexer的某個回復中有GWLP_USERDATA……
            順手再查了一下……


            2. array index
            array就是一個byte的array。
            這個array的地址是不會返回給用戶的 —— 應該如此。

            大致應該是如下偽代碼:
            LONG_PTR GetWindowLongPtr( hwnd , index )
            {

            char* array = get_extra(hwnd);
            LONG_PTR r;
            if ( index + sizeof(r) > get_extra_size(hwnd) )
            {
            SetLastError(ERROR_INVALID_INDEX); // 1413
            return 0;
            }

            memcpy(&r , array+index , sizeof(r) );
            return r;

            }

            越界會返回0,LastError是無效索引,1413。

            有效索引應該是:
            GetWindowLong, [0, cbWndExtra - sizeof(LONG) ], msdn說-4,也算對。
            GetWindowLongPtr, [0, cbWndExtra - sizeof(LONG_PTR) ] , msdn說-sizeof integer, 這就是我困惑的地方……
            integer是啥? short算不算…… 應該是sizeof(LONG_PTR)才對……


            對齊用戶(winapi的用戶)也不需要特別關心。


            反正array的地址是不可能暴露出來的,上面有些地方可能說錯了。

            SetWindowLong/Ptr也是會檢查無效索引。


            3. 創建窗口時收到的消息:

            測試時順便得到的:
            WM_GETMINMAXINFO = 0x24;
            WM_NCCREATE = 0x81;
            WM_NCCALCSIZE = 0x83;
            WM_CREATE = 0x1;

            可能還和窗口風格什么的有關,以上不準,以msdn為準。


            4. GetWindowLongPtr的其他限制
            cbWndExtra在對話框中有一些規定。
            MDI也有一些特殊規定 —— 針對CreateWindow是傳入context。
            可能還有一些特殊情況……

            總之…… win32 gui是很浩瀚的事情…………
            cexer…… 精神上支持你……


            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 00:44
            我說一下思路吧,不算證明 tss+table不行,只是說一下這個方案可能遇到的陷阱。

            假設這就是注冊窗口類時使用的函數:
            LRESULT CALLBACK proc(HWND hwnd, UINT msg, WPARAM, LPARAM )
            {

            if ( msg == WM_XXX ) // 處理某條消息時需要得到this
            {
            table = get_tss_table(); // 線程相關映射表
            w = table.lookup( hwnd ); // 查找hwnd對應的object。
            w->f() // 調用一個成員函數...
            }

            }

            如果另一個成員函數:
            C::g()
            {

            SendMessage( this->m_hwnd , WM_XXX , ... );
            // 而且這個消息、WM_XXX、在proc處理過程中需要取得this

            }

            那這個成員函數g,就不能在另一個線程中使用。
            因為table = get_tss_table();是線程相關的。


            是否會有這種成員函數…… 就不知道了…… 沒這方面的經驗,也想不出例子……



            mfc返回一個CWnd* ,可能也是基于這種考慮:
            CWnd* w = father->getChild (...);
            w->g(); // 這里可能需要查表。

            mfc中也可以這樣:
            HWND hwnd = ::getChind( father->m_hwnd, ... );
            CWnd w;
            w.Attach( hwnd ); // 插入表
            w.g(); // 可查詢
            w.Detach(); // 從表中刪除

            可能afx考慮到最后的w.Detach會被忘記調用。一旦w離開作用域,那個hwnd -> object的表,就指向一個無效對象。

            所以就引入了一個類似gc的方式…… 返回CWnd* 同時插入表中。

            純猜測……


            @codejie
            因為我最近寫的絕大部分是Clean C —— C和C++的子集。
            所以對C和C++的區別比較注意。
            還會同時拿到兩種語言的編譯器上去編譯。


            我的blog里面還有一些C/C++區別的文章。
            因為學校要求寫實習周記…… 我又不想隨便寫點應付交差……
            所以就寫了一些文章放到blog上, 然后再抄給學校交差……

            所以那個月更新了不少…… 以后還會更新一些。

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-20 00:13
            @cexer
            確實有點猥瑣……

            沒這功能,WndProc的設計真的就是缺陷了……
            有這功能,WndProc還是很麻煩……


            我剛吃完晚飯…… 囧……
            習慣沒養好…… 夜貓慣了…… 慢慢改吧……
            可能哪天不寫代碼了,生活會規律一些……


            那個array的index到底是怎樣,msdn我沒看怎么明白……
            待會驗證一下……

            tss + table是否可以調用成員函數, 我覺得有困難……
            我再想想……
            畢竟,要證明很容易 —— 想出一個實現。
            要證偽 —— 證明不可能實現出來 —— 要困難不少……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-19 23:14
            還有一句:

            Remarks
            Reserve extra window memory by specifying a nonzero value in the cbWndExtra member of the WNDCLASSEX structure used with the RegisterClassEx function.


            原來叫cbWndExtra…… 不叫cbExtra……
            很有沒用…… 寫錯了……
            也對,因為相對的還有cbClassExtra……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-19 23:12
            @cexer
            哈哈,性感誘人吧? 所以我極力推薦這妞……
            也正因為誤解很嚴重……
            所以用戶代碼訪問 [0, length ) 的幾率還真不大……


            抄一下msdn……

            The GetWindowLongPtr function retrieves information about the specified window. 【The function also retrieves the value at a specified offset into the extra window memory.】

            LONG_PTR GetWindowLongPtr
            (
            HWND hWnd,
            int nIndex
            );

            nIndex
            [in] Specifies the zero-based offset to the value to be retrieved. 【Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.】
            To retrieve any other value, specify one of the following values.
            ... 后面就是其他很多系統定義index了。


            GetWindowLongPtr的另一個作用 —— 0-based array —— 只有這么2句話……
            還隱藏在對系統自定義index的大篇幅的介紹之中……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-19 23:03
            @cexer
            我沒有細想過 HWND -> this的映射的方案……
            因為它一開始就在我排除的范圍內……

            ——【一個設計缺陷不應該由另一個設計缺陷來彌補】。

            幸好,最后我發現WndProc(hwnd, ... )還不算徹底的設計缺陷。
            因為hwnd可以有cbExtra。


            我再想想table(hwnd,this)是否一定需要mfc那樣的機制……
            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-19 22:57
            @cexer
            我一直都在強調……
            GetWindowLongPtr, GWL_USERDATA是普遍被誤解了的……


            我從學Windows開始…… 就把這個用錯了……
            因為我的老師給我們的課件上用錯了……
            網絡上【我沒見過用對的文章】,也全都用錯了:
            p = (Window*)GetWindowLongPtr( hwnd , GWL_USERDATA );



            不是這樣的……
            GWL_USERDATA有其本身的含義(在對話框中)。
            應該怎么用還是怎么用。
            庫是不需要占用它的……


            GetWindowLongPtr返回的是一個array中的元素。
            1.
            它可以是一個以0開始的,大小為cbExtra / sizeof(void*)的數組。
            2.
            或者是以0開始的,大小為cbExtra / sizeof(long) —— 如果以GetWindowLong取得。
            3.
            或者是一些系統定義的index, 比如GWL_USERDATA, GWL_WNDPROC —— 這些系統定義的index全是負數。

            我上面寫的代碼全是:
            GetWindowLongPtr( hwnd, 0 );
            或者:
            GetWindowLongPtr( hwnd, index );
            而不是:
            GetWindowLongPtr( hwnd, GWL_USERDATA );是錯的 —— 對于塞this來說的話 —— 確實會占用掉用可以留給用戶的機制。


            庫只要將注冊窗口類的過程截獲,并且給cbExtra增加一點點 —— padding + sizeof(this) + guard。

            庫的用戶還是可以自由使用GetWindowLongPtr, 只要他沒有越軌 —— 不超過他填入的cbExtra。
            用戶要越軌,那就責任自負……


            內存分配:
            cbExtra應該是和window一起被分配,不需要專門分配thunk結構體或者SetProp用的數據。
            速度:
            直接通過index尋址array,也幾乎是最快的查找方式。


            所以我說從整體上thunk和GetWindowLongPtr的效率可能差不多。
            thunk如果one window one WNDCLASS,會注冊很多WNDCLASS。
            如果多個window共享一個WNDCLASS,就需要轉發一次。
            這些都是有效率損耗的。

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-19 22:18
            @cexer
            void f( CWnd* w)
            {

            CWnd* d = w->GetItem( ... );
            d->GetWindowTitle( ... );

            }

            具體是不是叫這個名字不記得了。
            由一個窗口得到上面的控件的窗口,然后取一下標題。
            如果w來自另一個線程 …… 等著程序掛吧……


            d是一個指針,有主動釋放嗎?
            mfc的消息循環還會在odle時清除這種"臨時對象"的指針。
            就為了滿足它的table機制,需要在動態內存中創建對象,并使用類似gc的機制去清理……
            mfc的大、慢就是這么一點一點的積累出來的。
            table這種方式絕不可取……


            這個例子只是舉例,可能并不正確,因為我很久都不用mfc了……
            太out……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-19 22:07
            @cexer
            如果不考慮效率,最好的辦法就是不用C++做gui了。
            開發又慢又容易出錯,何必呢? 圖什么?


            GetWindowLongPtr的問題真的很容易解決。
            只要程序員用這個函數之前,看過GetWindowLongPtr的文檔。
            如果一個庫的用戶繞過庫并且不看文檔而直接使用win32api,庫是怎么都做不到,也花太多心思防止他用錯的。

            他甚至可以不用看這個gui庫的文檔 —— 因為你可以把this放在array的最后,而不是最前。
            甚至還可以在this前后設置一些guard來檢測使用庫的人員是否出軌。
            并且,如果將this放在最后,正好也可能需要一些padding來對齊。



            SetProp和thunk的效率就不用比了……
            同樣要分配內存。
            thunk是直接調用,SetProp調用之后還有活要干……
            當然,效率不是最主要的…… 也不是可以完全不顧的……


            我覺得將this放在GetWindowLongPtr所在array最后,既安全又快速又簡便……


            當然,最終權衡在你。
            而且…… 只要這個方式不對用戶公開……
            隨時可以改……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-19 18:07
            @cexer

            thunk 完成的功能只是消息從系統到窗口類的很小一步,也是最需要完成的第一步。在這一步上我嘗試過很多方法,包括這種 thunk 技術,甚至還用過TLS做映射。我目前用的方法很簡單就是 SetProp,GetProp 兩個函數,有點重劍換木劍的感覺。效率上肯定沒有 thunk 的直接調用高,但是心里更踏實一點,在內存當中寫二進制代碼總有在犯罪的感覺。

            “過早的優化是一切罪惡的根源”。基于這一步只是整個GUI框架當中是很微小的一步,幾乎不影響框架設計,可以先把框架搭好,再來從安全,效率,可移值性各個方面進行考慮。反正不要選擇 GetWindowLong ,GWLP_USERDATA 那一套,如果發布出去后客戶也使用這個東西就一切全完了,“過時的悲觀毫無益處”。


            我就知道……
            大多數人對GetWindowLongPtr/GWL_USERDATA的理解是有誤的。
            你先仔細看看msdn是怎么介紹cbExtra、GetWindowLongPtr,還有GWL_USERDATA再考慮要不要否定這套方案。


            線程局部存儲映射表?
            先不說效率,mfc不能在線程之間傳遞CWnd*,就是一個使用上的劣勢。


            mfc需要在文檔中記錄: 不能將CWnd*傳遞到另一個線程。
            使用SetProp需要在文檔中記錄:不要SetProp("cexer" , ...)
            使用GetWindowLongPtr需要在文檔中記錄:不要SetWindowLongPtr( hwnd, 0, ... );


            3種方案有區別嗎?文檔中都必須有記錄 —— 什么是庫使用的,用戶不能隨意使用 —— 沒一個逃得掉。
            可能只有thunk,不需要在文檔中特別說明什么,比如ATL/WTL。

            區別還是有的,效率。
            tss-table肯定是最低,編程也最復雜的,完全不值得使用。
            SetProp需要查找,效率肯定沒有另外兩種高,而且,它很可能也需要分配內存。

            thunk和GetWindowLongPtr旗鼓相當。很難說誰高誰低。
            但后者要簡單很多,而且是在所有Windows上都可以使用,不用考慮匯編的問題。

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-19 17:56
            @Loaden

            非靜態。但只有三個參數,第一個參數通過匯編,寫到了m_wnd成員變量上了。沒辦法:兼容x64平臺,只能這樣thunk。

            你用的是和atl一樣的方式?
            將hwnd"替換"為this? 而非"插入"一個this?
            是嗎?

            1~3cpu指令只是"調整棧"而已,調整完了,肯定會繼續執行thunk中的call,來到你的函數。
            這個call或者jmp是免不到的。
            同時,編譯器得不到thunk的目的地的信息,所以不可能進行任何優化。
            本來也沒什么優化的余地了,都機器碼了……


            x64的我也有點想做……
            但對x64上的匯編和調用約定不熟悉,而且也沒有x64的測試平臺……
            @codejie
            我也是看那篇文章才懂的……
            值得看看,推薦~_~

            typedef struct DefStruct DefStruct;

            typedef BOOL (*func)(const DefStruct* s);

            struct DefStruct
            {

            int i;
            func f;

            };

            這是tag在C和C++中的區別造成的問題。
            更詳細的,見這里:
            http://www.embedded.com/9900748

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 16:08
            @陳梓瀚(vczh)
            我就一閑人……

            我和羅登在gtalk上聊得更歡……
            要不要來插一腳?

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 16:00
            @Loaden

            糾正一下,是隨手 三 測 ^)^
            第三次改輸出代碼時崩潰的。


            隨手1測就錯了,非常不靠譜……
            隨手3測就錯了,稍微靠譜一點點……
            反正代碼很不靠譜的,免責聲明我也寫了的……
            出了任何問題我不負責哦~_~

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 15:59
            @Loaden

            #define EVTCREATE(wnd, wpa, lpa) \
            case WM_CREATE: \
            { \
            evt::Create evt = {wnd, this, reinterpret_cast<LPCREATESTRUCT>(lpa), false}; \
            ...

            this是怎么得到的?


            LRESULT CALLBACK Frame::WndProc(UINT msg, WPARAM wpa, LPARAM lpa)
            {
            ...

            這是靜態還是非靜態的? 函數定義看不出來,只有聲明才行。
            估計也是非靜態的吧? 因為有m_wnd這些東西。
            那么,this是怎么得到的??



            追根究底,要從【注冊窗口類】時給它的【WndProc】查起。
            如果你傳遞給它的不是一個真正的函數,而是一個thunk,這個thunk就會被調用。

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 15:45
            果然沒有錯誤處理是邪惡的……

            經過Loaden的強力測試(其實也就隨手一測),崩了……

            先上代碼:
            time_t t1 = time(0);
            tm t2 = *localtime(&t1);
            t2.tm_hour = 348294832;
            t1 = mktime(&t2);
            printf("%d\n",(int)t1);
            tm* pt3 = localtime(&t1);
            printf("%p\n",(void*)pt3);

            t2.tm_hour = 348294832; // 這里對于32位的int來說,沒有溢出。
            但hour的"權值"比較重,最后轉化為time_t的表達可能會溢出。


            t1 = mktime(&t2);
            printf("%d\n",(int)t1);
            C89要求:mktime如果不能將tm轉換到正確的time_t格式,就返回(time_t)-1。

            而vc8的運行庫不是這么處理的,它會直接崩掉,而不是返回-1。
            觸發的斷言是:
            loctime64.c (78)
            (*ptime <= _MAX_TIME64_T)

            所以,在vc8下,mktime那行代碼就會崩。
            在vc9、mingw3.4.x下,返回-1。


            tm* pt3 = localtime(&t1);
            printf("%p\n",(void*)pt3);
            如果time_t不是正確格式,C89好像沒說應該怎么做……
            vc8、vc9、mingw3.4.x一致返回空指針。


            再看date的代碼:

            int get_(int tm::*field) const
            {

            return localtime(&impl_.time_)->*field;
            //當time_t不正確時,返回空指針,然后被解引用,崩。

            }

            date& set_(int tm::*field,int v)
            {

            tm t = *localtime(&impl_.time_);
            t.*field = v;
            impl_.time_ = mktime(&t);
            // 當t格式不正確時,vc8不會返回-1,而是直接崩。
            return *this;

            }


            錯誤處理還是需要的……
            不過這僅僅是示例代碼,還是不加了吧……
            以免影響清晰…… 突出重點……

            感謝Loaden的測試~_~

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 15:05
            @Loaden

            thunk不用調用,直接處理消息后,調用默認窗口過程。

            肯定要調用的……
            不然怎么處理消息?

            只是我沒看出你的代碼中哪句是調用……


            我喜歡將什么東西都放在類里。

            最大的毛病:這會將優秀的算法埋葬到類中。


            強調一下,是"零"代價,不是低代價~_~
            union base, union member是低代價的。
            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 14:31
            @Loaden
            忘寫了…… 聯系方式,同名gmail郵箱……


            CWindow* w = (CWindow*)GetWindowLongPtr( ... );
            這個調用不僅僅是返回一個值而已。
            而且不同的窗口類,要返回不同的值。

            你的意思應該是,不同窗口“實例”,返回不同的值,是這樣嗎?
            那就對了,GetWindowLongPtr( hwnd, ... );本來就是不同窗口實例返回不同的值嘛……



            所以,這里編譯器如何優化呢?

            我說的優化是它下面的一個調用。
            w->handle( ... ); // 可能會被inline,避免參數傳遞。

            因為編譯器知道這里肯定調用的是handle,不會是其他函數。

            但thunk,調用的地址是運行時的,所以編譯器無法優化。



            DEP的問題,我想過內存池來實現。
            不過由于技術不過硬,暫時還沒想過去實現它。

            先把GUI框架穩定下來再說。
            謝謝你的指點!如果有好的解決thunk的內存池(ATL就是這么干的,只是不開源),請介紹一個給我。

            其實 …… atl的源代碼是能看到的…… 至少thunk部分是這樣。
            我上面有一個回帖, 估計你看掉了。
            如果只想支持win32, 可以用HeapAlloc那套。
            一個版本中的atl就是這么干的,還有個版本是自己實現的池 —— 具體記不太清楚了。


            如果想在posix平臺下,得到分配可執行內存的高效的池 ……
            等過年吧,哈哈哈



            而thunk,從其匯編代碼可以看到,代價只是多花1~3條CPU指令而已。
            顯然要高效。

            你看到thunk的分配了嗎? 考慮到thunk的復雜性了嗎?
            要全局的看~_~
            cbExtra可以被實現得很高效。當然,我只是猜……



            另外,static函數、全局函數也不利于C++封裝。

            free-function才是范化形式……
            member-function只是free-function的一個特例。
            是否封裝,是看你是否僅通過被數據認可的操作去使用數據,而不是自己去撥弄數據。
            跟語法無關。 語法只是個便利。

            這個話題…… 估計要扯很遠了……
            你不接受很正常,我可以理解……
            我只是提一下……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 13:54
            @Loaden

            Set/GetWindowLongPtr
            還有前幾天學到的…… Set/GetProp
            由于使用static函數,效率是一個大問題,而且有潛在的危險性。


            static 函數怎么影響效率了? 多傳遞了一次參數?
            有缺陷的回調肯定是C的,參數中肯定都是bitwise-copy。
            所以,還好吧……

            其實你需要的就是一個context而已。
            thunk, Set/Get WindowLongPtr/Prop 都是設置/得到context的手段。

            thunk的效率不見得比Set/Get WindowLongPtr高,而且用著很麻煩。

            WndProc(HWND hwnd,UINT msg,WPARAM w,LPARAM l)
            {

            CWindow* w = (CWindow*)GetWindowLongPtr( ... );
            // 編譯器知道這個調用處的上下文,可以進行優化
            return w->handle(hwnd, msg, w, l );

            }


            thunk的話,要么每個窗口對應一個窗口類,該窗口類對應一個thunk。
            要么若干窗口對應同一個窗口類,每個窗口再
            SetWindowLongPtr(hwnd, GWL_PROC , ) 去改窗口處理過程。

            調用一個chunk是絕對得不到優化的,因為編譯器根本就不可能知道thunk的目的地在哪。

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 13:44
            @Loaden
            兄弟,你知道這會分配多少嗎?

            DEP不是問題,很容易解決:
            void* operator new(size_t size)
            {
            return ::VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            }


            這里用new placement也不太妥當。

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 13:42
            補充一下: 關于DEP ,做thunk可能會用到。

            win32可以用win32 堆(HeapAlloc那套),創建時可以指定內存權限。
            這幾乎是最省心的做法了。

            不過我想做posix移植(論文啊…… thunk已經爛大街,不拿點新東西出來怎么過得去……)
            所以就要自己實現 ……
            做著做著嘛…… 發現內存池可以再分出幾部分……

            比如其中的一部分是intrusive data structure。
            小對象內存池其實只是intrusive date structure的使用方式之一。

            然后打算重構,然后…… 就畢業了…… 然后就無限延期了……

            過年之后再做吧……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 12:38
            @Loaden
            對框架設計,我不熟…… 因為一般來說,我討厭那個……

            為什么?
            框架和類庫有什么區別?
            類庫是幫助你的,框架是限制你的……

            作為一個框架的設計者,他必須對這個領域十分了解:哪些過程是常用的,哪些是有害的。
            才能準確的做出限制。
            我對gui編程經驗不多,所以就不做這方面的活了……



            說說win32下gui庫/框架必須解決的一個問題吧: user_data,或者叫context。
            因為那個萬惡的WndProc沒有傳給你……

            thunk是一種辦法,一種i386(其他平臺我不知道怎么實現)上通用的辦法 —— 只要是因為回調設計不正確,沒有帶context參數,就可以用thunk解決。
            實際上…… 我畢業設計就是做的這個……
            __stdcall也不是必須的…… __cdecl也可以……
            比如qsort, 它就是需要一個 __cdecl的。

            這里有一部分代碼:
            http://code.google.com/p/callback-currying/

            主要是做自由函數的thunk,而不是成員函數。
            一方面是可以給C程序員用。
            第2個原因是因為this和成員函數指針的可移植性很糟糕。
            第3個原因是因為可以通過自由函數,轉發到成員函數中,對成員函數的支持不是絕對必要的。

            直接thunk成員函數有一個好處。我論文里面有提到。
            返回一個大結構體的函數,直接thunk成員函數可以,thunk自由函數不行……

            stdcall2stdcall 需要12字節
            cdecl2cdecl 需要23字節
            thiscall2stdcal(msvc) 需要10字節
            其他記不清楚了


            設計好thunk結構體,只是關鍵技術之一。
            之后還有很多很多的麻煩…… 主要是DEP(數據執行保護)

            在win32下可以用VirtualAlloc來分配可執行內存,posix下使用mmap。
            但是…… 不可能為了這么小一塊內存,就去分配1整頁吧? win32下還要保留64k……
            內存池是必須做的…… 而且現有內存池都無法被復用

            所以,那個gcode后來就沒有維護了。
            svn管理實驗代碼很麻煩…… 轉git了……

            最后的版本還沒有敲定,等過年后我會發一個版本出來……

            named template parameter就是為了解決callback currying中模板參數混亂(不僅是多,真的是混亂,論文里面好像有詳細解釋)而設計的。



            跑題了…… 上面的中心思想是: thunk是一種通用技術,但麻煩多多。
            如果僅僅是為了解決WndProc, 有專有技術。
            Set/GetWindowLongPtr
            還有前幾天學到的…… Set/GetProp
            見這里:http://www.shnenglu.com/kyelin/archive/2009/11/13/100880.aspx

            需要注意的是,Set/GetWindowLongPtr網上很多文章都說錯了。
            具體的使用方式去查msdn。
            cbExtra是一個0 based array —— 我記得msdn是有這么一句話的。

            GWL_USERDATA是另外一回事。


            用這2種方案之一就可以了(或者win32還提供了其他附加數據的方案?)。
            mfc的線程局部映射表…… 哎……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 12:19
            @矩陣操作
            union里面好像不能出現private的東西。具體得查標準……

            我那個,也是權宜之計……
            客戶代碼訪問不了這個類型:date::impl
            但可以被推導……
            template<typename T>
            void set_default(T& v) { v = T(); }

            date d;
            set_default(d.impl_); // 掛了


            不過,private也不是萬能的……
            指針算術,宏,都有可能讓它失效。
            取名為impl_, 應該能起到警示作用 —— 不要使用這個名字 —— 吧……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 04:43
            為了避免vczh同學誤會……
            我再申明一下……

            1. 這是徹底的玩具代碼
            所以對日期的合法性不作檢查……

            2. 代碼還需在頂部加入兩行:
            #include <time.h>
            #include "property.hpp"
            才算完整

            3. "property.hpp"
            的搜索路徑需要自己配置,或者就和date.cpp放一起。

            4. 需要在<time.h>前加入:
            #define _CRT_SECURE_NO_DEPRECATE
            才能避免msvc上常見的4996警告。

            5. 或者另一種方式,代碼行數較多:
            #ifdef _MSC_VER
            #pragma warning(disable: 4996)
            #endif

            6. main的return 0可以省略
            因為一定是C++代碼,也不用兼容vc6



            能想到的我都說了……
            你就繞了我吧……

            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-18 04:18
            差不多敲定方案了,貼下代碼吧:
             
            class date
            {
                    
            class impl
                    {
                            time_t time_;
                            friend 
            class date;
                    };

                    
            int get_(int tm::*field) const { return localtime(&impl_.time_)->*field; }
                    date
            & set_(int tm::*field,int v)
                    {
                            tm t 
            = *localtime(&impl_.time_);
                            t.
            *field = v;
                            impl_.time_ 
            = mktime(&t);
                            
            return *this;
                    }

            public:
                    date() { time(
            &impl_.time_); }

                    
            int get_hour() const { return get_(&tm::tm_hour); }
                    date
            & set_hour(int h) { return set_(&tm::tm_hour,h); }

                    
            int get_minute() const { return get_(&tm::tm_min); }
                    date
            & set_minute(int m) { return set_(&tm::tm_min,m); }

                    
            int get_second() const { return get_(&tm::tm_sec); }
                    date
            & set_second(int s) { return set_(&tm::tm_sec,s); }

                    union
                    {
                            impl impl_;

                            property::proxy
                                    
            <property::get<date,int,&date::get_hour>
                                    
            >               hour_read;

                            property::proxy
                                    
            <property::set<date,date&,int,&date::set_hour>
                                    
            >               hour_write;

                            property::proxy
                                    
            <property::get<date,int,&date::get_minute>
                                    ,property::
            set<date,date&,int,&date::set_minute>
                                    
            >               minute; // read write

                            property::proxy
                                    
            <property::set<date,date&,int,&date::set_second>
                                    ,property::
            get<date,int,&date::get_second>
                                    
            >               second; // read write
                    };
            };

            int main()
            {
                    typedef 
            int ASSERT_EQUAL_SIZE[sizeof(date)==sizeof(time_t)?1:-1];

                    date d;
                    
            volatile int v = 1212;
                    
                    v 
            = d.hour_read;
                    d.hour_write 
            = v;
                    
            // v = d.hour_write;
                    
            // d.hour_read = v;

                    v 
            = d.minute;
                    d.minute 
            = v;

                    v 
            = d.second;
                    d.second 
            = v;

                    
            return 0;
            }

             
            這次首先重點show出來:
            1. 0代價實現proxy
            typedef int ASSERT_EQUAL_SIZE[sizeof(date)==sizeof(time_t)?1:-1];
             
            2. 定義property的語法也還不算太壞吧?
            使用一種"named template parameter"技巧,將read、writer、read-write合體。
            hour_read, hour_write不用解釋了。
            second,minute需要解釋一下:set和get出現在proxy的參數中出現的次序是隨意的。
            所謂named parameter是也。
             
            不要拿hour_read用于寫或者拿hour_write用于讀,報出的錯誤可能很難讀。
            有空閑再處理報錯好看問題吧……
             
             
             
            proxy的其他技巧有請vczh同學介紹。
             
             
             
             
            關于union中不能放非pod的問題……
            1. 基于C-API來構造自己的類
            那自然沒什么問題。
            如果還想進行訪問控制,就像date::impl一樣,包到一個private nested class中,并設置外部class為friend。
             
            依然0代價。
             
             
            2. 基于現有的非pod來構造自己的類
            樓上已經提到了union base和union member兩種方案。
            不過property::proxy不支持這種用法,得手工實現……
            遇到的問題也是樓上提到的"先雞還是先蛋"……
            為了解決雞蛋問題,想了很久都沒想出可以很清晰定義property的方案……
             
            換種思路:使用boost.aligned_storage + placement new + 直接調用析構函數吧……
            也算一種解決辦法了…… 而且也是0代價的。
             
             
             
            玩具代碼,僅為展示上面的2個重點技術而已。實際應用還需自行編寫。
            property我以前也沒用過…… 沒什么經驗,不知道通常會怎么使用。
             
            測試編譯器:msvc8、9、10,gcc3.4.x。
            寫命名模板參數就沒打算支持vc6……  不測了……
            gcc4.x懶得切換系統了…… 應該沒什么問題……
             
            re: GUI框架:談談框架,寫寫代碼 OwnWaterloo 2009-11-17 21:28
            @visualfc
            謝謝分享~_~

            英文…… sigh……

            共10頁: 1 2 3 4 5 6 7 8 9 Last 
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(8)

            隨筆檔案(16)

            鏈接

            搜索

            •  

            積分與排名

            • 積分 - 197925
            • 排名 - 133

            最新隨筆

            最新評論

            閱讀排行榜

            評論排行榜

            久久成人小视频| 九九99精品久久久久久| 久久91精品国产91久久小草| 日日狠狠久久偷偷色综合免费 | 国产精品美女久久久久| 久久九九兔免费精品6| 无码任你躁久久久久久| 亚洲一级Av无码毛片久久精品| 亚洲AⅤ优女AV综合久久久| 日日狠狠久久偷偷色综合0| 亚洲午夜精品久久久久久app| 久久久WWW成人免费毛片| 久久精品国产亚洲5555| 亚洲欧洲精品成人久久曰影片| 久久亚洲sm情趣捆绑调教| 99久久无色码中文字幕人妻| 久久99国产乱子伦精品免费| 久久免费精品视频| 国产精品久久久久久久午夜片| 久久无码精品一区二区三区| 亚洲欧洲中文日韩久久AV乱码| 久久久国产精华液| 996久久国产精品线观看| 久久精品国产福利国产琪琪| 久久亚洲国产最新网站| 久久人爽人人爽人人片AV| 精品久久久久中文字| 久久久久久国产精品美女| MM131亚洲国产美女久久| 久久久久女教师免费一区| 天天爽天天狠久久久综合麻豆| 精品综合久久久久久97超人 | 久久精品中文字幕大胸| 狠狠88综合久久久久综合网| 欧美激情精品久久久久久久| 亚洲欧美日韩中文久久| 成人国内精品久久久久影院VR| 精品久久亚洲中文无码| 国产伊人久久| 中文字幕乱码久久午夜| 久久精品国产亚洲Aⅴ香蕉|