如何配置 boost::graph 为顶点使用我自己的(稳定的)索引?
How to configure boost::graph to use my own (stable) index for vertices?
最简单的boost::adjacency_list
使用std::size_t
作为基础vertex_descriptor(索引)。
boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
boost::no_property,
boost::no_property
> graph;
一旦我知道了顶点描述符,我就可以快速访问所需的顶点。
auto vertex = graph[idx]; // idx is the veretx descriptor
但是,当图形发生变异时,无法保证 vertex_decriptor
会稳定。
auto v0 = boost::add_vertex(graph);
auto v1 = boost::add_vertex(graph);
auto v2 = boost::add_vertex(graph);
boost::remove_vertex(v0, graph); // v1 and v2 are no longer valid
我希望能够快速找到一个特定的顶点 - 这意味着我希望避免遍历图形结构来搜索我知道存在的顶点。
我的想法是,我可以用 VertexListS 模板参数的不同选择器以某种方式调整 boost::adjacency_list
,这将允许我使用我自己提供的 vertex_descripor(索引)。
我探索了使用关联容器选择器(例如内置 boost::hash_mapS
)的可能性,但在调用 add_vertex
时,我似乎无法控制它 returns 的确切 ID。
我希望能够为每个顶点使用我自己的 ID (vertex_descriptor)。
我会尝试用我希望的代码更清楚一点:
// the following type will serve as the vertex_descriptor:
using my_id_type = std::size_t;
struct MyVertex
{
my_id_type id;
// some other fields
}
// add vertices with unique identifiers that will also serve as the vertex_descriptors
boost::add_vertex(MyVertex{1111}, graph); // I expect this to return 1111
boost::add_vertex(MyVertex{2222}, graph);
boost::add_vertex(MyVertex{1234}, graph);
boost::remove_vertex(1111, graph); // I expect this to remove the first vertex
// access a specific vertex using its unique, stable descriptor:
auto vertex = graph[1234];
可以使用 boost::graph 实现吗?
Can this be achieved using boost::graph?
对于 BGL,答案几乎总是“是”。它是现存最深刻的通用库设计之一。
令我惊讶的是,adjacency_list
的 type-hierarchy 中出现了一些新内容。显然现在有一个 named_graph
mixin(实际上是 maybe_name_graph
),它使用顶点束上的特征来检测“内部名称”。
这意味着你可以靠近了。尽管顶点描述符 不会 成为您的 ID,但您 可以 进行 O(1) 查找。而且接口有一些便利,所以你可以写:
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
备注:
- 查找的时间复杂度为 O(1),因为 name-vertex 查找基于无序(散列)索引
- 如果你不小心使
id
类型与 vertex_descriptor
类型相同(或者如果你的论点两者都有同样“远”的转换。
- 据我所知,内部名称 属性 是 而不是 自动选择为
vertex_index_t
或 vertex_name_t
属性.
第 1 步:图表
struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
到目前为止没有惊喜。我
- 选择添加第二个成员
other
只是为了显示when/how它是默认构造的
- 选择
listS
因为它
- 一个。提供 reference/descriptor 稳定性(移除的顶点除外)
- b.导致不透明
vertex_descriptor
(void*
) 与重载解析中的 id 类型 (size_t
) 不冲突
第 2 步:命名特征
接下来我们向 BGL 介绍我们的内部顶点名称。
This is purely parameterized by Vertex
bundle, so keep in mind that different graphs using the same bundle would use the same name traits.
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extract_name_type = typename internal_vertex_name<Vertex>::type;
using vertex_name_type = typename remove_cv<typename remove_reference<
typename extract_name_type::result_type>::type>::type;
public:
using argument_type = vertex_name_type;
using result_type = Vertex;
result_type operator()(const vertex_name_type& id) const {
return {id};
}
};
};
备注
我们当然可以hard-code第二专业的知识:
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
Vertex operator()(size_t id) const { return {id}; }
};
};
它非常重要return id 引用。否则会导致 UB 没有来自 library/compiler
的诊断
步骤 #3 Adding/Finding 顶点
现在可以添加顶点了。通过“名称”(您的 id
):
auto x = add_vertex(1111, g); // by id
或者您在问题中预期的 old-fashioned 方式:
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
重复添加无效:
assert(add_vertex(1111, g) == x);
存在不同的查找。 vertex_by_property
return 是一个 optional<vertex_descriptor>
给定的顶点束。
assert(x == *g.vertex_by_property(g[x]));
它通过从传递的 bundle 中提取“内部名称”来实现,因此 bundle 不需要包含 id 之外的任何其他状态:
assert(x == *g.vertex_by_property(Vertex{1111}));
虽然感觉像一个实现细节,但实际multi_index_container
实现名称-> 描述符索引也暴露了:
assert(x == *g.named_vertices.find(1111));
步骤 #4 添加边
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
从你之前的问题中借用了一页:)
显然,您仍然可以通过顶点描述符添加边。
步骤 #5 其他操作
从之前的答案中借用更多页面:
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
完整演示
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>
#include <iomanip>
struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extractor = typename internal_vertex_name<Vertex>::type;
using name_t = std::decay_t<typename extractor::result_type>;
public:
using argument_type = name_t;
using result_type = Vertex;
result_type operator()(const name_t& id) const { return {id}; }
};
};
int main() {
Graph g;
{
auto x = add_vertex(1111, g); // by id
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
// duplicate additions have no effect
assert(add_vertex(1111, g) == x);
// different lookups exist
assert(x == *g.named_vertices.find(1111));
assert(x == *g.vertex_by_property(Vertex{1111}));
assert(x == *g.vertex_by_property(g[x]));
}
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
}
打印
---
1111 --> 2222
2222 --> 1111
---
default-constructed --> twotwotwotwo
twotwotwotwo --> default-constructed
---
1111 "default-constructed"
2222 "twotwotwotwo"
==== removing 1111
---
2222 -->
---
twotwotwotwo -->
旁注:
- 您不能对顶点使用关联容器选择器。指定
hash_mapS
导致 unordered_set<void*, hash<void*>, equal_to<void*>, allocator<void*>>
作为 m_vertices
类型。
最简单的boost::adjacency_list
使用std::size_t
作为基础vertex_descriptor(索引)。
boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
boost::no_property,
boost::no_property
> graph;
一旦我知道了顶点描述符,我就可以快速访问所需的顶点。
auto vertex = graph[idx]; // idx is the veretx descriptor
但是,当图形发生变异时,无法保证 vertex_decriptor
会稳定。
auto v0 = boost::add_vertex(graph);
auto v1 = boost::add_vertex(graph);
auto v2 = boost::add_vertex(graph);
boost::remove_vertex(v0, graph); // v1 and v2 are no longer valid
我希望能够快速找到一个特定的顶点 - 这意味着我希望避免遍历图形结构来搜索我知道存在的顶点。
我的想法是,我可以用 VertexListS 模板参数的不同选择器以某种方式调整 boost::adjacency_list
,这将允许我使用我自己提供的 vertex_descripor(索引)。
我探索了使用关联容器选择器(例如内置 boost::hash_mapS
)的可能性,但在调用 add_vertex
时,我似乎无法控制它 returns 的确切 ID。
我希望能够为每个顶点使用我自己的 ID (vertex_descriptor)。
我会尝试用我希望的代码更清楚一点:
// the following type will serve as the vertex_descriptor:
using my_id_type = std::size_t;
struct MyVertex
{
my_id_type id;
// some other fields
}
// add vertices with unique identifiers that will also serve as the vertex_descriptors
boost::add_vertex(MyVertex{1111}, graph); // I expect this to return 1111
boost::add_vertex(MyVertex{2222}, graph);
boost::add_vertex(MyVertex{1234}, graph);
boost::remove_vertex(1111, graph); // I expect this to remove the first vertex
// access a specific vertex using its unique, stable descriptor:
auto vertex = graph[1234];
可以使用 boost::graph 实现吗?
Can this be achieved using boost::graph?
对于 BGL,答案几乎总是“是”。它是现存最深刻的通用库设计之一。
令我惊讶的是,adjacency_list
的 type-hierarchy 中出现了一些新内容。显然现在有一个 named_graph
mixin(实际上是 maybe_name_graph
),它使用顶点束上的特征来检测“内部名称”。
这意味着你可以靠近了。尽管顶点描述符 不会 成为您的 ID,但您 可以 进行 O(1) 查找。而且接口有一些便利,所以你可以写:
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
备注:
- 查找的时间复杂度为 O(1),因为 name-vertex 查找基于无序(散列)索引
- 如果你不小心使
id
类型与vertex_descriptor
类型相同(或者如果你的论点两者都有同样“远”的转换。 - 据我所知,内部名称 属性 是 而不是 自动选择为
vertex_index_t
或vertex_name_t
属性.
第 1 步:图表
struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
到目前为止没有惊喜。我
- 选择添加第二个成员
other
只是为了显示when/how它是默认构造的 - 选择
listS
因为它- 一个。提供 reference/descriptor 稳定性(移除的顶点除外)
- b.导致不透明
vertex_descriptor
(void*
) 与重载解析中的 id 类型 (size_t
) 不冲突
第 2 步:命名特征
接下来我们向 BGL 介绍我们的内部顶点名称。
This is purely parameterized by
Vertex
bundle, so keep in mind that different graphs using the same bundle would use the same name traits.
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extract_name_type = typename internal_vertex_name<Vertex>::type;
using vertex_name_type = typename remove_cv<typename remove_reference<
typename extract_name_type::result_type>::type>::type;
public:
using argument_type = vertex_name_type;
using result_type = Vertex;
result_type operator()(const vertex_name_type& id) const {
return {id};
}
};
};
备注
我们当然可以hard-code第二专业的知识:
template <> struct boost::graph::internal_vertex_constructor<Vertex> { struct type { Vertex operator()(size_t id) const { return {id}; } }; };
它非常重要return id 引用。否则会导致 UB 没有来自 library/compiler
的诊断
步骤 #3 Adding/Finding 顶点
现在可以添加顶点了。通过“名称”(您的 id
):
auto x = add_vertex(1111, g); // by id
或者您在问题中预期的 old-fashioned 方式:
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
重复添加无效:
assert(add_vertex(1111, g) == x);
存在不同的查找。 vertex_by_property
return 是一个 optional<vertex_descriptor>
给定的顶点束。
assert(x == *g.vertex_by_property(g[x]));
它通过从传递的 bundle 中提取“内部名称”来实现,因此 bundle 不需要包含 id 之外的任何其他状态:
assert(x == *g.vertex_by_property(Vertex{1111}));
虽然感觉像一个实现细节,但实际multi_index_container
实现名称-> 描述符索引也暴露了:
assert(x == *g.named_vertices.find(1111));
步骤 #4 添加边
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
从你之前的问题中借用了一页:)
显然,您仍然可以通过顶点描述符添加边。
步骤 #5 其他操作
从之前的答案中借用更多页面:
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
完整演示
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>
#include <iomanip>
struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extractor = typename internal_vertex_name<Vertex>::type;
using name_t = std::decay_t<typename extractor::result_type>;
public:
using argument_type = name_t;
using result_type = Vertex;
result_type operator()(const name_t& id) const { return {id}; }
};
};
int main() {
Graph g;
{
auto x = add_vertex(1111, g); // by id
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
// duplicate additions have no effect
assert(add_vertex(1111, g) == x);
// different lookups exist
assert(x == *g.named_vertices.find(1111));
assert(x == *g.vertex_by_property(Vertex{1111}));
assert(x == *g.vertex_by_property(g[x]));
}
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
}
打印
---
1111 --> 2222
2222 --> 1111
---
default-constructed --> twotwotwotwo
twotwotwotwo --> default-constructed
---
1111 "default-constructed"
2222 "twotwotwotwo"
==== removing 1111
---
2222 -->
---
twotwotwotwo -->
旁注:
- 您不能对顶点使用关联容器选择器。指定
hash_mapS
导致unordered_set<void*, hash<void*>, equal_to<void*>, allocator<void*>>
作为m_vertices
类型。