Проблемы с десериализацией злаков PortableBinaryArchive

Я сталкиваюсь с исключением std::length, использующим библиотеку cereal для десериализации std::vector, полного моего собственного класса. Я думаю, что проще всего дать код. Это мой класс:

#include "cereal/archives/portable_binary.hpp"
#include "cereal/archives/json.hpp"
#include "cereal/types/vector.hpp"

enum class myType {
    None, unset, Type1, Type2, Type3
};

class myClass
{
public:
    myClass();
    myClass(size_t siz);
    ~myClass();
    std::vector<size_t> idxs;
    myType dtype;
    bool isvalid;

    // This method lets cereal know which data members to serialize
    template<class Archive>
    void serialize(Archive & archive)
    {
        archive(CEREAL_NVP(dtype), CEREAL_NVP(isvalid), CEREAL_NVP(idxs));
    }
protected:
private:
};

Член idxs не обязательно всегда одного и того же размера. После некоторых расчетов я получаю

std::vector<myClass> allData;

который я хочу сериализовать, а затем десериализовать в другом приложении. Это мой код для сериализации:

std::ofstream ofile(allDataFilename.c_str());
if (ofile.good())
{
    cereal::PortableBinaryOutputArchive theArchive(ofile);
    theArchive(allData);
    //ofilefp.close();   // Do not close because of RAII where dtor of cereal archive does some work on file!
}
else
{
    std::cout << "Serialization to portable binary archive failed. File not good." << "\n";
}

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

std::string allDataFilename("C:\\path\\to\\file\\data.dat");
std::ifstream infile(allDataFilename.c_str());
std::vector<myClass> myDataFromDisk;
if (infile.good())
{
    cereal::PortableBinaryInputArchive inArchive(infile);
    inArchive >> myDataFromDisk;
}
else
{
    std::cout << "Data file unavailable." << "\n";
}

Когда я запускаю этот код десериализации, я получаю исключение "std::length_error". Как-то связано обсуждение этой ошибки здесь, но мне кажется, что это не относится к моему делу. (Либо это?)

Я попытался де-сериализовать с помощью отдельных функций загрузки / сохранения, потому что я не был уверен, применима ли здесь эта часть документации по зерновым культурам:

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

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

Оба приложения скомпилированы с Visual Studio 2015 Update 3. Я использую текущую версию cereal v1.2.2, но также пробовала использовать cereal v1.1.2, которая дала мне немного идентичный результат сериализации.

Как в стороне: это работает с зерновым архивом JSON. Но только после того, как я изменил вызов сериализации на

archive(CEREAL_NVP(dtype), CEREAL_NVP(isvalid), CEREAL_NVP(idxs));

в то время как он не работал с JSON, когда элемент vector был первым при сериализации. Но это может быть совершенно не связано.

archive(CEREAL_NVP(idxs), CEREAL_NVP(dtype), CEREAL_NVP(isvalid));

Теперь мои вопросы:

1) Предполагается ли, что сериализация будет работать с зерновыми?

2) Мне нужно добавить больше функций сериализации? Например, в классе enum?

С наилучшими пожеланиями AverageCoder

1 ответ

Решение

В вашем классе нет ничего плохого в том, что касается вашего кода сериализации. Вам не нужно предоставлять сериализацию для перечислений, она включается автоматически через cereal/types/common.hpp, Порядок, в котором ваши поля сериализуются, не имеет значения.

Ваша ошибка заключается в неправильном использовании архивов при загрузке и сохранении. cereal обрабатывает весь интерфейс с потоком, поэтому вы не должны использовать потоковые операторы (т.е. << или же >>) прямо в зерновом архиве. Посмотрите еще раз на примеры на веб-сайте по зерновым культурам, и вы заметите, что всякий раз, когда происходит взаимодействие с архивом зерновых, это делается с помощью () оператор.

Вы также должны убедиться, что вы используете соответствующий флаг (std::ios::binary) при работе с потоками, которые работают с двоичными данными - это может предотвратить некоторые проблемы, которые трудно отладить.

Вот рабочий пример использования вашего класса, где я сохраняю в потоке в памяти, а не в файл, но принцип тот же:

#include <cereal/archives/portable_binary.hpp>
#include <cereal/archives/json.hpp>
#include <cereal/types/vector.hpp>
#include <algorithm>
#include <sstream>

enum class myType {
    None, unset, Type1, Type2, Type3
};

class myClass
{
public:
  myClass() = default;
  myClass( myType mt, size_t i ) : isvalid( true ), dtype( mt ),
                                idxs( i )
  {
    std::iota( idxs.begin(), idxs.end(), i );
  }

  std::vector<size_t> idxs;
  myType dtype;
  bool isvalid;

  // This method lets cereal know which data members to serialize
  template<class Archive>
  void serialize(Archive & archive)
  {
    archive(CEREAL_NVP(dtype), CEREAL_NVP(isvalid), CEREAL_NVP(idxs));
  }
};

int main(int argc, char* argv[])
{
  std::vector<myClass> allData = {{myType::None, 3}, {myType::unset, 2}, {myType::Type3, 5}};

  // When dealing with binary archives, always use the std::ios::binary flag
  // I'm using a stringstream here just to avoid writing to file
  std::stringstream ssb( std::ios::in | std::ios::out | std::ios::binary );
  {
    cereal::PortableBinaryOutputArchive arb(ssb);
    // The JSON archive is only used to print out the data for display
    cereal::JSONOutputArchive ar(std::cout);

    arb( allData );
    ar( allData );
  }

  {
    cereal::PortableBinaryInputArchive arb(ssb);
    cereal::JSONOutputArchive ar(std::cout);

    std::vector<myClass> data;
    arb( data );

    // Write the data out again and visually inspect
    ar( data );
  }

  return 0;
}

и его вывод:

{
    "value0": [
        {
            "dtype": 0,
            "isvalid": true,
            "idxs": [
                3,
                4,
                5
            ]
        },
        {
            "dtype": 1,
            "isvalid": true,
            "idxs": [
                2,
                3
            ]
        },
        {
            "dtype": 4,
            "isvalid": true,
            "idxs": [
                5,
                6,
                7,
                8,
                9
            ]
        }
    ]
}{
    "value0": [
        {
            "dtype": 0,
            "isvalid": true,
            "idxs": [
                3,
                4,
                5
            ]
        },
        {
            "dtype": 1,
            "isvalid": true,
            "idxs": [
                2,
                3
            ]
        },
        {
            "dtype": 4,
            "isvalid": true,
            "idxs": [
                5,
                6,
                7,
                8,
                9
            ]
        }
    ]
}
Другие вопросы по тегам