2014-09-01 65 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