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

cexer

cexer
posts - 12, comments - 334, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

轉帖請注明出處 http://m.shnenglu.com/cexer/archive/2008/07/06/55484.html

  VC當中有一個鮮為人知的關鍵字,除了微軟自己的代碼,我從未在任何地方看到有人用過它。雖然它的功能很強大,不過除非設計上的問題或是一些無法排除的困難,否則幾乎從不會需要用到它的功能。但是有時候,它確實能作為一個最簡單的解決方案而讓某些設計過程事半功倍。

  借用 CCTV10《走近科學》的語氣:那么這個神秘的關鍵關鍵字到底是什么呢?它又實現了什么神奇的功能呢?帶著這一連串的疑問,讓我們先來看一個具體的例子。

  我在自己曾經寫的一個GUI框架當中,為了實現消息與處理函數自動映射的,就需要求助于這種功能。比如說有一個窗口類,它包含若干消息處理函數和一個消息與處理函數的映射 map:(請無視當中的 show() 和 create() 函數,與主題無關)

    class Window
{
typedef UINT _Message;
typedef LRESULT (Window::*_Handler)(_Message);

map<_Message,_Handler> m_handlerMap;

public:
bool show();
bool create();

public:
LRESULT onEvent( WindowEvent<WM_CREATE> );
LRESULT onEvent( WindowEvent<WM_DESTROY> );
};

  
  我需要利用模板元編程 從 0 到 WM_USER  進行循環檢測,檢測 Window 類是否存在該消息對應的處理函數。如果消息對應的處理函數存在,那么就將消息與函數的映射放進 m_handlerMap 當中。比如說消息 WM_CREATE,我檢測類 Window是否存在 LRESULT onEvent( WindowEvent<WM_CREATE> ) 成員函數,在上例代碼中是存在的,于是我將這樣一個映射放進m_handlerMap:(真正實現的時候,還要考慮函數的類型。不同類型的函數,是不能直接裝進 map 當中的。不過在這里請無視例子當中涉及的所有類型轉換,與主題無關)

    pair<WM_CREATE,&Window::onEvent>


  這樣就達到了消息自動映射的目的。而不用像MFC一樣手寫宏去映射。(最后通過努力的確達到了我的目的,我的GUI框架能夠進行自動消息映射了,然而可以預見,由于幾千個(0-WM_USER)循環,編譯期的速度受到極大影響。所以最終我還是拋棄了這種自動映射實現,而采用了更高效神奇的方法,這是后話也與本主題無關就先不提)。

  要實現以上的自動映射功能就引出了這樣一個難題:如何編譯期檢測類的某特定名字的成員是否存在。

  功能不負有心人,經過爬山涉水翻山越嶺,我終于在 MSDN 一個偏遠角落里找著了傳說當中那個神秘的關鍵字:__if_exists(其實還有一個 __if_not_exists)。MSDN 當中這樣說明:__if_exists (__if_not_exists)允許你針對某符號的存在與否條件性地執行語句。使用語法:(注意檢測的是“存在性”,而不是值)

    __if_exists ( /*你要檢測存在性的函數或變量的名字*/ ) { 
 //做些有用的事
}


  MSDN當中的示例代碼如下:
    // the__if_exists_statement.cpp
// compile with: /EHsc
#include <iostream>

template<typename T>
class X : public T {
public:
void Dump() {
std::cout << "In X<T>::Dump()" << std::endl;

__if_exists(T::Dump) {
T::Dump();
}

__if_not_exists(T::Dump) {
std::cout << "T::Dump does not exist" << std::endl;
}
}
};

class A {
public:
void Dump() {
std::cout << "In A::Dump()" << std::endl;
}
};

class B {};

bool g_bFlag = true;

class C {
public:
void f(int);
void f(double);
};

