В C 20 мы можем упаковать объекты заголовка и данных вместе и получить доступ к данным из заголовка по смещению?

#c #c 20

Вопрос:

 struct header
{
    int a1;
    int a2;
    // ...;

    std::byte * get_data_bytes()
    {
        return align_up<data>( // make sure alignment requirements are met
                    reinterpret_cast<std::byte *>(this)   sizeof(*this)); 
       // maybe std::launder around the reinterpret_cast (only) is needed?
    }

    data amp; get_data()
    {
        return *std::launder(reinterpret_cast<data *>(get_data_bytes()));
    }

    void use_data()
    {
        get_data().use();
    }
};

void example()
{
    alignas(header) std::byte storage[/* plenty of space*/]; 
    
    auto h = new (storage) header;
    new (h->get_data_bytes()) data;
    
    h->use_data(); // Does this eventually cause a UB?
}

 

Возможно ли это без UB? Если нет, то есть ли альтернатива?
Требование состоит в том, чтобы данные не были подобъектом заголовка, и не было
указателя/ссылки на данные из заголовка, чтобы избежать дополнительной
косвенности. Возможно, это было бы возможно с гибким пустым массивом, но я
не думаю, что это входит в стандарт.

Комментарии:

1. Не могли бы вы уточнить немного больше о требованиях к типу data ? Я думаю, что вы танцуете вокруг нарушений псевдонимов здесь.

2. @AndyG Я намеренно не предоставил никаких подробностей data , но можно начать обсуждение с простого тривиального типа с несколькими целыми числами внутри.

3. Это называется «выстрелить себе в ногу». Возьмите начальный адрес структуры, добавьте смещение элемента, к которому вы хотите получить доступ, используйте приведение в стиле C, чтобы привести адрес (указатель) так, как вы хотите. Совершенно неопределенное поведение. Помните, что вам, возможно, придется учитывать отступы при расчете смещения.

4. @CppNerd13373 AFAIK нет, проблема в том, что в общем смысле вы пытаетесь взломать структуру. Проблема в том, что компилятор может поместить в массив больше заполнений, чем на самом деле требуется объекту sizeof . Это означает, что, даже если объект имеет a sizeof 12 , он может быть выровнен по 16 и т. Д. На нем есть видео CppCon, если вам интересно

5. @ThomasMatthews Несмотря на то, что этот пример выглядит так, как будто кто-то хотел бы выстрелить себе в ногу, почти везде вам приходится делать подобные вещи при общении с низкоуровневым кодом или оборудованием. Он очень часто представляет собой структуру с пустым массивом в конце, чтобы иметь возможность доступа к произвольным данным после объекта заголовка. Если вы уверены, что это неопределенное поведение, пожалуйста, не могли бы вы объяснить, почему и предоставить альтернативу?

Ответ №1:

Единственное, что даже гипотетически верно, — это является ли reinterpret_cast<std::byte *>(this) указатель на один из байтов в массиве или нет. Но вы можете std::launder это сделать, чтобы убедиться, что это так. Действительно, отмывание указателей байтов на/с достаточно полезно, чтобы вы могли создавать для них шаблонные функции:

 template<typename T>
std::byte *to_byte_ptr(T *ptr)
{
  return std::launder(reinterpret_cast<std::byte*>(ptr));
}

template<typename T>
T *from_byte_ptr(std::byte *ptr)
{
  return std::launder(reinterpret_cast<T*>(ptr));
}
 

Все остальное в полном порядке. Вы создали header хранилище, предоставляемое массивом байтов, поэтому там есть std::byte объекты, доступные через launder . И поскольку эти std::byte объекты находятся в массиве, вы можете выполнять арифметику указателей на них. И хотя эти std::byte объекты на самом деле не указывают на объекты, для которых они предоставляют хранилище, если у вас есть указатель с тем же адресом, что и нужный объект, вы можете launder извлечь указатель на этот объект.

Комментарии:

1. Это именно то, о чем я думал, так что спасибо, что дали мне это представление. Как бы интуитивно это ни звучало, я не смог найти в стандарте уверенности, которая скажет мне, что отмывание этого или этого 1 после отмывания действительно указывает на элемент массива массива std::байт. Кроме того, при чтении требований к стирке я был озадачен требованиями к достижимости. Не могли бы вы провести меня, может быть, через это?