【问题标题】:How to use boost::mpl to compose policies?如何使用 boost::mpl 来编写策略?
【发布时间】:2010-09-20 09:06:28
【问题描述】:

我已使用以下内容为我的应用程序编写策略:

策略类如下所示:

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 专家来说是微不足道的。

提前致谢。

【问题讨论】:

    标签: c++ templates metaprogramming boost-mpl


    【解决方案1】:

    由于没有人满意地回答这个问题,我花了一些时间深入研究 boost::mpl 源代码。伙计,多层宏和数百行专业化类并不漂亮。我现在更加感谢 boost 库的作者,他们让元编程对我们来说更容易和更便携。希望 C++0x 也能让库编写者的生活更轻松。

    无论如何,结果证明解决方案简单而优雅。

    第一个 iter_fold 不是我想要的,因为我不知道如何指定一个可以遵循 null 类型的迭代器。所以我摆弄 fold 并找到以下内容:

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

    为了让它工作,我需要为 Cons 提供 Null 类型和特化:

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

    【讨论】:

    • 优雅,我同意。看起来有点像 Alexandrescus type_list。在一定程度上,让用户看不见东西所需的魔法是相当麻烦的......
    【解决方案2】:

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

    你可以试试 mpl 的运行时算法, 喜欢:

    for_each<Policies>(InitPolicy());
    

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

    【讨论】:

    • 您的示例中有一个小错误。这可能适用于该示例。我可以为每种方法使用 for_each。但我更喜欢有一个可以传递的组合策略,即,我更喜欢在编译时执行命令,而不是在运行时使用 for_each。
    • 在我看来,你只能在编译时调用元函数,我看不到在编译时调用 init_options() 或任何其他普通函数的方法。我知道您想通过在运行时调用 init_with 来自动应用策略列表中的所有策略,这就是 for_each 所做的。请澄清
    • 目标是在编译时按照我的原始示例中强制执行的顺序组成一个策略类,并且实际方法确实在运行时调用,如 MyCombinedPolicy::init_options() 等。
    • 进一步澄清这一点:如果我想为每个策略都有一个 void cleanup() 方法,并且我想按照类型列表中声明的顺序相反的顺序进行清理,这很容易完成在我最初的显式 cons 方案中,但 mpl::for_each 显然在这里不起作用。
    【解决方案3】:

    我认为您正在寻找类似的东西:

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

    如果您内置某种 CRTP 以在编译时调用硬连线的基函数,您可能还想查看inherit_linearly<>

    【讨论】:

    • 这是我最初的猜测,但我认为这是不正确的(不会编译等)。此外,我认为 inherit_linearly 不适合这里的模型。我想让组合变得微不足道和声明性。类型序列将是最简单的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-14
    • 2012-09-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多