Инициализация структуры содержимым std::vector

#c #c 11 #vector #struct

#c #c 11 #вектор #структура

Вопрос:

Это кажется чем-то, что должно быть простым и понятным, но Google выдает очень мало.

Какой чистый, современный (C 11) способ инициализации простой структуры заголовка файла, например, следующий

 typedef struct FooHeader {
    uint8_t    FooCount;
    uint8_t    BarCount;
    uint32_t   BazOffsets[4];
} FooHeader;
 

с данными, содержащимися в std::vector<unsigned char> ? Является ли хорошей идеей создать вложенный вектор и преобразовать его данные в тип структуры заголовка или?

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

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

2. Да, это так, поскольку формат файла уже устоялся, и все приложения, работающие с ним до сих пор, договорились о том, как его следует читать и записывать. Также крайне маловероятно, что это программное обеспечение будет использоваться на какой-либо платформе, отличной от x86.

3. С какой целью вы хотите создать подвектор? Кроме того, есть ли у вас в вашей программе одна переменная FooHeader или std::vector<FooHeader>, которую вы хотите заполнить с помощью std::vector<unsigned char> ?

4. Если мне не нужно создавать подвектор, я не буду. Это была просто мысль. В этом случае FooHeader будет локальной переменной.

5. Как насчет FooHeader h = *(reinterpret_cast<FooHeader*>(amp;data_vec[0])) ?

Ответ №1:

Чтобы избежать проблем с упаковкой, выравниванием и порядком следования, лучше всего читать данные на байтовом уровне (почти на всех современных аппаратных средствах вы можете использовать 8-битные байты, но упаковка часто меняется между компиляторами (или даже просто между разными флагами компиляции), и как большие, так и маленькие конечные компьютерывсе еще распространено).

Это означает, что лучше всего сделать что-то вроде:

 FooHeader load_FooHeader(std::vector<unsigned char> const amp;dat) {
    static_assert(
        std::numeric_limits<unsigned char>::digits == 8,
        "Assumes 8-bit bytes");

    FooHeader retv;

    retv.FooCount = dat[0];
    retv.BarCount = dat[1];

    //Start at the fifth byte, to allow for padding.
    //If you want to use a packed format, use index = 2;
    std::size_t index{4};
    for (std::size_t i{0}, iend{4}; i < iend;   i) {
        retv.BarOffsets[i] = 0;
        //Adjust ordering depending on desired endianness.
        //Currently uses little endian.
        for (std::size_t j{0}, jend{4}; j < jend;   j) {
            retv.BarOffsets[i] |= dat[index   i*4   (3-j)] << (j*8);
        }
    }

    return retv;
}