Enum и макрос состояния C ++ - PullRequest
3 голосов
/ 13 июля 2010

(Вопрос связан с моими предыдущими вопросами здесь , здесь , здесь и здесь ).

Я поддерживаю очень старое приложение, которое было перенесено несколько лет назад из DOS в Windows, но многие старые соглашения C до сих пор продолжают работать.

Одно конкретное соглашение - это макросы setBit и clrBit:

#ifndef setBit
#define setBit(word, mask) word |= mask
#endif

#ifndef clrBit
#define clrBit(word, mask) word &= ~mask
#endif

Я обнаружил, что могу объявить переменную как тип enum и установить мою переменную равной одному из определенных перечисленных значений.

enum SystemStatus
{
    SYSTEM_ONLINE                = BIT0,
    SYSTEM_STATUS2               = BIT1,
    SYSTEM_STATUS3               = BIT2,
    SYSTEM_STATUS4               = BIT3
};

С BIT0 = 0x00000001, BIT1 = 0x00000002 и т. Д.

SystemStatus systemStatus;

systemStatus = SYSTEM_ONLINE

По вашему мнению, макросы setBit и clrBit больше похожи на C или C ++ и было бы лучше просто объявить переменные как перечислимый тип и избавиться от всех старых вещей setBit / clrBit?

Ответы [ 6 ]

7 голосов
/ 13 июля 2010

Нет, вы не можете - присвоение значения enum перезаписывает все значение, в то время как макросы меняют биты в существующем значении. А что такое BIT0, BIT1 и другие? Это похоже на определение INT0, INT1 и т. Д. - ужасная практика.

Итог, старый код в стиле C создает вам проблемы? Если нет, примените это проверенное временем правило - если оно не сломано, не исправляйте его.

6 голосов
/ 13 июля 2010

setBit и clrBit - это хорошо, хотя я бы преобразовал их в встроенные функции в C ++. Они были бы очень полезны, если бы биты состояния не зависели друг от друга, например:

  SystemStatus systemStatus = SYSTEM_ONLINE | SYSTEM_STATUS3;

является допустимым значением.

systemStatus = clrBit(systemStatus, SYSTEM_STATUS3);
systemStatus = setBit(systemStatus, SYSTEM_STATUS4);
4 голосов
/ 13 июля 2010

Я думаю, вы путаете цели. Перечисление о настройке значений для использования в качестве флагов. setBit и clrBit предназначены для работы с данными. Эти данные могут оказаться флагом, но это действительно единственная связь между двумя идеями.

При этом макросы, безусловно, НЕ являются способом выполнения C ++. Вместо этого вы бы использовали встроенные функции. Например:

template<typename T>
inline T& setBit(T& word, T mask) { return word |= mask; }

template<typename T>
inline T& clrBit(T& word, T mask) { return word &= ~mask; }

Изменить, чтобы отразить придирки:

Это всего лишь пример того, как вы могли бы реализовать функции. Вам не нужно использовать шаблоны, вы можете использовать два параметра шаблона вместо 1, вы можете использовать функцию void или значения вместо ссылок, если хотите, (хотя тогда это утратит некоторую первоначальную семантику). Суть в том, чтобы получить преимущества безопасности типов, которых вы не найдете в макросах (среди многих других недостатков макросов). http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.5

Редактировать: вот пустая, не шаблонная версия для сравнения

inline void setBit(unsigned int word, unsigned int mask) { word |= mask; }

inline void clrBit(unsigned int word, unsigned int mask) { word &= ~mask; }
2 голосов
/ 13 июля 2010

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

ОК, если перечисления являются битовыми флагами, так что вы можете написать

systemStatus = SYSTEM_ONLINE | BIT2;

Тогда я думаю, что этоудобочитаем и может поддерживать комбинации, хорошо.

1 голос
/ 14 июля 2010

Я согласен с bta о том, что если вы хотите использовать подход C ++, вы должны создать класс, который абстрагирует всю реализацию состояний.

Но я не буду перегружать операторы + =, - =, потому что вы продолжаете переносить C old school.

Я предлагаю объявление метода для этого.

Вы можете выбрать один метод с логическим флагом или два для настройки и очистки.

class Status{...};

void main(){
    Status status;

    //first approach
    status.SystemOnline(true);
    status.BackUPOnline(false);

    //second approach
    status.SetEmergencySystem();
    status.ClearSystemOnline();
}

с этим стилем вы инкапсулируете реализацию о том, как реализовать хранение информации.

1 голос
/ 13 июля 2010

Использование enum, как вы сделали, работает только в том случае, если вы можете гарантировать, что за один раз должен быть установлен не более одного бита.В противном случае вы должны иметь перечисляемую константу для всех битовых комбинаций, которая может довольно быстро усложниться.Вы можете использовать набор #define операторов (или enum, я полагаю) для псевдонимов битовых масок с понятным именем, но вы все равно, вероятно, в конечном итоге будете использовать макросы set / clear.

Установка и очистка битов определенно выглядит скорее как «C-подобный» подход.Тем не менее, я (лично) не считаю ваш enum подход «очень похожим на C ++».Для более похожего на C ++ подхода создайте класс для представления состояния системы и манипулируйте членами класса вместо битовых полей.Вы даже можете перегрузить операторы + и -, чтобы они действовали как ваши функции setBit и clrBit соответственно.Например, используя systemStatus -= SYSTEM_ONLINE#define SYSTEM_ONLINE (1<<31) или аналогичным), чтобы очистить бит «System Online», если и только если он установлен.Вы могли бы даже наследовать от STL Bitset и повторно использовать большую часть этой функциональности.

Edit: OP пояснил, что BIT0 и т. Д. Являются битовыми масками, поэтомумой первый абзац больше не актуален.

...