Как да използвам 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. Човече, не е красиво със слоеве макроси и стотици редове от класове за специализация. Сега оценявам повече авторите на библиотеките за усилване, за да направят мета програмирането по-лесно и по-преносимо за нас. Надяваме се, че C++0x ще улесни и живота на писателите на библиотеки.

Както и да е, решението се оказва просто и елегантно.

First 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(); }
};
person ididak    schedule 04.11.2008
comment
елегантен, съгласен съм. изглежда малко като Alexandrescus type_list. На определено ниво магията, необходима, за да се направят нещата невидими за потребителите, е доста притеснителна... - person tabdamage; 04.11.2008

Мисля, че вашият проблем е по-скоро извикване по време на изпълнение, отколкото метафункции, защото искате да извикате init функциите на действителните обекти по време на изпълнение.

Можете да опитате алгоритмите за изпълнение на 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() за всяка политика и искам да направя почистванията в обратен ред на декларирания в списъка с типове, това лесно се прави в моята оригинална изрична схема за минуси, но 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