I have a Visual Studio 2008 C++03 project where a factory method is used to create mixin classes based on a set of bit flags using a large switch/case statement.
For example:
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 );
}
}
Unfortunately, this switch/case statement quickly grows enormous with just a few flags. Is there a better way to do this? Possibly using template meta-programming?
Thanks
This is definitely possible, even though not straightforward: since flags
is only know at runtime, you'll need to intertwine compile-time and runtime computation.
Here is a generic solution that could be used with any base interface, base class and 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 );
}
};
And here is a sample use, akin to your example:
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
}
Basically, the idea is to store the relation between flags and wrappers into a compile-time data structure (here, an mpl::map
) and to iterate over this structure, keeping the wrappers to apply along the way. At the end of the iteration, all the wrappers are applied and the instance is created.
In your example, the construction needs parameters: if you can use C++11, you can easily adapt my solution to use variadic parameters and perfect forwarding; otherwise, you can use the preprocessor to generate various versions of the apply
functions (see Boost.Preprocessor for how to do that).