如何使用boost::mpl来组合策略?

6

我曾使用以下类似的方式为我的应用程序编写策略:

策略类如下所示:

struct Policy {
  static void init();
  static void cleanup();
  //...
};

template <class CarT, class CdrT>
struct Cons {
  static void init() {
    CarT::init();
    CdrT::init();
  }
  static void cleanup() {
    CdrT::cleanup();
    CarT::cleanup();
  }
  //...
};

撰写策略:

typedef Cons<Policy1, Cons<Policy2, Cons<Policy3, Policy4> > > MyPolicy;

使用MyPolicy:

init_with<MyPolicy>(...);
//...
cleanup_with<MyPolicy>(...);

他们会在这里打电话:

MyPolicy::init_options(); // calls Policy1 to 4's init in order

并且

MyPolicy::cleanup(); // calls Policy1 to 4's cleanup in reverse order

实际上,这里 Cons 构造了一个类型列表。这很简单直接。然而,typedef cons 行有点丑陋。最好的情况是有一个策略组合器可以做到这一点:

typedef CombinePolicy<Policy1, Policy2, Policy3, Policy4> MyPolicy;

由于我们可以拥有任意数量的策略,因此CombinePolicy需要在C++0x中支持可变模板,这只在最前沿的编译器中实验性地提供。然而,似乎boost:mpl库通过使用一些预处理技巧解决/绕过了这个问题。我猜测我可以使用类似以下代码:

typedef mpl::list<Policy, Policy2, Policy3, Policy4> Policies;

接下来调用:

init_with<Policies>(...);

然后会使用:

typedef iter_fold<Policies, begin<Policies>::type,
                  some_magic_lambda_expression>::type MyPolicy;

显然,我在这里有些困惑如何理解some_magic_lambda_expression。我相信对于mpl专家来说,这是非常微不足道的。
提前感谢您的帮助。
3个回答

9
由于没有人令人满意地回答这个问题,我花了一些时间深入研究boost :: mpl源代码。天哪,它不太好看,有层层的宏和数百行特化类。现在我更加欣赏boost库的作者为我们使元编程更容易和更具可移植性所做的努力。希望C ++ 0x也能让库编写者的生活变得更加轻松。

无论如何,解决方案很简单而优雅。

首先,iter_fold不是我想要的,因为我无法弄清楚如何指定可以被延迟到空类型的迭代器。所以我尝试了fold并发现以下内容:

typedef fold<Policies, Null, Cons<_1, _2> >::type MyPolicy;

为了使其工作,我需要提供Null类型和Cons的特化:
struct Null { };

template<class PolicyT>
struct Cons<Null, PolicyT> {
  static void init() { PolicyT::init(); }
  static void cleanup() { PolicyT::cleanup(); }
};

优雅,我同意。看起来有点像Alexandrescu的type_list。在某个层次上,使事物对用户不可见所需的魔力确实令人烦恼... - tabdamage

1

我认为你的问题更多是运行时调用而不是元函数,因为你想在实际运行时对象上调用init函数。

你可以尝试使用mpl的运行时算法,例如:

for_each<Policies>(InitPolicy());

使用

struct InitPolicy() {
    template<class Policy>
    void operator() (Policy& p) { p.init_options(); }
};

你的示例中有一个小错误。这可能会导致示例无法正常工作。我可以使用for_each方法来处理每个方法。但是,我更喜欢拥有一个组合策略,可以传递,即我更喜欢在编译时强制执行顺序,而不是在运行时使用for_each。 - ididak
在我看来,您只能在编译时调用元函数,我看不到在编译时调用 init_options() 或任何其他普通函数的方法。我理解您希望通过在运行时调用 init_with 自动应用策略列表中的所有策略,这就是 for_each 的作用。请明确说明。 - tabdamage
目标是在编译时组合一个策略类,强制执行与我的原始示例相同的顺序,并确实在运行时调用实际方法,例如MyCombinedPolicy :: init_options()等。 - ididak
进一步澄清一下:如果我想为每个策略都有一个void cleanup()方法,并且我希望按照类型列表中声明的相反顺序进行清理,那么在我的原始显式cons方案中很容易实现,但mpl::for_each在这里显然行不通。 - ididak

1

我想你正在寻找类似这样的东西:

typedef 
  iter_fold<
    Policies,
    begin<Policies>::type,
    Cons<_1,_2>
  >::type
  MyType;

如果您在编译时硬编码调用基类函数,则可能还需要查看inherit_linearly<>


那是我的最初猜测,但我认为这不正确(无法编译等)。此外,我认为inherit_linearly在这里不适合模型。我想使组合变得简单和声明性。类型序列将是最简单的方法。 - ididak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接