6

プロジェクトのセクションに次のコードがあります。

enum myEnum
{
    invalid = -1,
    val1 = 1,
    val2 = 2,
    val3 = 4
};

int bitmask = val1 | val3;

if(bitmask & val1)
    ...
if(bitmask & val2)
    ...
if(bitmask & val3)
    ...

これは問題なく、完全に機能しますが、スイッチで実行できるかどうか常に疑問に思っていました。私はこれに沿って何かを考えていました:

int checkMask(int& mask)
{
    for(int i = 0; mask; mask &= ~(1 << i++))
    {
        if(mask & (1 << i))
        {
            int ret = mask & (1 << i);
            mask &= ~ret;
            return ret;
        }
    }

    return invalid;
}

#define START_BITMASK_SWITCH(x) int xcopy = x; while(xcopy) { switch(checkMask(xcopy))
#define END_BITMASK_SWITCH };

int bitmask = val1 | val3;

START_BITMASK_SWITCH(bitmask)
{
    case val1:
        ...
        break;
    case val2:
        ...
        break;
    case val3:
        ...
        break;
}
END_BITMASK_SWITCH

私の質問は次のとおりです。

  • 私はちょうど私の問題を解決しましたか?私は持っていると思いますが、それはきれいな解決策ですか?
  • これを達成する簡単な方法はありますか?
  • #define と関数を混在させるのは悪い考えですか?

  • 4

    7 に答える 7

    14

    いいえ、それはきれいな解決策ではありません。あなたの文脈では、混合#defineと機能を避けることができます。必要に応じて、以下のソリューションを試すことができますswitch()

    int bitmask = val1 | val3;
    int mask = 1;
    while(bitmask)
    {
      switch(bitmask & mask)
      {
      case val1: ... break;
      case val2: ... break;
      case val4: ... break;
      case val8: ... break;
      }
      bitmask &= ~mask; 
      mask <<= 1;
    }
    
    于 2011-07-07T08:24:05.600 に答える
    6

    いいえ、それは(明らかに)クリーンなソリューションではありません。元のコードは単純で、ループせず、言語に奇妙な構造を追加する特別なケースの「秘密」マクロを含んでいませんでした。

    「奇妙な構造」とは、START_BITMASK_SWITCH()/END_BITMASK_SWITCH マクロを意味します。

    • 標準のキーワードを使用せずにループを追加して、ループが発生していることを示唆することさえできます
    • 現在のスコープ内の clobber 名は多かれ少なかれ静かに
    • 偽のセミコロンを含める

    あなたのソリューションには何のメリットもありません。何らかの理由で a を使用switchして何かをしたいというかゆみを掻くために、肥大化とオーバーヘッド (コードサイズ、複雑さ、実行時のパフォーマンスの両方の点で) を追加するだけです。するのにはあまり適していません。

    明らかに、これは非常に主観的ですが、あなたは尋ねました。

    于 2011-07-07T08:15:47.490 に答える
    5

    いくつかの問題があります。

    • それは本当の利益のないプリプロセッサのクラフトを追加します
    • 多くの遅いコード (シフト、ループ、テスト) を追加します。
    • これにより、「ビット 2 がオンでビット 3 がオフの場合」などの特殊なケースを追加できなくなります ( if ((bitmask & (val2 | val3)) == val2)) 。
    • コンパイラは、生成されたコードを最適化する可能性をほとんどすべて見逃します。

    はるかに簡単な方法でも実行できます。

    #define START_BITMASK_SWITCH(x) \
        for (uint64_t bit = 1; x >= bit; bit *= 2) if (x & bit) switch (bit)
    
    int bitmask = val1 | val3;
    
    START_BITMASK_SWITCH(bitmask)
    {
        case val1:
            ...
            break;
        case val2:
            ...
            break;
        case val3:
            ...
            break;
    }
    
    于 2011-07-07T08:53:51.757 に答える
    3

    ビットマスクは、必要に応じて単なるブールの配列であり、列挙型はインデックスです。ブール値の配列を切り替えることはできますか?いいえ、できません。同時に複数の状態を表すことができるからです。他の整数と同様に、ビットマスク全体を切り替えることしかできませんでした。

    于 2011-07-07T08:22:45.790 に答える
    0

    ビットマスクのビットを反復処理する foreach コンストラクトを作成し、switch ステートメントを含む関数を提供できます。

    于 2011-07-07T08:43:36.910 に答える
    -1
    enum Positions {
        ALPHA,
        BETA,
        GAMMA
    };
    
    enum Flag {
        ALPHA_FLAG == 1 << ALPHA,
        BETA_FLAG  == 1 << BETA,
        GAMMA_FLAG == 1 << GAMMA
    };
    
    Position position_of (Flag f) {
        unsigned n = f;
        unsigned i = 0;
        while ((n & 1) == 0) {
             ++i;
             n >>= 1;
        }
        return Position (i);
    }
    
    switch (position_of (flag)) {
        case ALPHA:
        case BETA:
        // ...
    };
    

    これはC++0x の強力な列挙型を使用するとより適切であり、より明確な名前を付けるためにPosition::ALPHAandを使用できます。Flag 値を安全にビットマスクするためにFlag::ALPHA使用することもできます。constexpr

    于 2011-07-07T08:13:14.413 に答える