6

C から C++ に移植するプログラムで使用される一連のビット フラグがあります。

始める...

私のプログラムのフラグは、以前は次のように定義されていました。

/* Define feature flags for this DCD file */
#define DCD_IS_CHARMM       0x01
#define DCD_HAS_4DIMS       0x02
#define DCD_HAS_EXTRA_BLOCK 0x04

...これで、定数 (クラス定数などに対して) の #defines は一般的に悪い形式と見なされることがわかりました。

これにより、C++ でビット フラグを格納する最善の方法と、16 進数をこの方法で ("0x" を介して) 割り当てることができるように、C++ が int へのバイナリ テキストの割り当てをサポートしない理由について疑問が生じます。これらの質問は、この投稿の最後にまとめられています。

簡単な解決策の 1 つは、個々の定数を単純に作成することです。

namespace DCD {
   const unsigned int IS_CHARMM = 1;
   const unsigned int HAS_4DIMS = 2;
   const unsigned int HAS_EXTRA_BLOCK = 4;
};

この考えを 1 としましょう。

私が持っていた別のアイデアは、整数列挙型を使用することでした:

namespace DCD {
   enum e_Feature_Flags {
      IS_CHARMM = 1,
      HAS_4DIMS = 2,
      HAS_EXTRA_BLOCK = 8
   };
};

しかし、これについて私を悩ませていることの1つは、より高い値になると直感的でなくなることです.

namespace DCD {
   enum e_Feature_Flags {
      IS_CHARMM = 1,
      HAS_4DIMS = 2,
      HAS_EXTRA_BLOCK = 8,
      NEW_FLAG = 16,
      NEW_FLAG_2 = 32,
      NEW_FLAG_3 = 64,
      NEW_FLAG_4 = 128
   };
};

このアプローチをオプション 2 と呼びましょう。

Tom Torf のマクロ ソリューションの使用を検討しています。

#define B8(x) ((int) B8_(0x##x))

#define B8_(x) \
( ((x) & 0xF0000000) >( 28 - 7 ) \
| ((x) & 0x0F000000) >( 24 - 6 ) \
| ((x) & 0x00F00000) >( 20 - 5 ) \
| ((x) & 0x000F0000) >( 16 - 4 ) \
| ((x) & 0x0000F000) >( 12 - 3 ) \
| ((x) & 0x00000F00) >( 8 - 2 ) \
| ((x) & 0x000000F0) >( 4 - 1 ) \
| ((x) & 0x0000000F) >( 0 - 0 ) )

インライン関数に変換されます。

#include <iostream>
#include <string>
....

/* TAKEN FROM THE C++ LITE FAQ [39.2]... */
class BadConversion : public std::runtime_error {
 public:
   BadConversion(std::string const& s)
     : std::runtime_error(s)
     { }
};

inline double convertToUI(std::string const& s)
{
   std::istringstream i(s);
   unsigned int x;
   if (!(i >> x))
     throw BadConversion("convertToUI(\"" + s + "\")");
   return x;
} 
/** END CODE **/

inline unsigned int B8(std::string x) {
   unsigned int my_val = convertToUI(x.insert(0,"0x").c_str());
   return ((my_val) & 0xF0000000) >( 28 - 7 ) |
            ((my_val) & 0x0F000000) >( 24 - 6 ) | 
            ((my_val) & 0x00F00000) >( 20 - 5 ) |
            ((my_val) & 0x000F0000) >( 16 - 4 ) |
            ((my_val) & 0x0000F000) >( 12 - 3 ) |
            ((my_val) & 0x00000F00) >( 8 - 2 ) |
            ((my_val) & 0x000000F0) >( 4 - 1 ) |
            ((my_val) & 0x0000000F) >( 0 - 0 );
}

namespace DCD {
   enum e_Feature_Flags {
      IS_CHARMM       = B8("00000001"),
      HAS_4DIMS       = B8("00000010"),
      HAS_EXTRA_BLOCK = B8("00000100"),
      NEW_FLAG        = B8("00001000"),
      NEW_FLAG_2      = B8("00010000"),
      NEW_FLAG_3      = B8("00100000"),
      NEW_FLAG_4      = B8("01000000")
   };
};

これはクレイジーですか?それとも、より直感的に見えますか?この選択を 3 としましょう。

要約すると、私の包括的な質問は次のとおりです。

1. なぜ c++ は "0x" のような "0b" 値フラグをサポートしないのですか?
2. フラグを定義するのに最適なスタイルはどれですか...
i. 名前空間でラップされた定数。
ii. 直接割り当てられた unsigned int の名前空間でラップされた列挙型。
iii. 読み取り可能なバイナリ文字列を使用して割り当てられた unsigned int の名前空間でラップされた列挙型。

前もって感謝します!また、このスレッドを主観的なものとして閉じないでください。最適なスタイルとは何か、および c++ にバイナリ代入機能が組み込まれていない理由について本当に助けてもらいたいからです。


