2012-05-29 69 views
3

我有一個Visual Studio 2008 C++ 03項目,其中工廠方法用於基於一組位標誌使用大型開關/ case語句創建mixin類。MPL工廠方法創建mixin類

例如:

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); 
    } 
} 

不幸的是,這種開關/ case語句快速增長巨大,只需幾個標誌。有一個更好的方法嗎?可能使用模板元編程?

感謝

+0

不是我能想到的,假設只有在運行時才知道'flags'的值。 – ildjarn

+0

@ildjarn - 是的,只有在運行時才知道標誌。 – PaulH

+0

你需要各種標誌的所有組合嗎? –

回答

8

這絕對是可能的,即使不是簡單:因爲flags只知道在運行時,你需要糾結的編譯時間和運行時間計算。

這裏是一個通用的解決方案,它可以與任何基本接口,基類和混入使用:

// 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)。

+0

太酷了。謝謝。 – PaulH

+0

另外,對於任何想要看到這一點的人:http://ideone.com/3JS3e – PaulH

+0

如果您認爲這很酷,請隨時致信:)! –