Способ МПЛ фабрики, чтобы создать миксин-классы
У меня есть проект Visual Studio 2008 C++03, в котором фабричный метод используется для создания классов mixin на основе набора битовых флагов с использованием большого оператора switch / case.
Например:
inline boost::shared_ptr< MyInterface > Create( DWORD flags )
{
int a, b, c;
/* ... */
/*
0x000000 - MixinBase
0x000001 - AddOnA
0x001000 - AddOnB
0x002000 - AddOnC
0x400000 - AddOnD
... several more
*/
switch( flags )
{
case 0x000001:
return boost::make_shared< AddOnA< MixinBase > >( a, b, c );
case 0x001001:
return boost::make_shared< AddOnB< AddOnA< MixinBase > > >( a, b, c );
case 0x003001:
return boost::make_shared< AddOnC< AddOnB< MixinBase > > >( a, b, c );
case 0x003001:
return boost::make_shared< AddOnC< AddOnB< AddOnA< MixinBase > > > >( a, b, c );
case 0x402001:
return boost::make_shared< AddOnD< AddOnC< AddOnA< MixinBase > > > >( a, b, c );
default:
return boost::make_shared< MixinBase >( a, b, c );
}
}
К сожалению, этот оператор switch / case быстро становится огромным с помощью всего нескольких флагов. Есть ли лучший способ сделать это? Возможно, используя шаблонное мета-Программирование?
Спасибо
1 ответ:
Это, безусловно, возможно, хотя и не прямолинейно: поскольку
flags
известно только во время выполнения, вам нужно будет переплести время компиляции и вычисления во время выполнения.Вот общее решение, которое может быть использовано с любым базовым интерфейсом, базовым классом и mixins:
// recursive case template < typename Interface, typename BaseMixin, typename It, typename End, typename WrappersToApply > struct MixinCreatorIteration { static boost::shared_ptr< Interface > apply( int flags ) { typedef typename mpl::deref< It >::type flag_to_wrapper; typedef typename mpl::first< flag_to_wrapper >::type flag; typedef typename mpl::second< flag_to_wrapper >::type wrapper; if ( flags & flag::value ) // add current wrapper { return MixinCreatorIteration< Interface, BaseMixin, typename mpl::next< It >::type, End, typename mpl::push_back< WrappersToApply, wrapper >::type >::apply( flags ); } else // don't add current wrapper { return MixinCreatorIteration< Interface, BaseMixin, typename mpl::next< It >::type, End, WrappersToApply >::apply( flags ); } } }; //base case through partial template specialization template < typename Interface, typename BaseMixin, typename End, typename WrappersToApply > struct MixinCreatorIteration< Interface, BaseMixin, End, End, WrappersToApply > { static boost::shared_ptr< Interface > apply( int flags ) { using mpl::placeholders::_1; using mpl::placeholders::_2; typedef typename mpl::fold< WrappersToApply, BaseMixin, mpl::apply1< _2, _1 > >::type mixin; return boost::make_shared< mixin >(); } }; template < typename Interface, typename BaseMixin, typename WrapperMap > struct MixinCreator { static boost::shared_ptr< Interface > apply( int flags ) { return MixinCreatorIteration< Interface, BaseMixin, typename mpl::begin< WrapperMap >::type, typename mpl::end< WrapperMap >::type, mpl::vector< > >::apply( flags ); } };
А вот пример использования, сродни вашему примеру:
boost::shared_ptr< MyInterface > create( int flags ) { using namespace mpl::placeholders; typedef mpl::map< mpl::pair< mpl::int_< 0x01 >, AddOnA<_> >, mpl::pair< mpl::int_< 0x02 >, AddOnB<_> >, mpl::pair< mpl::int_< 0x04 >, AddOnC<_> > > flag_to_wrapper; return MixinCreator< MyInterface, MixinBase, flag_to_wrapper >::apply( flags ); } int main() { create( 0x01 ); // creates AddOnA< MixinBase > create( 0x02 ); // creates AddOnB< MixinBase > create( 0x07 ); // creates AddOnC< AddOnB< AddOnA< MixinBase > > > create( 0x08 ); // creates MixinBase }
В основном, идея состоит в том, чтобы хранить отношение между флагами и оболочками в структуре данных времени компиляции (здесь,
mpl::map
) и повторять над этой структурой, сохраняя обертки, чтобы применить по пути. В конце итерации применяются все оболочки и создается экземпляр.В вашем примере строительства должен параметры: если вы можете использовать C++11, вы можете легко адаптировать решение для использования вариативных параметров и совершенной прямой; в противном случае, вы можете использовать препроцессор для генерации различных версий
apply
(см. импульс.Препроцессор для того, как это сделать).