Проблемы с десериализацией злаков 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
]
}
]
}