使用boost::序列化递归图结构时,如何防止堆栈溢出



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

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

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

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

更准确地说,在序列化Graph时,它将从序列化"nodes"向量中的第一个节点开始。在这样做的同时,它需要序列化第一个节点的"相邻节点",例如第二个节点包含在其中。

因此,它需要在返回第一个节点的序列化之前序列化第二个节点,依此类推

我在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将需要在遇到前向引用时提前查找以读取实际对象,此外还需要一个单独的映射知道在哪里寻找。这可能与助推不相容连载(我承认我对此一无所知)。

到目前为止,这些提议中的任何一项都能实施吗?

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

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

当您需要读入数据时,您可以创建初始nodes向量,该向量填充有指向空/伪节点的指针(在序列化时会填充这些指针)。

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

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

示例:假设我们有:

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

如果尝试序列化vector v = {p1, p2, p3, ... , p1000},则会失败但它将与vector v = {p1000, p999, p998, ... , p1}一起工作但如果你有这样的东西,你就没有机会了

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

最新更新