我有一个 Visual Studio 2008 C++03 项目,其中工厂方法用于使用大型 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 语句很快就变得非常庞大,只需要几个标志。有一个更好的方法吗?可能使用模板元编程?
谢谢
最佳答案
这绝对是可能的,尽管并不简单:因为 flags
只在运行时知道,所以您需要将编译时和运行时计算交织在一起。
这是一个可以与任何基接口(interface)、基类和混入一起使用的通用解决方案:
// 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
函数(请参阅 Boost.Preprocessor 了解如何执行此操作)。
关于c++ - 用于创建混合类的 MPL 工厂方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10807160/