Сериализация структур в стиле C (с использованием C ++) - PullRequest
3 голосов
/ 15 октября 2010

Зло ли сериализовать объекты структуры с использованием memcpy?

В одном из моих проектов я делаю следующее: я запоминаю структурный объект, кодирую 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 ++ с использованием memcpy может вызвать проблемы, потому что конструкторы копирования не выполняются должным образом.Я не уверен, относится ли это также к структурам в стиле C.Или же сериализация с помощью дампа памяти просто не выполнена ?

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

Ответы [ 5 ]

6 голосов
/ 15 октября 2010

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

3 голосов
/ 15 октября 2010

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

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

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

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

3 голосов
/ 15 октября 2010

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

2 голосов
/ 15 октября 2010

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

2 голосов
/ 15 октября 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...