Как использовать boost::mpl для составления политик?

Я использовал что-то вроде следующего для составления политик для своего приложения:

Классы политик выглядят следующим образом:

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 довольно уродлива. Было бы идеально иметь объединитель политик, который может это сделать:

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 здесь.

Заранее спасибо.


person ididak    schedule 02.11.2008    source источник


Ответы (3)


Поскольку никто удовлетворительно не ответил на вопрос, я некоторое время копался в исходниках boost::mpl. Чувак, это некрасиво со слоями макросов и сотнями строк классов специализации. Теперь я больше ценю авторов библиотек boost, которые делают метапрограммирование более простым и переносимым для нас. Будем надеяться, что C++0x также облегчит жизнь создателям библиотек.

В любом случае решение оказывается простым и элегантным.

Во-первых, iter_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(); }
};
person ididak    schedule 04.11.2008
comment
элегантно, согласен. немного похоже на Alexandrescus type_list. На определенном уровне магия, необходимая для того, чтобы сделать что-то невидимым для пользователей, может сильно беспокоить... - person tabdamage; 04.11.2008

Я думаю, что ваша проблема скорее связана с вызовом времени выполнения, чем с метафункциями, потому что вы хотите вызывать функции инициализации для реальных объектов времени выполнения.

Вы можете попробовать алгоритмы времени выполнения mpl, например:

for_each<Policies>(InitPolicy());

с

struct InitPolicy() {
    template<class Policy>
    void operator() (Policy& p) { p.init_options(); }
};
person tabdamage    schedule 02.11.2008
comment
В вашем примере есть небольшая ошибка. Это может заставить работать для примера. Я могу использовать for_each для каждого метода. Но я предпочитаю иметь одну комбинированную политику, которую можно передавать, т. е. я предпочитаю, чтобы порядок применялся во время компиляции, а не во время выполнения с помощью for_each. - person ididak; 02.11.2008
comment
Насколько я понимаю, вы можете вызывать метафункции только во время компиляции, я не вижу возможности вызвать init_options() или любую другую простую функцию во время компиляции. Насколько я понял, вы хотите автоматически применить все политики в списке политик, вызвав init_with во время выполнения, что и делает for_each. просьба уточнить - person tabdamage; 02.11.2008
comment
Цель состоит в том, чтобы составить класс политики во время компиляции с принудительным порядком, как в моем исходном примере, и фактические методы действительно вызываются во время выполнения, например MyCombinedPolicy::init_options() и т. д. - person ididak; 02.11.2008
comment
Чтобы еще больше прояснить этот момент: если я хочу иметь метод void cleanup() для каждой политики, и я хочу выполнять очистку в порядке, обратном порядку, объявленному в списке типов, это легко сделать в моей исходной явной схеме cons, но mpl ::for_each здесь явно не сработает. - person ididak; 02.11.2008

Я думаю, вы ищете что-то вроде:

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

Вы также можете изучить inherit_linearly ‹>, если вы встроили какой-то CRTP для вызова функций базы, жестко запрограммированных во время компиляции.

person Dean Michael    schedule 02.11.2008
comment
Это было мое первоначальное предположение, но я не думаю, что это правильно (не будет компилироваться и т. д.). Также я не думаю, что inherit_linearly подходит для данной модели. Я хочу сделать композицию тривиальной и декларативной. Последовательность типов была бы самой простой. - person ididak; 02.11.2008