Подтвердить что ты не робот

Сложение структуры в С++

Если у меня есть struct в C++, нет ли способа безопасно читать/записывать ее в файл, совместимый с кросс-платформенным/компилятором?

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

4b9b3361

Ответ 1

Нет. Это невозможно. Это из-за отсутствия стандартизации C++ на двоичном уровне.

Дон Бокс пишет (цитируя его книгу Essential COM, глава COM As A Better C++)

C++ и переносимость


После принятия решения о распространении класса C++ в качестве DLL, перед ним стоит одна из основных недостатков C++, то есть отсутствие стандартизации на двоичном уровне. Хотя в проекте рабочего документа ISO/ANSI C++ делается попытка кодифицировать, какие программы будут компилироваться и какими будут семантические эффекты их запуска, он не пытается стандартизировать бинарную модель времени выполнения C++. Впервые эта проблема станет очевидной, когда клиент пытается связать с библиотекой импорта DLL FastString из среды разработки C++, отличной от той, которая используется для сборки библиотеки FastString.

Построение структуры выполняется по-разному разными компиляторами. Даже если вы используете один и тот же компилятор, выравнивание упаковки для структур может быть различным в зависимости от того, какой пакет прагмы вы используете.

Не только это, если вы пишете две структуры, члены которых точно такие же, единственное различие заключается в том, что порядок, в котором они объявлены, различен, тогда размер каждой структуры может (и часто бывает) другим.

Например, см. Это,

struct A
{
   char c;
   char d;
   int i;
};

struct B
{
   char c;
   int i;
   char d;
};

int main() {
        cout << sizeof(A) << endl;
        cout << sizeof(B) << endl;
}

Скомпилируйте его с помощью gcc-4.3.4, и вы получите этот результат:

8
12

То есть, размеры разные, хотя в обеих структурах одинаковые члены!

Код на Ideone: http://ideone.com/HGGVl

Суть в том, что в стандарте не говорится о том, как должно выполняться дополнение, и поэтому компиляторы могут принимать какие-либо решения, и вы не можете предположить, что все компиляторы принимают такое же решение.

Ответ 2

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

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

Во-первых, основной факт в C и С++ заключается в том, что выравнивание типа не может превышать размер типа. Если бы это было так, тогда было бы невозможно выделить память с помощью malloc(N*sizeof(the_type)).

Разместите структуру, начиная с самых больших типов.

 struct
 {
   uint64_t alpha;
   uint32_t beta;
   uint32_t gamma;
   uint8_t  delta;

Затем выровняйте структуру вручную, чтобы в итоге вы соответствовали самому большому типу:

   uint8_t  pad8[3];    // Match uint32_t
   uint32_t pad32;      // Even number of uint32_t
 }

Следующий шаг - решить, следует ли хранить структуру в маленьком или большом формате. Лучший способ - "поменять" весь элемент на месте перед записью или после прочтения структуры, если формат хранения не соответствует энсианности хост-системы.

Ответ 3

Нет, нет безопасного пути. В дополнение к дополнению вы должны иметь дело с различным порядком байтов и разными размерами встроенных типов.

Вам нужно определить формат файла и преобразовать структуру в этот формат. Библиотеки сериализации (например, boost: serialization или google protocolbuffers) могут помочь с этим.

Ответ 4

Короче говоря, нет. Не существует независимого от платформы, стандартно-совместимого способа борьбы с заполнением.

Заполнение называется "выравниванием" в стандарте, и оно начинает обсуждать его в 3.9/5:

Типы объектов имеют выравнивание требования (3.9.1, 3.9.2). выравнивание полного типа объекта целое число, определенное реализацией значение, представляющее количество байтов; объект выделяется по адресу который соответствует требованиям к выравниванию его типа объекта.

Но это продолжается оттуда и отходит ко многим темным углам Стандарта. Выравнивание является "реализацией", что означает, что он может быть различным для разных компиляторов или даже через модели адресов (например, 32-разрядные/64-разрядные) под одним и тем же компилятором.

Если у вас действительно жесткие требования к производительности, вы можете рассмотреть возможность хранения ваших данных на диске в другом формате, например, char. Многие высокопроизводительные протоколы отправляют все, используя строки, когда естественный формат может быть чем-то другим. Например, канал обмена с низкой задержкой, который я недавно работал, отправляет даты в виде строк, отформатированных следующим образом: "20110321", и время отправляется аналогичным образом: "141055.200". Несмотря на то, что этот обменный канал отправляет 5 миллионов сообщений в секунду в течение всего дня, они все еще используют строки для всего, потому что таким образом они могут избежать эндиенции и других проблем.

Ответ 5

Вы можете использовать что-то вроде boost::serialization.