背景
我正在研究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 ++。因此,我想请这里的专家向我指出,如果我犯了那些没有经验的设计师和程序员倾向于犯的错误。提前谢谢。