3

私は Visual Studio 2008 C++03 プロジェクトを持っています。このプロジェクトでは、ファクトリ メソッドを使用して、大きな switch/case ステートメントを使用した一連のビット フラグに基づいて 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 );
    }
}

残念ながら、この switch/case ステートメントは、ほんの数個のフラグですぐに巨大になります。これを行うより良い方法はありますか?おそらくテンプレートのメタプログラミングを使用していますか?

ありがとう

4

1 に答える 1

8

簡単ではありませんが、これは間違いなく可能flagsです。 は実行時にしか分からないため、コンパイル時と実行時の計算を絡み合わせる必要があります。

以下は、任意の基本インターフェイス、基本クラス、および mixin で使用できる一般的なソリューションです。

// 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を参照してください)。

于 2012-05-30T12:41:14.133 に答える