【问题标题】:Trouble deserializing cereal PortableBinaryArchive反序列化谷物 PortableBinaryArchive 遇到问题
【发布时间】:2017-07-04 08:32:05
【问题描述】:

我面临一个 std::length 异常,使用谷物库反序列化一个充满我自己的类的 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”。不知何故,与此错误相关的讨论是here,但对我来说,这似乎与我的情况无关。 (或者是吗?)

我尝试使用单独的加载/保存函数进行反序列化/序列化,因为我不确定谷物文档的这一部分是否适用于此:

如果可能,最好使用单个内部序列化 方法,尽管在必要时可以使用拆分方法(例如 在加载类时动态分配内存)。

我还尝试在基于范围的 for 循环中分别归档 idxs 成员的每个向量元素(就像在内部谷类食品中所做的那样),但这两件事都没有帮助。

这两个应用程序都是使用 Visual Studio 2015 Update 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) 我需要添加更多的序列化函数吗?例如。到枚举类?

最好的问候 平均编码器

【问题讨论】:

    标签: c++ c++11 serialization vector cereal


    【解决方案1】:

    关于序列化代码,您的课程没有任何问题。您不需要为枚举提供序列化,它通过cereal/types/common.hpp 自动包含。字段的序列化顺序无关紧要。

    您的错误在于执行加载和保存时未正确使用存档。谷物处理与流的所有接口,因此您不应直接在谷物存档上使用流操作符(即&lt;&lt;&gt;&gt;)。再看一下谷物网站上的示例,您会注意到,每当与谷物档案进行交互时,都是通过 () 运算符完成的。

    您还应该确保在处理二进制数据的流上操作时使用适当的标志 (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
                ]
            }
        ]
    }
    

    【讨论】:

    • 指定 std::ios::binary 标志没有帮助。使用 operator() 而不是 operator>> 也没有改变。我想我必须深入挖掘并进一步剥离我的示例,或者尝试在同一个应用程序中加载。
    • 在同一个应用程序中序列化和反序列化没有区别。它也失败了。我将尝试的下一件事如下:我的 allData std::vector 被保留为比最终以它结尾的更大的大小(大小
    • cereal 在加载期间适当地调整向量的大小 - 您的保留容量不应该有任何区别。我鼓励您再次查看我发布的示例以及谷物网站上的示例,从您的描述来看,我认为您在某个地方犯了一个简单的界面错误,导致您的问题。
    • 做一个简短的更新:我还没有发现问题是什么,但现在的情况是:在更改了 idxs 成员的 myClass 中的一些计算后,新的输出可以被谷物读取作为便携式二进制存档。 allData-vector 中的项目数量现在只有原来的一半(现在约 8K 的 myClass 项目,以前约 16K)。但是,通过 JSON 打开失败的旧存档(什么有效)和通过portableBinaryArchive 打开新存档,我看不出调试器的结构没有区别。我有点难过从这一切中学到什么,因为我不认为这是一个大小问题。
    • 由于问题可能在我这边,我会接受这个答案,因为它回答了我的问题,所以我在这里没有做任何根本错误的事情 :-) 再次感谢 @Azoth。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-20
    • 2011-06-30
    相关资源
    最近更新 更多