Правильно ли составлен приведенный ниже код, в частности, в отношении правил псевдонимов?

Приведенная ниже функция шаблона является частью генератора последовательности. Вместо ручных сдвигов я придумал следующее решение на основе объединения, чтобы сделать операции более явными. Он отлично работает на всех протестированных компиляторах. Ссылка на Godbolt.

Однако, несмотря на то, что он работает на практике, я боюсь, что существуют правила алиасинга, которые нарушаются, что означает, что он может не работать в будущем или в другом компиляторе, отличном от GCC и CLANG.

Строго в соответствии со стандартом С++: правильно ли сформирован приведенный ниже код? Это связано с неопределенным поведением?

template <int BITS>
uint64_t flog2(uint64_t num) {
    constexpr uint64_t MAXNUM = (uint64_t(1) << BITS);    
    if (num < MAXNUM) return num; 
    union FP {
        double dbl;
        struct {
            uint64_t man: 52;
            uint32_t exp: 11;
            uint32_t sign: 1;
        };
        struct {
            uint64_t xman: 52-BITS;
            uint32_t xexp: 11+BITS;
            uint32_t xsgn: 1;
        };
    };
    FP fp;
    fp.dbl = num;
    fp.exp -= 1023-1+BITS;
    return fp.xexp;
}

Спасибо!

запись одного значения объединения и чтение другого — это UB.

Raildex 18.02.2023 09:36

или в другом компиляторе, отличном от GCC и CLANG" Известно, что компилятор Microsoft размещает битовые поля в другом порядке. «Определяется реализацией» и все такое.

BoP 18.02.2023 10:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
70
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Во-первых, программа синтаксически некорректна в стандарте ISO C++. Анонимные члены struct не являются стандартными C++ (в отличие от C). Они являются расширением. В стандарте ISO C++ struct должен быть назван и доступен через это имя.

Я проигнорирую это до конца ответа и притворюсь, что вы обращались через такое имя.


Технически это не нарушение псевдонимов, а неопределенное поведение для чтения неактивного члена объекта объединения в

fp.exp -= 1023-1+BITS;

Типы для этого не имеют большого значения (в отличие от алиасинга). Всегда существует не более одного активного члена союза, который будет последним, который был либо явно создан, либо записан с помощью выражения доступа/назначения члена. В вашем случае fp.dbl = num; означает, что dbl является активным участником и единственным, с которого можно читать.

В стандарте есть одно исключение для доступа к общей начальной последовательности членов стандартного типа класса макета объединения, и в этом случае доступ к неактивному можно получить, как если бы он был активным. Но даже у ваших двух членов struct { есть непустая общая начальная последовательность только для BITS == 0.

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


Конечно, даже если оставить все это в стороне, расположение битовых полей и представления вовлеченных типов полностью определяются реализацией, и вы не можете ожидать, что это будет в целом переносимым.

Если расположение битовых полей не соблюдается, что происходит с миллионами строк C++, написанных для обработки сетевых пакетов? Скажем, структуры iphdr или ethdr, например?

Henrique Bucher 18.02.2023 16:58

@HenriqueBucher Дело не в уважении. Битовое поле uint64_t man: 52; означает только то, что к man можно получить доступ как к члену типа uint64_t, который может хранить в нем до 52-битных целых значений. На самом деле это ничего не говорит о структуре памяти. Это добавлено реализацией. Например, MSVC иногда добавляет заполнение там, где GCC/Clang этого не делает. Вы не можете наивно использовать код, написанный для одного, с другим, полагаясь на макет битового поля, хотя, полагаясь на него, вы, вероятно, имеете UB, как я все равно описал, и это еще больше зависит от реализации (например, endianess).

user17732522 18.02.2023 17:18

Итак, вы говорите, что нет кросс-платформенного способа определить в C++ схему памяти для структуры? Это звучит как огромное ограничение для меня.

Henrique Bucher 18.02.2023 17:21

@HenriqueBucher Макет члена не может быть форсирован в стандартном C++, но можно вкладывать подобъекты в любом месте внутри члена массива unsigned char/std::byte и предоставлять методы доступа. Но у вас все еще есть размеры, требования к выравниванию и битовое представление типов, которые вы там храните, и это также в значительной степени определяется реализацией. Я не думаю, что стандарт ISO когда-либо предназначался для определения поведения на этом низком уровне переносимым образом. Это необходимо только для интерфейсов, специфичных для платформы, или для оптимизации, которая в любом случае также будет зависеть от реализации.

user17732522 18.02.2023 18:21

Я имею в виду, что стандарт ISO определил модели доступа к памяти, не так ли? Почему в C это есть, а в C++ нет? Я думаю, что это довольно важный вопрос, чтобы небрежно оставить его в UB.

Henrique Bucher 18.02.2023 19:53

Вы случайно не знаете, где в стандарте эти правила?

Henrique Bucher 18.02.2023 19:58

@HenriqueBucher Что касается битовых полей, некоторые детали того, что не указано / определяется реализацией, различаются, но в основном то же самое верно как для C, так и для . В C разрешена каламбуризация типов union, но результирующие значения не указаны. Модель памяти/объекта C++ описана в basic.memobj . Вам нужно взять его вместе с отдельными разделами в expr для того, кто относится к тому или иному выражению. Для битовых полей смотрите также class.bit. К сожалению, это не просто читать.

user17732522 18.02.2023 20:31

@HenriqueBucher Ну, и, конечно же, class.union за поведение профсоюзов.

user17732522 18.02.2023 20:37

@HenriqueBucher Кроме того, извините, я не имел в виду «неопределенное» выше для каламбура типа C. Я имел в виду, что результирующие значения в любом случае по-прежнему определяются реализацией, потому что представление основных типов и структура памяти структур также определяются реализацией. Однако результирующее значение может быть представлением ловушки. (Это не указано только при чтении из заполнения.)

user17732522 18.02.2023 20:49

Чтение из члена союза, который не был записан последним, является неопределенным поведением.

Кроме того, расположение битовых полей определяется реализацией.

Следовательно, с точки зрения строгого стандарта C++, этот код вызывает как неопределенное поведение (путем чтения exp после записи dbl), так и полагается на поведение, определяемое реализацией, в предположении, что макет битового поля соответствует представлению double с плавающей запятой (которое, кстати, также определяется реализацией).

Интересный. Разве это не противоречит всей цели бифилдов?

Henrique Bucher 18.02.2023 19:57

Другие вопросы по теме