int main() {
X<A> x1;
X<B> x2;

x1.Dump();
x2.Dump();

__if_exists(::g_bFlag) {
std::cout << "g_bFlag = " << g_bFlag << std::endl;
}

__if_exists(C::f) {
std::cout << "C::f exists" << std::endl;
}

return 0;
}


  以上代碼的輸出如下:(未測試,此輸出為MSDN的說明文檔當中的)

    In X<T>::Dump()
In A::Dump()
In X<T>::Dump()
T::Dump does not exist
g_bFlag = 1
C::f exists


  大概很少人見過這個關鍵字吧。雖然它們的功能與我的需求是如此的接近,但是面對如此強憾的關鍵字,我還是只能搖頭嘆息。我傷心地在文檔里看到說明,__if_exists(__if_not_exists)關鍵字用于函數的時候,只能根據函數名字進行檢測,而會忽略對參數列表的檢測,因此沒有對重載函數的分辨能力,而正是我需要的。比如類 Window 有一個函數:

    LRESULT Window::onEvent( WindowEvent<WM_DESTROY> )
{
//做些有用的事
}


  我用以下代碼來檢測 WM_CREATE 消息是否存在處理函數:

    __if_exists(Window::onEvent)
  {
      //添加消息映射
   }


  即使 Window 類當中不存在 LRESULT onEvent ( WindowEvent<WM_CREATE> ),以上測試也能通過。這是因為 __if_exists 關鍵字是不管函數重載的,如果存在一個 onEvent ,那么所有的檢測都能通過。這不是我想要的。我需要比 __if_exists 更強憾的檢測功能,強憾到能夠針對不同參數列表的同名函數(重載函數)做出正確的存在性測試。

  于是我繼續翻山越嶺地尋找,從 CSDN 到 MSDN,從 SourceForge 到 CodeProject。要相信那句老話:“有心人天不負”。最后我在 CodeProject 上面看到一篇讓我醍醐灌頂的文章:

  Interface Detection by Alexandre Courpron

  這篇文章從原理到實現,很詳細地說明地一種編譯期檢測技術,先說明一下,由于VC7.1數千個bug當中的一個,以下技術不能在VC++7.1或更低版本上使用。具體的實現在那篇文章當中說得很詳盡了,還是在這兒贅述一下。

  Alexandre Courpron的實現方式基于C++的這樣一個規則:Substitution Failure Is Not An Error(簡稱SFINAE)。它的含義我也理解得比較含糊,不過它作用于重載函數的時候,可以這樣理解:對于一個函數調用,在匹配函數的過程當中,如果最終能夠有一個函數匹配成功,那么對其余函數的匹配如果失敗,編譯器也不會視為錯誤。聽起來有些麻煩,看Alexandre Courpron給出的例子:

    struct Test 
{
typedef int Type;
};

template < typename T >
void f(typename T::Type) {} // definition #1

template<typename T>
void f(T){} // definition #2

f<Test>(10); //call #1

f<int>(10); //call #2

  
  對于 call#1 編譯器直接匹配 definition#1 成功。對于 call#2,編譯器先用 definition#1 匹配 如下:

    void f( typename int::Type ) {}


  這顯然是不正確的。不過編譯器并沒有編譯失敗報告錯誤,因為下面的 definition#2 匹配成功,根據 SFINAE的 規則,編譯器有權保持沉默 。

  雖然是個小小的規則,在平時幾乎不會注意它。然而在這兒,我們卻可以利用它實現編譯期檢測的強大功能了,一個最簡單的示例:

    #include <iostream>
using namespace std;
//
struct TestClass
{
void testFun();
};

struct Exists { char x;};
struct NotExists { char x[2]; };

template <void (TestClass::*)()>
struct Param ;

template <class T>
Exists isExists( Param<&T::testFun>* );

