2017-02-15 2 views
0

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

#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"; 
} 

Когда я запускаю этот код десериализации, я получаю исключение «СТД :: length_error». Как-то связанное обсуждение этой ошибки - here, но для меня это, похоже, не имеет отношения к моему делу. (Или это?)

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

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

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

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

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

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

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

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

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

1) Является ли это способ сериализации должен работать с кашей?

2) Нужно ли добавлять дополнительные функции сериализации? Например. к классу enum?

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

ответ

0

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

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

Вы также должны убедиться, что используете флагом (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 
      ] 
     } 
    ] 
} 
+0

Указание двоичного флага std :: ios :: не помогло. Использование оператора() вместо оператора >> тоже не изменило. Я думаю, что мне нужно копать глубже и убрать мой пример дальше или попробовать загрузить в том же приложении. – AverageCoder

+0

Сериализация и десериализация в одном приложении не имеет значения. Он также терпит неудачу. Следующее, что я попробую, это следующее: My allData std :: vector зарезервирован для большего размера, чем то, что в конечном итоге закончится в нем (размер <зарезервировано), возможно, это проблема сериализации. Я обновлю этот вопрос в соответствии с моими выводами, что сработало, а также что не получилось. – AverageCoder

+0

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

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