C++ 在基于BGL的新类中,自定义函数addEdge的返回值应该是多少?
我尝试实现一个基于的图形类。添加边时,我返回已添加边的边描述符,但如果该边已存在,则不应添加该边。那我该怎么回呢?不幸的是,C++ 在基于BGL的新类中,自定义函数addEdge的返回值应该是多少?,c++,boost,iterator,api-design,boost-graph,C++,Boost,Iterator,Api Design,Boost Graph,我尝试实现一个基于的图形类。添加边时,我返回已添加边的边描述符,但如果该边已存在,则不应添加该边。那我该怎么回呢?不幸的是,null\u-edge()不存在(与null\u-vertex()不同)。它可以是一个std::pair,具有适当的边缘迭代器类型e_It_t,但是我如何才能将迭代器添加到新的边缘?不要使用将近10年的类。它已经过时了 据我所知,我一直来BGL,这是。。。至少从2010年开始。从根本上说,没有什么比直接推进更容易的了 另一个奇怪的特性是,不知何故,只有互补边可以插入到该图中
null\u-edge()
不存在(与null\u-vertex()
不同)。它可以是一个std::pair
,具有适当的边缘迭代器类型e_It_t
,但是我如何才能将迭代器添加到新的边缘?不要使用将近10年的类。它已经过时了
据我所知,我一直来BGL,这是。。。至少从2010年开始。从根本上说,没有什么比直接推进更容易的了
另一个奇怪的特性是,不知何故,只有互补边可以插入到该图中。这可能是您想要的,但它并不保证拥有完整的类,IMO
事实上,拥有自定义类型会删除ADL,这会使事情变得更加乏味,除非您去添加其他操作(比如,你知道,out\u-edges
或in\u-edges
,这大概是你首先想要一个双向图的原因;也许你实际上希望有一个iterable范围,而不是pair
,这需要你编写老式的for循环)
现在我已经做了一些准备,让我们来演示:
使用过时的包装器类
链接包装器提供如下用途:
struct VertexProperties { int i; };
struct EdgeProperties { double weight; };
int main() {
using MyGraph = Graph<VertexProperties, EdgeProperties>;
MyGraph g;
VertexProperties vp;
vp.i = 42;
MyGraph::Vertex v1 = g.AddVertex(vp);
g.properties(v1).i = 23;
MyGraph::Vertex v2 = g.AddVertex(vp);
g.properties(v2).i = 67;
g.AddEdge(v1, v2, EdgeProperties{1.0}, EdgeProperties{0.0});
for (auto vr = g.getVertices(); vr.first!=vr.second; ++vr.first) {
auto& vp = g.properties(*vr.first);
std::cout << "Vertex " << vp.i << "\n";
for (auto er = g.getAdjacentVertices(*vr.first); er.first!=er.second; ++er.first) {
auto s = *vr.first;
auto t = *er.first;
// erm how to get edge properties now?
std::cout << "Edge " << g.properties(s).i << " -> " << g.properties(t).i << " (weight?!?)\n";
}
}
}
注意,我并没有费心去解决获取边缘权重的问题(我们根本不容易从接口获取边缘描述符)。
for循环将我们拖回至少6年的时间。这几乎不是最糟糕的问题。大概,您需要图形。假设您需要最小割或最短路径。这意味着您需要调用一个需要边权重的算法。如下所示:
// let's find a shortest path:
// build the vertex index map
boost::property_map<MyGraph::GraphContainer, vertex_properties_t>::const_type vpmap =
boost::get(vertex_properties, g.getGraph());
// oops we need the id from it. No problem, it takes only rocket science:
struct GetId {
int operator()(VertexProperties const& vp) const {
return vp.i;
}
};
GetId get_id;
boost::transform_value_property_map<GetId,
boost::property_map<MyGraph::GraphContainer, vertex_properties_t>::const_type,
int> id_map
= boost::make_transform_value_property_map<int>(get_id, vpmap);
// build the weight map
boost::property_map<MyGraph::GraphContainer, edge_properties_t>::const_type epmap =
boost::get(edge_properties, g.getGraph());
// oops we need the weight from it. No problem, it takes only rocket science:
struct GetWeight {
double operator()(EdgeProperties const& ep) const {
return ep.weight;
}
};
GetWeight get_weight;
boost::transform_value_property_map<GetWeight,
boost::property_map<MyGraph::GraphContainer, edge_properties_t>::const_type,
double> weight_map
= boost::make_transform_value_property_map<double>(get_weight, epmap);
// and now we "simply" use Dijkstra:
MyGraph::vertex_range_t vertices = g.getVertices();
//size_t n_vertices = g.getVertexCount();
MyGraph::Vertex source = *vertices.first;
std::map<MyGraph::Vertex, MyGraph::Vertex> predecessors;
std::map<MyGraph::Vertex, double> distance;
boost::dijkstra_shortest_paths(g.getGraph(), source,
boost::predecessor_map(boost::make_assoc_property_map(predecessors))
.distance_map(boost::make_assoc_property_map(distance))
.weight_map(weight_map)
.vertex_index_map(id_map));
相同的用例-创建和dijkstra
它们仍然很简单,或者说实际上更简单。完整的代码从249行下降到57行:
#include <boost/graph/adjacency_list.hpp>
namespace MyLib {
template <typename VertexProperties, typename EdgeProperties>
using Graph = boost::adjacency_list<boost::setS, boost::listS, boost::bidirectionalS, VertexProperties, EdgeProperties>;
}
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
struct VertexProperties { int i; };
struct EdgeProperties { double weight; };
int main() {
using boost::make_iterator_range;
using MyGraph = MyLib::Graph<VertexProperties, EdgeProperties>;
MyGraph g;
auto v1 = add_vertex({42}, g);
auto v2 = add_vertex({42}, g);
g[v1].i = 23;
g[v2].i = 67;
add_edge(v1, v2, EdgeProperties{ 1.0 }, g);
add_edge(v2, v1, EdgeProperties{ 0.0 }, g);
for (auto v : make_iterator_range(vertices(g))) {
std::cout << "Vertex " << g[v].i << "\n";
}
for (auto e : make_iterator_range(boost::edges(g))) {
auto s = source(e, g);
auto t = target(e, g);
std::cout << "Edge " << g[s].i << " -> " << g[t].i << " (weight = " << g[e].weight << ")\n";
}
// let's find a shortest path:
auto id_map = get(&VertexProperties::i, g);
auto weight_map = get(&EdgeProperties::weight, g);
auto source = *vertices(g).first;
using Vertex = MyGraph::vertex_descriptor;
std::map<Vertex, Vertex> predecessors;
std::map<Vertex, double> distance;
std::map<Vertex, boost::default_color_type> colors;
boost::dijkstra_shortest_paths(
g, source,
boost::vertex_color_map(boost::make_assoc_property_map(colors))
.predecessor_map(boost::make_assoc_property_map(predecessors))
.distance_map(boost::make_assoc_property_map(distance))
.weight_map(weight_map)
.vertex_index_map(id_map));
}
#include <boost/graph/adjacency_list.hpp>
namespace MyLib {
template <typename VertexProperties, typename EdgeProperties>
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS, VertexProperties, EdgeProperties>;
}
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
struct VertexProperties { int i; };
struct EdgeProperties { double weight; };
int main() {
using boost::make_iterator_range;
using MyGraph = MyLib::Graph<VertexProperties, EdgeProperties>;
MyGraph g;
add_vertex({23}, g);
add_vertex({67}, g);
add_edge(0, 1, EdgeProperties{ 1.0 }, g);
add_edge(1, 0, EdgeProperties{ 0.0 }, g);
for (auto v : make_iterator_range(vertices(g))) {
std::cout << "Vertex " << g[v].i << "\n";
}
for (auto e : make_iterator_range(boost::edges(g))) {
auto s = source(e, g);
auto t = target(e, g);
std::cout << "Edge " << g[s].i << " -> " << g[t].i << " (weight = " << g[e].weight << ")\n";
}
// let's find a shortest path:
std::vector<size_t> predecessors(num_vertices(g));
std::vector<double> distance(num_vertices(g));
boost::dijkstra_shortest_paths(g, *vertices(g).first,
boost::predecessor_map(predecessors.data()).distance_map(distance.data())
.weight_map(get(&EdgeProperties::weight, g)));
}
输出:
Vertex 23
Vertex 67
Edge 23 -> 67 (weight = 1)
Edge 67 -> 23 (weight = 0)
等等,别忘了这个问题!
我不会的!我想以上说明了问题的严重性
如果您没有自定义类包装的障碍,那么检测重复边是一个给定条件(请参见背景):
如果你愿意,你当然可以写得更表达:
Graph::edge_descriptor e;
bool inserted;
boost::tie(e, inserted) = add_edge(request.from, request.to, { request.weight }, g);
或者,凭借一些c++17的天赋:
auto [e, inserted] = add_edge(request.from, request.to, { request.weight }, g);
从这里开始更多
此外,很可能您也需要对顶点进行唯一性检查,因此您最终会得到图形创建代码,如下面的答案所示:
Graph read\u Graph(){
标准::istringstream iss(R)(
0 1 0.001
0 2 0.1
0 3 0.001
1 5 0.001
2 3 0.001
3 4 0.1
1 482 0.1
482 635 0.001
4 705 0.1
705 5 0.1
1 1491 0.01
1 1727 0.01
1 1765 0.01)");
图g;
std::map idx;//临时查找现有顶点
自动顶点=[&](int-id)可变{
auto it=idx.find(id);
如果(it!=idx.end())
返回->秒;
返回idx.emplace(id,add_顶点(id,g)).first->second;
};
for(std::string line;getline(iss,line);){
std::istringstream ls(线);
整数s,t;双w;
如果(ls>>s>>t>>w){
添加_边(顶点(s)、顶点(t)、w、g);
}否则{
std::cerr开箱即用:可选
可能合适前两个代码示例在Boost Graph中遇到了一个错误,dijkstra_最短路径
没有像它应该的那样使用自定义颜色映射。这个问题已经出现了,但它再次出现了。它与这个答案无关,我将看看是否可以尽快报告。我不确定我是否可以这样做这值得一个新问题,但是你的(太好了,谢谢!)答案让我问:你能推荐任何学习BGL的有序方法吗?官方文件不太适合这个IMO。我使用了stackoverflow…我必须说,回答确实有助于了解隐蔽处、裂缝、怪癖和疣。过了一段时间,你就有了一种诀窍,可以找到既具有强大功能又具有可读性的局部最优方法。这是一个很好的方法旅行,但如果你有时间…如果你能比别人更快地回答问题@sehe@Rerito我可以向你保证,在标签中,你不必急于第一个回答。特别是有了它,你可以很容易地抽出几天时间。另外,不要给人留下我“a”的印象这些答案很快就能得到:我知道我经常花几个小时来回答特定的问题。
Vertex 23
Vertex 67
Edge 23 -> 67 (weight = 1)
Edge 67 -> 23 (weight = 0)
struct { size_t from, to; double weight; } edge_data[] = {
{0, 1, 1.0},
{1, 0, 0.0},
{0, 1, 99.999} // oops, a duplicate
};
for(auto request : edge_data) {
auto addition = add_edge(request.from, request.to, { request.weight }, g);
if (!addition.second) {
auto& weight = g[addition.first].weight;
std::cout << "Edge already existed, changing weight from " << weight << " to " << request.weight << "\n";
weight = request.weight;
}
}
Edge already existed, changing weight from 1 to 99.999
Graph::edge_descriptor e;
bool inserted;
boost::tie(e, inserted) = add_edge(request.from, request.to, { request.weight }, g);
auto [e, inserted] = add_edge(request.from, request.to, { request.weight }, g);
Graph read_graph() {
std::istringstream iss(R"(
0 1 0.001
0 2 0.1
0 3 0.001
1 5 0.001
2 3 0.001
3 4 0.1
1 482 0.1
482 635 0.001
4 705 0.1
705 5 0.1
1 1491 0.01
1 1727 0.01
1 1765 0.01)");
Graph g;
std::map<int,Vertex> idx; // temporary lookup of existing vertices
auto vertex = [&](int id) mutable {
auto it = idx.find(id);
if (it != idx.end())
return it->second;
return idx.emplace(id, add_vertex(id, g)).first->second;
};
for (std::string line; getline(iss, line);) {
std::istringstream ls(line);
int s,t; double w;
if (ls >> s >> t >> w) {
add_edge(vertex(s), vertex(t), w, g);
} else {
std::cerr << "Skipped invalid line '" << line << "'\n";
}
}
return g;
}