template <class T>
NotExists isExists( ... );
//
int main()
{
cout<<sizeof(isExists<TestClass>(0))<<endl;
}


  上面的代碼會輸出 1。說明一下檢測的過程:

  1. 編譯器遇到 isExists<TestClass>(0) 這一句,會去匹配 isExists 的兩個重載函數。不定長的參數優先級更低,因此先匹配第一個函數。
  2. 第一個函數參數類型為 Param<&T::testFun>*,在這里是 Param<&TestClass::testFun>,編譯器在匹配這個參數類型的時候會嘗試實例化模板類 Param。
  3. 編譯器嘗試用 &TestClass::testFun 去實例化 Param,因為 TestClass 確實存在一個 void (TestClass::*)() 類型,且名為 testFun 的成員函數。所以 Param 的實例化成功,因此參數匹配成功。
  4. 匹配第一個函數成功。編譯器決定 isExists<TestClass>(0) 這一句調用就是調用的第一個函數。
  5. 因為第一個函數返回的類型為 Exists,用 sizeof 取大小就是 1。

  如果是我們把 TestClass 的定義修改為:(僅把函數的參數類型改為 int )

    struct TestClass
{
void testFun(int);
};


  這一次代碼會輸出 2。因為在第3步的時候,由于 TestClass 沒有類型為 void (TestClass::*)(),且名為 testFun 的函數,所以實例化 Param 會失敗,因此匹配第一個函數失敗。然后編譯器去匹配第二個函數。因為其參數類型是任意的,自然會匹配成功。結果會輸出 2。

  當然這只是個最簡單的示例,通過模板包裝類。可以實現更靈活更強大的功能。比如回到那個自動消息映射的例子,用以下代碼就能夠實現了:

//c++std
#include <iostream>
using namespace std;



//windows
#include <windows.h>


//detector
template<typename TWindow,UINT t_msg>
struct MessageHandlerDetector
{
typedef WindowEvent<t_msg> _Event;

struct Exists {char x;};
struct NotExists {char x[2];};

template<LRESULT (TWindow::*)(_Event)>
struct Param;

template<typename T>
static Exists detect( Param<&T::onEvent>* );

template<typename T>
static NotExists detect( ... );

public:
enum{isExists=sizeof(detect<TWindow>(0))==sizeof(Exists)};
};

//test classes
struct Window
{
LRESULT onEvent( WindowEvent<WM_CREATE> );
};

struct Button
{
LRESULT onEvent( WindowEvent<WM_DESTROY> );
};

//main
int main()
{
cout<<MessageHandlerDetector<Window,WM_CREATE>::isExists<<endl;
cout<<MessageHandlerDetector<Window,WM_DESTROY>::isExists<<endl;
cout<<MessageHandlerDetector<Button,WM_CREATE>::isExists<<endl;
cout<<MessageHandlerDetector<Button,WM_DESTROY>::isExists<<endl;

return 0;
}



  以上代碼會輸出:

    1
0
0
1


  以上的示例代碼再加上模板元編程,可以很輕易地實現消息的自動映射,具體實現這個已不在本貼的討論范圍并且這種自動映射的實現,太過復雜,在編譯期沒有效率,且不夠靈活。不過在消息映射機制上來說,已稱得上是一種革命性的嘗試。

  在說完了這所有一切之后,再告訴你一個我最近才知道的秘密(不準笑我孤陋寡聞):其實 boost 庫當中已有相關功能的 MPL  工具存在,叫做 has_xxx。

  源文件:<boost\mpl\has_xxx.hpp>

  文檔:http://www.boost.org/doc/libs/1_35_0/libs/mpl/doc/refmanual/has-xxx-trait-def.html

Feedback

# re: C++編譯期函數/變量檢測技術,超越VC的某某關鍵字  回復  更多評論   

2008-07-06 23:54 by Bill Gates
不就是traits嗎

# re: C++編譯期函數/變量檢測技術,超越VC的某某關鍵字  回復  更多評論   

2008-07-06 23:56 by cexer
不是。并不是涉及模板就能用traits來說事的。

# re: C++編譯期函數/變量檢測技術,仿真VC關鍵字__if_exists  回復  更多評論   

2008-07-08 18:29 by 周星星
好方法呀,俺也一直在尋找 __if_exists 的替代方法(用標準C++語法)

