2010-10-14 6 views
3

Неправильно ли сериализовывать объекты структуры с помощью memcpy?Сериализация структур C-стиля (с использованием C++)

В одном из моих проектов я выполняю следующее: я memcpy объект struct, base64 закодировал его и запишу в файл. При анализе данных я делаю обратный. Кажется, что он работает нормально, но в некоторых ситуациях (например, при использовании WINDOWPLACEMENT для HWND проигрывателя Windows Media) получается, что декодированные данные не соответствуют sizeof(WINDOWPLACEMENT).

Вот некоторые фрагменты кода:

// Using WINDOWPLACEMENT from Windows API headers: 
typedef struct tagWINDOWPLACEMENT { 
    UINT length; 
    UINT flags; 
    UINT showCmd; 
    POINT ptMinPosition; 
    POINT ptMaxPosition; 
    RECT rcNormalPosition; 
#ifdef _MAC 
    RECT rcDevice; 
#endif 
} WINDOWPLACEMENT; 


static std::string EncodeWindowPlacement(const WINDOWPLACEMENT & inWindowPlacement) 
{ 
    std::stringstream ss; 
    { 
     Poco::Base64Encoder encoder(ss); // From the Poco C++ libraries 
     const char * offset = reinterpret_cast<const char*>(&inWindowPlacement); 
     std::vector<char> buffer(offset, offset + sizeof(inWindowPlacement)); 
     for (size_t idx = 0; idx != buffer.size(); ++idx) 
     { 
      encoder << buffer[idx]; 
     } 
     encoder.close(); 
    } 
    return ss.str(); 
} 


static WINDOWPLACEMENT DecodeWindowPlacement(const std::string & inEncoded) 
{ 
    std::string decodedString; 
    { 
     std::istringstream istr(inEncoded); 
     Poco::Base64Decoder decoder(istr); // From the Poco C++ libraries 
     decoder >> decodedString; 
     assert(decoder.eof()); 
     if (decoder.fail()) 
     { 
      throw std::runtime_error("Failed to parse Window placement data from the configuration file."); 
     } 
    } 

    if (decodedString.size() != sizeof(WINDOWPLACEMENT)) 
    { 
     // !! Occurs frequently !! 
     throw std::runtime_error("Errors occured during parsing of the Window placement."); 
    } 

    WINDOWPLACEMENT windowPlacement; 
    memcpy(&windowPlacement, &decodedString[0], decodedString.size()); 
    return windowPlacement; 
} 

Я знаю, что копирование классов в C++ с использованием тетсра может вызвать проблемы, потому что копию конструкторы не выполняется должным образом. Я не уверен, что это относится и к структурам C-стиля. Или сериализуется сбрасывание памяти просто не сделано?

Обновление: Ошибка в Base64Encoder/Decoder Poco не является невозможной, но маловероятной. Его тестовые примеры выглядят довольно тщательно: Base64Test.cpp.

+1

memcpy не более или менее вероятно вызывает проблемы с классами, чем с structs. За исключением случаев, когда вы принимаете во внимание тот факт, что большинство людей используют структуры как типы POD и классы для более сложных типов. structs в C++ также используют конструкторы копирования. И конструктор копии по умолчанию эквивалентен memcpy, по крайней мере для типов POD. –

+1

Что касается точки Ганса, разумно сказать компилятору явную «пакетную» инструкцию в определении структуры и использовать поля фиксированного размера. Если вы не можете этого сделать, то другой вариант - поместить поле размера в качестве первого члена структуры. Поле размера может использоваться как версия, его можно сравнить при загрузке, а будущие версии могут корректировать вещи, если они не совпадают. –

ответ

6

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

+0

+1 для будущей совместимости – SoapBox

3

Я не уверен, как operator>>() реализован в Poco::Base64Decoder. Если он равен istreamoperator>>(), то после decoder >> decodedString;decodedString может не содержать всех символов на входе. Например, если в кодированной строке есть какой-либо символ пробела, то decoder >> decodedString; будет читать до этого пробела.

2

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

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

Я бы рекомендовал более надежный способ сериализации и десериализации данных. Я слышал много хорошего о Google protocol buffers, что-то вроде этого будет намного более гибким и, вероятно, сэкономит вам головные боли в конце.

3

Выполнение memcpy из классов/структур в порядке, если они просто Plain Old Data (POD), но если это так, то вы можете рассчитывать на C++ делает копирование для вас с помощью конструкторов копирования (которые существуют как для struct и class типов в C++).

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

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

Вот некоторые темы, обсуждающие методы сериализации в C++:

2

Сериализация данных в порядке, что вы сделали это не особенно зло, если вы знаете, что «оставаясь на машине с тем же размером байта, размером слова, контентом и т. д. Поскольку вы сериализуете информацию о расположении окна, вам, вероятно, не важно переносимость между двумя разными машинами и только хотите сохраните эту информацию между сеансами на одном компьютере. Я бы рискнул предположить, что вы храните это в реестре. Если вы хотите переносить другие данные, которые действительно полезны, когда они переносятся на другие архитектуры, вы можете посмотреть на многие из других предложений, размещенных здесь уже, например, буферов протокола Google и т. Д. Whitespace - это красная селедка, поскольку все WS не имеет значения в потоке данных с кодировкой base64, и все декодеры должны игнорировать его (PoCo делает). Мне любопытно узнать, каковы размеры строки и структуры при ее сбое. Знание этого может дать вам некоторое представление о проблеме.

Смежные вопросы