編集1

少し追加情報。ファイルから 32 ビットのビットフィールドを読み取り、これらのフラグを使用してテストします。したがって、提案を投稿するときは、そのことを念頭に置いてください。

4

7 に答える 7

13

C++14 より前は、バイナリ リテラルは何年にもわたって議論されていましたが、私の知る限り、誰もそれを標準に入れるための真剣な提案を書いたことがなく、実際にその段階を過ぎることはありませんでした。それについて話しています。

C++ 14 については、最終的に誰かが提案を作成し、委員会はそれを受け入れました。したがって、現在のコンパイラを使用できる場合、質問の基本的な前提は誤りです。形式を持つバイナリ リテラルを使用できます0b01010101

C++11 では、バイナリ リテラルを直接追加する代わりに、バイナリ、ベース 64、またはその他の種類のものを完全にサポートするために使用できる一般的なユーザー定義リテラルを許可する、より一般的なメカニズムが追加されました。基本的な考え方は、数値 (または文字列) リテラルの後にサフィックスを指定し、そのリテラルを受け取る関数を定義して、好みの形式に変換できるというものです (そして、その状態を "定数として維持できます" " それも...)

どちらを使用するかについて: 可能であれば、C++14 以降に組み込まれているバイナリ リテラルが当然の選択です。それらを使用できない場合は、通常、オプション 2 のバリエーション (16 進数の初期化子を持つ列挙型) をお勧めします。

namespace DCD {
   enum e_Feature_Flags {
      IS_CHARMM = 0x1,
      HAS_4DIMS = 0x2,
      HAS_EXTRA_BLOCK = 0x8,
      NEW_FLAG = 0x10,
      NEW_FLAG_2 = 0x20,
      NEW_FLAG_3 = 0x40,
      NEW_FLAG_4 = 0x80
   };
};

別の可能性は次のようなものです。

#define bit(n) (1<<(n))

enum e_feature_flags = { 
    IS_CHARM = bit(0), 
    HAS_4DIMS = bit(1),
    HAS_EXTRA_BLOCK = bit(3),
    NEW_FLAG = bit(4),
    NEW_FLAG_2 = bit(5),
    NEW_FLAG_3 = bit(6),
    NEW_FLAG_4 = bit(7)
};
于 2010-09-30T20:14:34.603 に答える
7

オプション 2 では、左シフトを使用できますが、これはおそらく「非直感的」ではありません。

namespace DCD { 
   enum e_Feature_Flags { 
      IS_CHARMM =        1, 
      HAS_4DIMS =       (1 << 1), 
      HAS_EXTRA_BLOCK = (1 << 2), 
      NEW_FLAG =        (1 << 3), 
      NEW_FLAG_2 =      (1 << 4), 
      NEW_FLAG_3 =      (1 << 5), 
      NEW_FLAG_4 =      (1 << 6) 
   }; 
};
于 2010-09-30T20:15:08.743 に答える
4

注記として、Boost (いつものように) はこのアイデアの実装を提供します。

于 2010-09-30T20:15:12.560 に答える
2

ビットフィールド構造体を使用しないのはなぜですか?

struct preferences {
        unsigned int likes_ice_cream : 1;
        unsigned int plays_golf : 1;
        unsigned int watches_tv : 1;
        unsigned int reads_stackoverflow : 1;
    }; 

struct preferences fred;

fred.likes_ice_cream = 1;
fred.plays_golf = 0;
fred.watches_tv = 0;
fred.reads_stackoverflow = 1;

if (fred.likes_ice_cream == 1)
    /* ... */
于 2010-09-30T20:15:48.987 に答える
2

GCC には、バイナリ割り当てを可能にする拡張機能があります。

int n = 0b01010101;

編集: C++14 の時点で、これは言語の公式部分になりました。

于 2013-06-11T00:31:53.720 に答える
1

このユースケースの 16 進数の何が問題になっていますか?

enum Flags {
    FLAG_A = 0x00000001,
    FLAG_B = 0x00000002,
    FLAG_C = 0x00000004,
    FLAG_D = 0x00000008,
    FLAG_E = 0x00000010,
    // ...
};
于 2010-09-30T20:34:59.677 に答える
0

要するに、それは本当に必要ではないということだと思います。

フラグにバイナリを使用したいだけの場合は、以下のアプローチが私が通常行う方法です。元の定義の後、あなたが言及したように、「乱雑な」より大きな2の倍数を見ることを心配する必要はありません

int FLAG_1 = 1
int FLAG_2 = 2
int FLAG_3 = 4
...
int FLAG_N = 256

あなたは簡単にそれらをチェックすることができます

if(someVariable & FLAG_3 == FLAG_3) {
   // the flag is set
}

ちなみに、コンパイラによっては(私はGNU GCC Compilerを使用しています)、「0b」をサポートする場合があります

質問に答えるために編集されました。

于 2010-09-30T20:05:47.690 に答える