# re: C++編譯期函數/變量檢測技術,仿真VC關鍵字__if_exists  回復  更多評論   

2008-07-08 19:50 by cexer
你可以看看,boost有更好的實現。

# re: C++編譯期函數/變量檢測技術,仿真VC關鍵字__if_exists  回復  更多評論   

2008-07-10 23:13 by 夢在天涯
沒用過,有難度,長見識!

# re: C++編譯期函數/變量檢測技術,仿真VC關鍵字__if_exists  回復  更多評論   

2008-08-19 12:47 by 螞蟻終結者
不錯,有點意思。

# re: C++編譯期函數/變量檢測技術,仿真VC關鍵字__if_exists  回復  更多評論   

2009-09-24 11:20 by pipilu
問一下怎么編譯器比較兩個常數來設置編譯呢

#define AAA 12

// 如果 AAA > 12 則編譯下面內容
bool bUse = 134;

# re: C++編譯期函數/變量檢測技術,仿真VC關鍵字__if_exists  回復  更多評論   

2012-06-01 01:09 by 春秋十二月
不錯 受益了

# re: C++編譯期函數/變量檢測技術,仿真VC關鍵字__if_exists  回復  更多評論   

2013-05-30 14:01 by zirandeai
我覺得這只是語法糖,增加了復雜度,卻并未讓人看到實質性的好處
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美人与性动交cc0o| 欧美一区日本一区韩国一区| 香蕉久久a毛片| 亚洲黄色精品| 欧美综合国产精品久久丁香| 亚洲最黄网站| 日韩一级在线| 亚洲性感美女99在线| 99视频精品免费观看| 亚洲视频999| 欧美中文字幕视频在线观看| 久久久久久尹人网香蕉| 老**午夜毛片一区二区三区| 欧美福利专区| 国产精品99久久久久久久女警| 亚洲四色影视在线观看| 久久av红桃一区二区小说| 你懂的一区二区| 国产精品日韩久久久| 国语自产在线不卡| 亚洲午夜国产成人av电影男同| 亚洲欧美日韩综合国产aⅴ| 免费久久99精品国产自在现线| 亚洲欧洲精品成人久久奇米网| 在线视频日韩| 欧美国产视频一区二区| 国产精品日韩专区| 一区二区三区蜜桃网| 欧美激情四色| 国产伦精品一区二区三区照片91 | 亚洲在线网站| 女女同性女同一区二区三区91| 欧美日韩999| 激情久久一区| 亚洲一区二区免费看| 免费成人av| 久久久91精品国产一区二区三区 | 99亚洲一区二区| 欧美va天堂va视频va在线| 欧美专区中文字幕| 国内精品久久久久久久影视蜜臀| 欧美日韩精品免费观看视一区二区 | 国产精品丝袜久久久久久app| 亚洲国产一区二区a毛片| 久久久亚洲高清| 久久经典综合| 一区福利视频| 亚洲高清视频中文字幕| 裸体素人女欧美日韩| 亚洲黄色一区二区三区| 亚洲国产日韩一区二区| 欧美日韩系列| 欧美日本一区二区三区| 亚洲美女在线视频| 一本色道**综合亚洲精品蜜桃冫| 欧美不卡视频| 亚洲女爱视频在线| 性久久久久久久久久久久| 国产亚洲免费的视频看| 亚洲电影在线看| 国产精品日韩一区二区三区| 浪潮色综合久久天堂| 欧美日韩高清区| 久久深夜福利| 国产目拍亚洲精品99久久精品| 久久综合色婷婷| 国产精品久久久爽爽爽麻豆色哟哟 | 欧美日韩精品是欧美日韩精品| 中文一区二区在线观看| 欧美高清hd18日本| 美女脱光内衣内裤视频久久网站| 欧美日韩精品免费看| 欧美99久久| 永久久久久久| 欧美在线中文字幕| 欧美一区二区成人6969| 欧美日一区二区三区在线观看国产免| 久久国产精品第一页| 国产精品亚洲综合| 在线中文字幕不卡| 亚洲欧美色一区| 国产精品视频第一区| 亚洲一区二三| 久久不射电影网| 狠狠久久综合婷婷不卡| 欧美一区二区黄色| 久久久欧美精品sm网站| 亚洲理伦在线| 欧美在线黄色| 国产午夜精品一区二区三区欧美| 亚洲欧美日韩精品久久亚洲区 | 欧美日韩国产综合新一区| 久久只有精品| 亚洲国产网站| 在线成人性视频| 欧美三区在线视频| 免费中文字幕日韩欧美| 午夜久久黄色| 久久精品国产2020观看福利| 亚洲午夜在线观看| 欧美日韩亚洲成人| 欧美国产日韩亚洲一区| 久久男女视频| 欧美另类变人与禽xxxxx| 亚洲激情第一区| 亚洲激情成人网| 欧美一区国产二区| 在线亚洲自拍| 国产精品国产a| 午夜日韩电影| 亚洲茄子视频| 国产视频在线观看一区二区| 欧美精品色综合| 老司机久久99久久精品播放免费| 亚洲美女视频在线观看| 欧美.日韩.国产.一区.二区| 香蕉视频成人在线观看| 亚洲一区二区三区四区视频| 亚洲精品乱码久久久久久黑人| 国产欧美日韩一区二区三区在线观看 | 日韩视频第一页| 欧美伊人久久大香线蕉综合69| 国语自产精品视频在线看抢先版结局| 国产精品毛片| 欧美午夜精品久久久久久浪潮| 欧美日韩不卡合集视频| 欧美日韩国产不卡在线看| 欧美日韩国产综合新一区| 欧美日本国产一区| 国产精品亚洲视频| 原创国产精品91| 亚洲欧洲免费视频| 美国十次成人| 欧美国产乱视频| 国产精品任我爽爆在线播放| 国产欧美一区二区白浆黑人| 在线观看日韩一区| 性色av一区二区三区红粉影视| 校园激情久久| 久久一日本道色综合久久| 欧美精品大片| 国产一区二区精品久久| 亚洲精品123区| 欧美视频网址| 亚洲大胆人体视频| 性xx色xx综合久久久xx| 亚洲欧洲午夜| 久久精品综合| 国产精品久久波多野结衣| 一区二区在线不卡| 久久爱www| 午夜在线成人av| 欧美日韩精品免费观看视频完整| 国产一区二区三区免费在线观看| 91久久国产精品91久久性色| 欧美一区二区网站| 亚洲欧美在线aaa| 国产精品久久久久久久久果冻传媒 | 欧美成人国产| 在线精品观看| 国产一区二区中文| 性欧美长视频| 亚洲日本成人| 欧美国产在线视频| 亚洲专区一区| 性欧美暴力猛交另类hd| 亚洲精品女av网站| 亚洲理论在线| 国产日韩欧美高清免费| 免费在线观看一区二区| 欧美精品www在线观看| 校园春色国产精品| 亚洲日本成人| 午夜久久久久久久久久一区二区| 国产一区二区黄| 亚洲精品久久久久久下一站| 国产精品美腿一区在线看| 久久九九免费视频| 欧美精品久久久久久久久久| 久久激情五月激情| 欧美日韩国产影院| 亚洲国产精品一区二区尤物区| 欧美日韩一区二区欧美激情 | 久久久精品国产一区二区三区 | 欧美激情亚洲国产| 国产精品美女午夜av| 国产区日韩欧美| 欧美成人精品| 精品69视频一区二区三区| 亚洲欧美日韩系列| 亚洲一区二区av电影| 欧美高清视频| 欧美激情亚洲| 最新高清无码专区| 你懂的一区二区| 亚洲精品黄色| 亚洲精品午夜| 欧美日韩三级电影在线| 亚洲电影免费观看高清| 一区二区三区日韩|