Я поддерживаю старую кодовую базу, которая использует объединение целочисленного типа со структурой битового поля для каламбура. Мой компилятор VS2017. Например, код похож на следующий:
struct FlagsType
{
unsigned flag0 : 1;
unsigned flag1 : 1;
unsigned flag2 : 1;
};
union FlagsTypeUnion
{
unsigned flagsAsInt;
FlagsType flags;
};
bool isBitSet(unsigned flagNum, FlagsTypeUnion flags)
{
return ((1u << flagNum) & flags.flagsAsInt);
}
Этот код имеет ряд неопределенных проблем поведения. А именно, горячо обсуждается, является ли каламбур типа определенным поведением или нет, но, кроме того, реализация упаковки битовых полей определяется реализацией. Чтобы решить эти проблемы, я хотел бы добавить операторы static-assert для проверки того, что реализация VS позволяет использовать этот тип подхода. Однако, когда я попытался добавить следующий код, я получаю сообщение об ошибке C2131: выражение не является константой.
union FlagsTypeUnion
{
unsigned flagsAsInt;
FlagsType flags;
constexpr FlagsTypeUnion(unsigned const f = 0) : flagsAsInt{ f } {}
};
static_assert(FlagsTypeUnion{ 1 }.flags.flag0,
"The code currently assumes bit-fields are packed from LSB to MSB");
Есть ли способ добавить проверки во время компиляции, чтобы убедиться, что код с типом и битовой упаковкой работает так, как предполагает код времени выполнения? К сожалению, этот код разбросан по кодовой базе, поэтому изменение структуры практически невозможно.
В новом коде подход к этому заключается в создании перечисляемого типа с перечислителями flag0 = 0x01
, flag1 = 0x02
, flag3 = 0x04
. Объект этого типа может содержать любое или все эти значения, и проверка наличия какого-либо конкретного значения тривиальна.
Вы можете использовать std::bit_cast
(С++ 20):
struct FlagsType
{
unsigned flag0 : 1;
unsigned flag1 : 1;
unsigned flag2 : 1;
unsigned padding : 32 - 3; // Needed for gcc
};
static_assert(std::is_trivially_constructible_v<FlagsType>);
constexpr FlagsType makeFlagsType(bool flag0, bool flag1, bool flag2)
{
FlagsType res{};
res.flag0 = flag0;
res.flag1 = flag1;
res.flag2 = flag2;
return res;
}
static_assert(std::bit_cast<unsigned>(makeFlagsType(true, false, false)) == 1);
constexpr
.К сожалению, метод bit_cast
нельзя написать без поддержки C++20. Хотя этот ответ будет чрезвычайно полезен после обновления до VS2019+, ответ на вопрос, возможно ли это в VS2017, выглядит так: «Невозможно проверить это во время компиляции без компилятора, обеспечивающего поддержку С++ 20».