【问题标题】:How to prevent stack overflow when serializing a recursive graph structure with boost::serialization?使用 boost::serialization 序列化递归图结构时如何防止堆栈溢出?
【发布时间】:2017-06-26 08:11:03
【问题描述】:

我正在尝试使用 boost 序列化库序列化一个大型(几何)图形结构。

我把我的图存储为邻接表,也就是我的结构如下:

class Node {
  double x,y;
  std::vector<Node*> adjacent_nodes;
  ...
}

class Graph {
  std::vector<Node*> nodes;
  ...
}

现在有 > 10k 个节点,我的问题是,当我开始序列化(保存)我的图时,它会在返回之前递归调用所有这些节点的序列化,因为图是连接的。

更准确地说,在序列化 Graph 时,它会从序列化“nodes”向量中的第一个节点开始。这样做时,它需要序列化第一个节点的“adjacent_nodes”,例如包含第二个节点。

因此需要先序列化第二个节点,然后再返回第一个节点的序列化,以此类推。

我发现 this thread 从 2010 年开始,有人解释了完全相同的问题。但是,他们没有找到可行的解决方案。

任何帮助将不胜感激。

我的结构更详细:

class Node {

    double x,y;
    std::vector<Node*> adjacent_nodes;

public:

    inline double get_x() const { return x; }
    inline double get_y() const { return y; }
    inline std::vector<Node*> const& get_adjacent_nodes() const { return adjacent_nodes; }

    Node (double x, double y):x(x),y(y) {}

    void add_adjacent(Node* other) {
        adjacent_nodes.push_back(other);
    }

private:

    Node() {}

  friend class boost::serialization::access;
  template <class Archive>
  void serialize(Archive &ar, const unsigned int) {
    ar & x;
        ar & y;
        ar & adjacent_nodes;
  }

};

class Simple_graph {

std::vector<Node*> nodes;

void add_edge(int firstIndex, int secondIndex) {
    nodes[firstIndex]->add_adjacent(nodes[secondIndex]);
    nodes[secondIndex]->add_adjacent(nodes[firstIndex]);
}

public:

/* methods to get the distance of points, to read in the nodes, and to generate edges */

~Simple_graph() {
    for (auto node: nodes) {
        delete node;
    }
}

private:

  friend class boost::serialization::access;
  template <class Archive>
  void serialize(Archive &ar, const unsigned int) {
    ar & nodes;
  }

};

编辑:添加在上述线程中提出的一些建议,引用 Dominique Devienne:

1) 在第一遍保存所有节点没有它们的拓扑信息 向量,从而为它们记录所有“跟踪”的指针,然后 为每个写拓扑信息,从那时起你不“递归”,你 仅将“ref”写入已序列化的指针。

2) 可以将“弱引用”写入指针, 它仅将指针添加到带有特殊标志的“跟踪”地图 说它还没有“真正”写出来,所以写拓扑 尚未写入的节点类似于“前向引用” 那些相邻的节点。要么节点真的稍后再写 开,否则永远不会,我想序列化应该处理 优雅地。

#1 不需要更改 boost 序列化,但将责任放在客户端代码上。特别是因为你必须“外部”保存 邻居,所以不再封装好,写一个子集 表面的节点变得更加复杂。

#2 需要在遇到前向引用时提前读取实际对象,此外还需要一个单独的映射到 知道在哪里寻找它。这可能与boost不兼容 序列化(我承认我对此一无所知)。

现在可以实施这些提案吗?

【问题讨论】:

    标签: c++ serialization boost graph stack-overflow


    【解决方案1】:

    由于您已经有了一个指向所有节点的向量,您可以使用索引序列化 adjacent_nodes 向量,而不是序列化实际的节点数据。

    序列化节点时,您需要将this 指针转换为索引。如果您可以将节点索引存储在节点中,这是最简单的,否则您必须通过 nodes 搜索才能找到正确的指针(可以通过创建某种关联容器将指针映射到索引)。

    当您需要读入数据时,您可以创建初始的nodes 向量,其中填充指向空/虚拟节点的指针(在序列化时将被填充)。

    如果这不可行,您可以将节点索引加载到临时数组中,然后在读入所有节点后返回并填充指针。但您不必寻找或重新读取你的文件。

    【讨论】:

    • 感谢您的回答。您建议,在回读时,我会创建随后填充的虚拟节点。我如何将其融入反序列化过程?是否有可能确保在预定的虚拟位置创建新节点?或者我可以预测,节点将在哪里创建?
    • @cero 我不熟悉 boost 序列化的细节,所以我不知道你是否可以在反序列化之前用Node * 填充你的向量,或者 boost 是否一直这样做。如果是这种情况,那么您将不得不执行我在上一段中建议的操作,在其中保留所有相邻的节点索引,然后在读完所有内容后返回并将它们转换为指针。如果相邻节点总是在'nodes'中更早(所以它们已经被反序列化了)然后你可以引用在这个过程中创建的指针。
    • 在做了一些研究之后,如果我坚持使用基于指针的数据结构,我想这确实是唯一的方法。在我的例子中,我用索引向量代替了指针向量,而不是复杂的双向反序列化。虽然不完全令人满意,但我认为您的答案与我能得到的一样接近。谢谢你。我会将您的答案标记为已接受。
    【解决方案2】:

    如果图表中没有任何大的循环,您可以按照图表“end”中的节点出现在向量开头的方式对节点向量进行排序。

    示例: 假设我们有:

    p1->p2->p3->....->p1000
    

    如果您尝试序列化vector v = {p1, p2, p3, ... , p1000},您将失败 但它适用于vector v = {p1000, p999, p998, ... , p1} 但如果你有类似的东西,你就没有机会

    p1->p2->p3->....->p1000->p1 
    

    【讨论】:

    • 您能详细说明一下吗?这看起来很奇怪
    猜你喜欢
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 2018-02-08
    • 1970-01-01
    • 2019-05-31
    • 2018-05-28
    • 2019-09-25
    • 1970-01-01
    相关资源
    最近更新 更多