Using boost graph library with a custom class for vertices

我与影子孤独终老i 提交于 2019-12-01 08:15:06
  1. without changing your declarations, this is a bit painful, but possible:

    {
        boost::dynamic_properties dp;
        boost::property_map<graph_t, boost::vertex_my_custom_class_t>::type custom = get(boost::vertex_my_custom_class, graph);
        dp.property("node_id", boost::make_transform_value_property_map(std::mem_fn(&my_custom_class::get_int), custom));
        dp.property("label", boost::make_transform_value_property_map(std::mem_fn(&my_custom_class::get_my_string), custom));
        boost::write_graphviz_dp(std::cout, graph, dp);
    }
    

    Printing: Live On Coliru

    digraph G {
    123 [label=Lorem];
    456 [label=ipsum];
    123 [label=Lorem];
    123->456 ;
    123->123 ;
    }
    
  2. You will want to take care of this externally. Why not have an intrusive set of nodes and verify the contraints that way. Changing the Vertex Container Selector as you say has no effect (it just ends up storing the vertex descriptors in ascending order, while they stay unique as they were before).

    A side effect is changing from contiguously allocated vertex storage to node-based (pro: iterator/reference stability, con: allocation overhead, reduced locality of reference, non-implicit vertex_index). The latter is the culprit: many things in BGL require a vertex index and if one is not implied (by using vecS e.g.) you have to pass one.

    Interestingly, since I used write_graphviz_dp with a specific node_id property there's no need for the implicit vertex index just now, so you could change vecS to setS and observe the behaviour: Live On Coliru

  3. I think that's not the right time to check. There is not really a better way than to visit all vertices, unless you keep an external index updated.

Style/Simplification

Since std::shared_ptr implies you have c++11, let's use it.

Also, the whole dance with a custom property is mostly a way more clumsy of doing property bundles: these are syntactically easier and better supported.

See the difference:

Note I was over-enthusiastic for a second, using a shared-ptr you still require the the transform-value-property-map:

Live On Coliru

#include <boost/graph/graphviz.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <iostream>

struct MyVertex {
    MyVertex(std::string label, int id) : _label(std::move(label)), _id(id) {}

    std::string label() const { return _label; }
    int         id()    const { return _id;    }

    bool operator<(const MyVertex &rhs) const { return std::tie(_id, _label) < std::tie(rhs._id, rhs._label); }
  private:
    std::string _label;
    int _id;
};

using graph_t = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, std::shared_ptr<MyVertex>>;

int main() {
    graph_t graph;

    auto v1 = add_vertex(std::make_shared<MyVertex>("Lorem", 123), graph);
    auto v2 = add_vertex(std::make_shared<MyVertex>("ipsum", 456), graph);
    auto v3 = add_vertex(std::make_shared<MyVertex>("Lorem", 123), graph);

    add_edge(v1, v2, graph);
    add_edge(v1, v3, graph);

    {
        boost::dynamic_properties dp;
        auto bundle = get(boost::vertex_bundle, graph);
        dp.property("node_id", make_transform_value_property_map(std::mem_fn(&MyVertex::id), bundle));
        dp.property("label", make_transform_value_property_map(std::mem_fn(&MyVertex::label), bundle));

        write_graphviz_dp(std::cout, graph, dp);
    }
}

Comparing: Without Shared Pointers

I'm not saying you must not use it, but I'm also not convinced you need it. So, let's remove it so you see the difference:

Live On Coliru

#include <boost/graph/graphviz.hpp>
#include <iostream>

struct MyVertex {
    std::string label;
    int id;
    bool operator<(const MyVertex &rhs) const { return std::tie(id, label) < std::tie(rhs.id, rhs.label); }
};

using graph_t = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, MyVertex>;

int main() {
    graph_t graph;

    auto v1 = add_vertex({"Lorem", 123}, graph);
    auto v2 = add_vertex({"ipsum", 456}, graph);
    auto v3 = add_vertex({"Lorem", 123}, graph);

    add_edge(v1, v2, graph);
    add_edge(v1, v3, graph);

    boost::dynamic_properties dp;
    dp.property("node_id", boost::get(&MyVertex::id, graph));
    dp.property("label", boost::get(&MyVertex::label, graph));

    write_graphviz_dp(std::cout, graph, dp);
}

That's roughly half the code. From here we can explore how to add the desired feature(s)

Uniqueness: Let's try Our Hand

The simplest thing to do is to check the existing nodes before adding a new one:

Live On Coliru

#include <boost/graph/graphviz.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>

struct MyVertex {
    std::string label;
    int id;

    auto key() const { return std::tie(id,label); }
    bool operator< (const MyVertex &rhs) const { return key() <  rhs.key(); }
    bool operator==(const MyVertex &rhs) const { return key() == rhs.key(); }
    bool operator!=(const MyVertex &rhs) const { return key() != rhs.key(); }
};

using graph_t = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, MyVertex>;

int main() {
    graph_t graph;

    auto node = [&graph](std::string name, int id) {
        for (auto&& v : boost::make_iterator_range(vertices(graph)))
            if (graph[v] == MyVertex{name, id})
                return v;
        return add_vertex({name, id}, graph);
    };

    auto v1 = node("Lorem", 123);
    auto v2 = node("ipsum", 456);
    auto v3 = node("Lorem", 123);

    assert(v3==v1);

    add_edge(v1, v2, graph);
    add_edge(v1, v3, graph);

    boost::dynamic_properties dp;
    dp.property("node_id", boost::get(&MyVertex::id, graph));
    dp.property("label", boost::get(&MyVertex::label, graph));

    write_graphviz_dp(std::cout, graph, dp);
}

Note how v3 now equals v1:

digraph G {
123 [label=Lorem];
456 [label=ipsum];
123->456 ;
123->123 ;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!