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;
}