Почему C ++ поддерживает шестнадцатеричное назначение, но не имеет двоичного назначения? Как лучше хранить флаги? - PullRequest
6 голосов
/ 30 сентября 2010

У меня есть набор битовых флагов, которые используются в программе, которую я портирую с 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 ++ и почему c ++ не поддерживает присваивание двоичного текста целочисленному типу, как, например, это позволяет назначать шестнадцатеричные числа таким образом (через «0x»).Эти вопросы кратко изложены в конце этого поста.

Я видел одно простое решение - просто создать отдельные константы:

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

Но одна вещь, которая меня беспокоит, это то, что она менее интуитивна, когда дело доходит до более высоких значений, кажется ... то есть

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.

Я подумываю использовать макро-решение Тома Торфа:

#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 ++ не поддерживает флаг значения "0b",похож на "0x"?
2. Какой стиль определения флагов является наилучшим ...
i. Константы в пространстве имен.
ii. Перечисленное в пространстве имен перечисление неподписанных целых чисел, назначенных напрямую.
iii. Перечисленное в пространстве имен перечисление неподписанных целых чисел, назначаемых с помощью читаемой двоичной строки.

Заранее спасибо!И, пожалуйста, не закрывайте эту тему как субъективную, потому что я действительно хочу получить справку о том, что такое лучший стиль и почему в c ++ отсутствует встроенная возможность двоичного назначения.


РЕДАКТИРОВАТЬ 1

Немного дополнительной информации.Я буду читать 32-битное битовое поле из файла, а затем проверять его с этими флагами.Так что имейте это в виду при публикации предложений.

Ответы [ 7 ]

13 голосов
/ 01 октября 2010

До C ++ 14 бинарные литералы обсуждались время от времени, но, насколько я знаю, никто никогда не писал серьезного предложения, чтобы привести его в стандарт, поэтому он так и не прошел Стадия разговоров об этом.

Для C ++ 14 кто-то наконец написал предложение, и комитет принял его, поэтому, если вы можете использовать текущий компилятор, основная предпосылка вопроса ложна - вы можете использовать двоичные литералы, которые имеют форму 0b01010101.

В C ++ 11 вместо непосредственного добавления двоичных литералов они добавили гораздо более общий механизм, позволяющий использовать общие пользовательские литералы, которые вы могли бы использовать для поддержки двоичных или базовых 64, или других типов вещей целиком. Основная идея заключается в том, что вы указываете числовой (или строковый) литерал, за которым следует суффикс, и вы можете определить функцию, которая получит этот литерал, и преобразовать его в любую форму, которую вы предпочитаете (и вы можете сохранить ее статус как «константу» "тоже ...)

Что использовать: если вы можете, двоичные литералы, встроенные в C ++ 14 или выше, являются очевидным выбором. Если вы не можете их использовать, я бы предпочел вариант 2: перечисление с инициализаторами в шестнадцатеричном виде:

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)
};
7 голосов
/ 01 октября 2010

При втором варианте вы можете использовать сдвиг влево, который, возможно, немного менее «неинтуитивен»:

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) 
   }; 
};
4 голосов
/ 01 октября 2010

Как примечание, Boost (как обычно) обеспечивает реализацию этой идеи.

2 голосов
/ 11 июня 2013

GCC имеет расширение, делающее его способным к двоичному присваиванию:

int n = 0b01010101;

Редактировать: Начиная с C ++ 14, теперь это официальная часть языка.

2 голосов
/ 01 октября 2010

Почему бы не использовать структуру битового поля ?

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)
    /* ... */
1 голос
/ 01 октября 2010

Что не так с гексом для этого варианта использования?

enum Flags {
    FLAG_A = 0x00000001,
    FLAG_B = 0x00000002,
    FLAG_C = 0x00000004,
    FLAG_D = 0x00000008,
    FLAG_E = 0x00000010,
    // ...
};
0 голосов
/ 01 октября 2010

Я думаю, суть в том, что в этом нет необходимости.

Если вы просто хотите использовать двоичные файлы для флагов, нижеприведенный подход - это то, как я обычно это делаю. После первоначального определения вам никогда не придется беспокоиться о том, чтобы смотреть на «более грязные» большие кратные 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»

note Отредактировано для ответа на вопрос.

...