2014-09-01 64 views
1

一個典型的訪問者模式的設計是這樣的:如何阻止訪客?

template<class Visitor> 
void processData(Visitor& visitor) 
{ 
    // maybe in sequence 
    visitor.process(...); 
    visitor.process(...); 
    ... 

    // may have expensive computations 
    doExpensiveComputations(); 

    // or in some loops 
    for (...) 
     visitor.process(...); 

    // or in some recursive calls 
    recursive(visitor); 
} 

怎能遊客儘快返回,當它失去其餘數據的利益(例如,回答被發現)?

請注意,任何需要更改設計的內容都是不可接受的,例如,我不想強​​制Visitor :: process返回值並每次檢查一次。

我真正想要做的是強制堆棧展開,我可以使用異常,但它已被告知使用異常控制流是反模式。

我想Boost.Coroutine可能會有幫助,但它仍然使用例外棧展開......

我現在要做的就是象下面這樣:

struct Visitor 
{ 
    void process(T data) 
    { 
     if (stopped) 
      return; 
     ... 
    } 
    ... 
}; 

但是,這仍然會往下走的路的執行,以及我們不需要的昂貴的計算。

由於除了可以展開堆棧的異常外,在C++中沒有其他可移植的方式,所以我應該在這裏使用異常來控制流?

+0

什麼是「改變設計」? – 2014-09-01 10:07:24

+2

「使用控制流的異常是反模式」 - 選擇你的毒藥 - 創建一個更靈活的「訪問者」,以便儘早返回代碼,使用異常,或讓它變慢。良好的編程是關於挑選全能最好/最差的,並且當你不得不做很顯着的事情時添加有價值的文檔和防禦性代碼。 – 2014-09-01 10:45:16

+0

這是一個「反模式」並不一定意味着它總是錯的。這聽起來像是這種情況之一,當它不是一件壞事。以及boost.coroutine如何實現*不應該影響您的代碼是否使用它;這是將代碼封裝在庫中的一點。如果它適合你的目的,那麼這可能是最好的選擇。 – molbdnilo 2014-09-01 11:21:39

回答

2

請注意,任何需要更改設計的內容都是不可接受的,例如,我不想強​​制Visitor :: process返回值並每次檢查一次。

在這種情況下,「設計」,我會假設你的意思是「Visitor :: process的簽名」。

我真正想做的是強制堆棧展開,我可以使用異常,但它已被告知使用異常控制流是反模式。

我應該在這裏使用異常控制流程?

在這裏使用異常不一定是反模式(儘管它是一個「中斷執行」系統 - 可能是錯誤或不)。我曾經遇到過這種情況(「使用異常來表示其他信息而不是錯誤」),並且我使用了一個可以接受的類型(它不是直接或間接地從std :: exception繼承)。我的指導原則是「如果它從std :: exception繼承,那是一個錯誤;否則,它是一個處理不會繼續的信號」。

考慮爲您例如此實現:

struct ProcessingInterrupted final {}; // <--- thin/empty implementation 
             // not inheriting std exceptions 
             // and not inheritable 

struct Visitor 
{ 
    void process(T data) 
    { 
     if(worldEnds) 
      throw ProcessingInterrupted{}; 
     // ... 
    } 
    ... 
}; 

template<class Visitor> 
bool process(Visitor& visitor) 
{ 
    try 
    { 
     processData(visitor); // taken from your example 
     return true; 
    } 
    catch(const ProcessingInterrupted&) 
    { 
     return false; 
    } 
} 

// client code 
Visitor v; 
/* auto success = */ process(v); 

這樣,ProcessingInterrupted類型告訴你到底是什麼在你的榜樣回事(「處理中斷」)和客戶端代碼(這兩個過程數據並在我的過程)看起來簡約,並有明確的目的。

+0

的確,至少這也是Boost.Thread和Boost.Coroutine所做的。 – Jamboree 2014-09-02 01:30:42

0

您可以禁用boost.coroutine的堆棧展開(類屬性) 示例/ cpp03/asymmetric/same_fringe.cpp顯示遍歷樹的變體遞歸 - 也許它會幫助你。

+0

不是,我需要的是堆棧展開,而不是協程。順便說一句,你有沒有考慮過使用一些展開ABI如果支持,並回退到boost.coroutine的異常呢? – Jamboree 2014-09-02 09:22:15

+0

@Jamboree:展開ABI(如Itanium C++ ABI - > __Unwind_ 函數)通過​​異常退出。 – olk 2014-09-18 06:33:05