У меня есть следующий тестовый код:
struct{
const int : 1;
const int b : 1;
} bit = {0};
int main(void) {
return bit.b;
}
Большинство компиляторов, включая последнюю версию GCC, компилируют его нормально, но Clang, начиная с версии 8, жалуется на «const» в анонимном битовом поле:
[source]:2:22: error: anonymous bit-field cannot have qualifiers
const int : 1;
Та же ошибка происходит с квалификатором «летучий». В чем причина того, что Clang так строг в отношении квалификаторов, и есть ли способ заставить замолчать эту ошибку?
Справочная информация: у меня есть автоматически сгенерированные файлы заголовков «Определение аппаратного регистра», которые представляют неиспользуемые биты и анонимные битовые поля, а квалификаторы, такие как «летучие константы» или «летучие», систематически используются для всех битов в регистре. Просматривать такие файлы и вручную удалять квалификаторы в анонимных полях — не вариант.
приемлемо ли использование сценария для выполнения задачи удаления?
Также знайте, что битовые поля, вероятно, НЕ делают то, что вы думаете. Компилятор может реализовать битовые поля любым способом (в пределах указанных вами типов). В лучшем случае он генерирует целочисленную переменную, которая как 1-битная арифметика, а не переменная, которая точно занимает один бит в вашей переменной. Таким образом, наличие безымянного битового поля не будет служить какой-либо (логической) цели.
Вероятно, вам следует использовать std::bitset<>
(без контроля над базовым типом) или написать небольшой класс, который выполняет за вас побитовые операции с базовым типом.
@Oersted не должен, говоря стандартным языком, означает, что это запрещено или соответствующий компилятор должен отклонить этот код. Стандарт C++ соответствует ISO OBP в отношении терминологии (согласно intro.defs#2.1 ), а OBP говорит «не должен» — указывает на требования, которые необходимо строго соблюдать для соответствия документу и от которых не допускаются никакие отклонения. "
@Yksisarvinen Спасибо. Таким образом, cl и gcc неверны?
@Oersted На данный момент приемлемым решением является простой переход на GCC, однако я пытаюсь предвидеть возможность исправления программного обеспечения после обновления GCC. И да, изменение с помощью сценария было бы моим запасным планом.
@PepijnKramer Это очень верные аргументы, однако даже сегодня многие заголовки описания оборудования определяют регистры устройств с использованием битовых полей и полагаются на компиляторы, чтобы упаковать их вместе, включая безымянные поля.
@DmitryGrigoriev Тогда вы полагаетесь на то, что этот конкретный компилятор сделает правильные действия (вы проверили, что сборка делает это?). Я все равно посоветовал бы вам создать небольшой шаблон класса, который выполняет за вас битовые операции (возможно, даже добавить некоторые элементы constexpr, чтобы перемещать вещи во время компиляции, что хорошо для встраивания).
Вы вдохновили меня попробовать это: godbolt.org/z/8WecqTaoM
@PepijnKramer Для сборок ПО, ориентированных на реальное оборудование, я использую компилятор, который рекомендует производитель ASIC, поэтому проверка сборки будет его обязанностью. Именно поэтому я не хочу прикасаться к этим заголовкам, предоставленным производителем: я не хочу нести ответственность за тестирование и обслуживание. Для тестовых сборок (например, модульных тестов с помощью Google Test) меня фактически не волнует, как компилятор распределяет битовые поля.
@DmitryGrigoriev В этом случае справедливый подход (компилятор конкретного поставщика + оборудование конкретного поставщика). Я просто больше привык писать переносимый код, а «реализация, определенная» в стандарте C++, является немного сложной областью.
Потому что Clang реализует CWG2229 (из-за чего было неправильно использовать тип с указанием cv для безымянного битового поля), а GCC - нет.
В настоящее время у Clang нет возможности игнорировать эту ошибку. Это недействительно для C++, вам придется автоматически генерировать структуры (либо без квалифицированных типов, либо с именем типа _padding1
).
@463035818_is_not_an_ai Добавление имени делает его больше не безымянным битовым полем. Я думаю, что Microsoft делает это для всех своих битовых полей.
ох. нужно больше кофе
Я думаю, что причина, по которой этого не было сделано, в первую очередь — автозаполнение. В современных ASIC все регистры имеют одинаковый размер (например, 32-битный), даже если на самом деле используется всего пара бит. Вы же не хотите, чтобы пользователи набирали Uart->CtlrReg.
и видели всплывающее окно с бесполезными _paddingX
именами.