С определением теряю безопасность типа
Не обязательно ...
// signed defines
#define X_NEW 0x01u
#define X_NEW (unsigned(0x01)) // if you find this more readable...
и с enum я теряю пробел (целые числа)
Не обязательно - но вы должны быть явными в точках хранения ...
struct X
{
RecordType recordType : 4; // use exactly 4 bits...
RecordType recordType2 : 4; // use another 4 bits, typically in the same byte
// of course, the overall record size may still be padded...
};
и, вероятно, придется выполнять приведение, когда я хочу выполнить побитовую операцию.
Вы можете создавать операторы, чтобы избавиться от боли:
RecordType operator|(RecordType lhs, RecordType rhs)
{
return RecordType((unsigned)lhs | (unsigned)rhs);
}
С const я думаю, что я также теряю безопасность типов, так как случайный uint8 мог попасть по ошибке.
То же самое может случиться с любым из этих механизмов: проверки диапазона и значений обычно ортогональны безопасности типов (хотя определяемые пользователем типы - т.е. ваши собственные классы - могут приводить в исполнение «инварианты» в отношении своих данных). С перечислениями компилятор может свободно выбирать больший тип для размещения значений, а неинициализированная, поврежденная или просто неправильно заданная переменная enum может все равно интерпретировать свой битовый шаблон как число, которого вы не ожидаете, сравнивая с любым из идентификаторы перечисления, любая их комбинация и 0.
Есть ли какой-нибудь другой более чистый способ? / Если нет, что бы вы использовали и почему?
Ну, в конце концов проверенный и проверенный C-стиль побитового ИЛИ перечислений работает очень хорошо, когда у вас есть битовые поля и пользовательские операторы на рисунке. Вы можете улучшить свою надежность с помощью некоторых пользовательских функций проверки и утверждений, как в ответе mat_geek; методы, которые в равной степени применимы к обработке строк, целых, двойных значений и т. д.
Можно утверждать, что это "чище":
enum RecordType { New, Deleted, Modified, Existing };
showRecords([](RecordType r) { return r == New || r == Deleted; });
Мне безразлично: биты данных упаковываются все теснее, но код значительно увеличивается ... зависит от того, сколько у вас объектов, и lamdbas - какими бы красивыми они ни были - все еще более беспорядочными и труднее их получить, чем побитовые ИЛИ .
КСТАТИ / - аргумент о довольно слабом ИМХО в отношении безопасности потоков - лучше всего запоминать в качестве фона, а не становиться доминирующей движущей силой принятия решений; Совместное использование мьютекса между битовыми полями является более вероятной практикой, даже если он не знает об их упаковке (мьютексы являются относительно громоздкими элементами данных - мне нужно серьезно задуматься о производительности, чтобы рассмотреть возможность использования нескольких мьютексов для членов одного объекта, и я бы внимательно посмотрел достаточно заметить, что они были битовыми полями). Любой тип размера подслов может иметь такую же проблему (например, uint8_t
). В любом случае, вы можете попробовать атомарные операции сравнения и замены, если вам нужен более высокий параллелизм.