I am trying to create a graph via Boost graph library, that has custom indexing (stable ones when vertexes are deleted),
I copied the sollution proposed here to create the graph which is working very well for me How to configure boost::graph to use my own (stable) index for vertices?
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>
#include <iomanip>
#include <boost/graph/dijkstra_shortest_paths.hpp>
using namespace boost;
struct Vertex {
int id;
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex, property<edge_weight_t, int>>;
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = int;
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}; }
};
};
typedef graph_traits<Graph>::vertex_descriptor vertex_descriptor;
Graph g;
add_vertex(1, g);
add_vertex(2, g);
add_vertex(5, g);
add_vertex(6, g);
add_vertex(7, g);
add_edge(1, 5, 1, g);
add_edge(2, 6, 1, g);
add_edge(2, 7, 2, g);
add_edge(5, 2, 7, g);
add_edge(5, 6, 3, g);
add_edge(6, 7, 1, g);
add_edge(7, 1, 1, g);
add_edge(7, 2, 1, g);
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
But then the fun started when I tried to use Dijkstra on this graph. On a normal graph, I would use Dijkstra this way
std::vector<vertex_descriptor> p(num_vertices(g));
std::vector<int> d(num_vertices(g));
vertex_descriptor source_idx = vertex(1, g);
dijkstra_shortest_paths(g, source_idx,
predecessor_map(boost::make_iterator_property_map(
p.begin(), get(vertex_index, g)))
.distance_map(boost::make_iterator_property_map(
d.begin(), get(vertex_index, g))));
But now that I am changing the indexes, most of this wont work, mainly get(boost::vertex_index, g), that raises In template: cannot form a reference to 'void' and No matching function for call to 'get'
and I have no idea how I need to change that, tried using get(&Vertex::id, g) instead but I get this
No matching function for call to 'dijkstra_shortest_paths'
Thanks in advance for your help!
It seems you're conflating 3 concepts here:
ad 1. The names are what you achieved with the
internal_vertex_nametrait. They offer a convenience mapping from an application-domain identifier ("name", yourVertex::idin the example) to vertex descriptors. In database terms this can be thought of as a "lookup index" into the vertices, but it isn't what BGL refers to as vertex index.ad 2. The descriptors have semantics that you can change by choosing a Vertex Container Selector. In your example you did indeed choose
boost::listSwhich does make the descriptors and iterators stable across mutation (except for deleted vertices).ad 3. The Vertex Index is a specific unique mapping of all vertex descriptors to the integral range
[0, N)(N == num_vertices(g)) that some algorithms assume so they can be implemented efficiently regardless of the chosen graph model. Consider it "a consistent way of numbering vertices". For some VertexContainer selectors (i.e.boost::vecS) the index is trivial and Boost knows how to implicitly deduce thevertex_index_tproperty map to use in algorithms.The Problem
In your code, the problem is that you do not have an implicit
vertex_index_tproperty map, due tolistScontainer selector.By far the easiest "solution" here is to use
vecSas your name property already allows you a stable identification [1]:Which prints Live On Coliru:
NOTE How the graph creation is significantly simplified.
source_idxis a misnomer (it's not an index, but a descriptor). Also, instead of magically selecting a vertex by some ordinal into the sequence of vertices internally stored, consider addressing by your name property:Manual
vertex_index_tindexIf you really need to store descriptors that stay valid across mutations, you need to supply an artificial external mapping:
Which you can then supply using a named parameter:
However, since vertices are not ordinal, your predecessor/distance maps also need indirection:
Interpreting the predecessor map now requires interpreting the index of the predecessors vector by reverse lookup into
manual_index... Perhaps at this rate it's way easier to make actual associative property maps all the way:Live On Coliru
Printing
[1] with O(log n) lookup