一种非常灵活的访问者模式,没有运行时开销

时间:2015-12-06 08:11:33

标签: c++ c++11

背景

我正在研究A*算法的实现,学术研究人员(例如我自己)将使用该算法来试验A*的新变种。两个主要的设计目标是:总灵活性(即可以实现任何变体)和没有运行时开销,这意味着所有用户定义的处理程序必须在编译时绑定。

我的第一次尝试是使用现代C ++设计(Alexandrescu,2001)所描述的基于策略的设计,但提出正交策略很快成为挑战。

在这篇文章中,我想分享一个我非常兴奋的新设计 。此设计特定于A*,因此可能与其他人相关。

设计目标

起点是访问者,当然,有些类似于Boost Graph Library用于其算法的访问者。 visitor 为算法生成的一个或多个事件指定处理程序。出于本项目的目的,处理程序必须具有以下属性:

  • 为了获得最大的灵活性,处理程序必须能够访问算法提供给访问者的数据。

  • 处理程序在编译时绑定到事件。

  • 处理程序可以返回一个为算法提供反馈的值。 (在A*的情况下,当选择一个节点时,该事件的处理程序可能会告诉算法该节点不应该展开,但是应该重新插入 Open List ,例如因为处理程序能够增加节点的成本)。处理程序也可以返回void,在这种情况下,处理返回值不应该有运行时成本。

实施

我们为那些什么都不做的处理程序的访问者定义了一个基类:

struct VisitorBase {
    template <class Algorithm>
    static void OnEvent1(Algorithm &alg) {(void)alg;}

    template <class Algorithm>
    static void OnEvent2(Algorithm &alg) {(void)alg;}
};

alg参数可让访问者访问:

  • 算法提供的所有数据和

  • 事件的返回类型。

用户可以按如下方式定义访问者:

struct UserVisitor: public VisitorBase {
    template <class Algorithm>
    static typename Algorithm::ResultOfHandlingEvent1 OnEvent1(Algorithm &alg) {
        std::cout << "In VisitorForEvent1! data1 = " << alg.data1 << std::endl;
        return Algorithm::ResultOfHandlingEvent1::EVENT1_ACTION1;
    }
};

这是一个定义访问者可用内容的类:

struct AlgorithmBase {
    enum class ResultOfHandlingEvent1 {EVENT1_ACTION1, EVENT1_ACTION2};
    enum class ResultOfHandlingEvent2 {EVENT2_ACTION1, EVENT2_ACTION2};
    int data1 = 5;
};

最后,算法看起来像这样:

template <class Visitor>
struct Algorithm: AlgorithmBase {
    void run() {
        constexpr bool ReturnTypeFlagOfOnEvent1 =
            std::is_same<decltype(Visitor::OnEvent1(*this)),
                         AlgorithmBase::ResultOfHandlingEvent1>::value;
        handleEvent1(std::integral_constant<bool, ReturnTypeFlagOfOnEvent1>());

        constexpr bool ReturnTypeFlagOfOnEvent2 =
            std::is_same<decltype(Visitor::OnEvent2(*this)),
                         AlgorithmBase::ResultOfHandlingEvent2>::value;
        handleEvent2(std::integral_constant<bool, ReturnTypeFlagOfOnEvent2>());
    }

private:
    void handleEvent1(std::true_type) {
        auto res = Visitor::OnEvent1((AlgorithmBase &)*this);
        std::cout << "Will switch on the result of handling event 1!"
                  << std::endl;
        (void)res; // but a switch statement will appear here
    }
    void handleEvent1(std::false_type) {
        Visitor::OnEvent1((AlgorithmBase &)*this);
        std::cout << "Nothing to be done for the result of handling event 1!"
                  << std::endl;
    }

    void handleEvent2(std::true_type) {
        auto res = Visitor::OnEvent2((AlgorithmBase &)*this);
        std::cout << "Will switch on the result of handling event 2!"
                  << std::endl;
        (void)res; // but a switch statement will appear here
    }
    void handleEvent2(std::false_type) {
        Visitor::OnEvent2((AlgorithmBase &)*this);
        std::cout << "Nothing to be done for the result of handling event 2!"
                  << std::endl;
    }
};

让我们试一试:

int main() {
    Algorithm<UserVisitor> a;
    a.run();
    return 0;
}

我们得到了输出:

In VisitorForEvent1! data1 = 5
Will switch on the result of handling event 1!
Nothing to be done for the result of handling event 2!

请求

我自己是研究员,正在为这个项目学习C ++。因此,我想请这里的专家向我指出,如果我犯了那些没有经验的设计师和程序员倾向于犯的错误。提前谢谢。

0 个答案:

没有答案