Visitors
BGL algorithms are not just black boxes that return a result. As they run, they reach event points: moments where something interesting happens (a vertex is discovered, an edge is relaxed, a search backtracks). A visitor is an object whose methods get called at these event points. This lets you inject custom logic into an algorithm without modifying or rewriting it.
| Visitors are passed by value. If your visitor holds state (a counter, a reference to a container), the algorithm operates on a copy. Hold state by pointer or reference if you need to read it after the algorithm returns. |
Quick start: pre-built event visitors
For common tasks (recording predecessors, distances, timestamps), BGL provides ready-made event visitors. You pick the ones you need, combine them, and pass them to the algorithm.
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/visitors.hpp>
#include <iostream>
#include <vector>
int main() {
using namespace boost;
using Graph = adjacency_list<vecS, vecS, undirectedS>;
Graph g(6);
add_edge(0, 1, g);
add_edge(0, 2, g);
add_edge(1, 3, g);
add_edge(2, 4, g);
add_edge(3, 5, g);
// Record predecessors and distances using pre-built event visitors.
// No need to write a visitor class.
std::vector<int> pred(num_vertices(g), -1);
std::vector<int> dist(num_vertices(g), 0);
auto vis = make_bfs_visitor(
std::make_pair(
record_predecessors(pred.data(), on_tree_edge()),
record_distances(dist.data(), on_tree_edge())
)
);
breadth_first_search(g, vertex(0, g), visitor(vis));
for (std::size_t v = 0; v < num_vertices(g); ++v) {
std::cout << "vertex " << v
<< " predecessor=" << pred[v]
<< " distance=" << dist[v] << "\n";
}
}
vertex 0 predecessor=-1 distance=0
vertex 1 predecessor=0 distance=1
vertex 2 predecessor=0 distance=1
vertex 3 predecessor=1 distance=2
vertex 4 predecessor=2 distance=2
vertex 5 predecessor=3 distance=3
The key pattern:
-
Pick event visitors (
record_predecessors,record_distances, etc.) -
Bind each to an event tag (
on_tree_edge,on_discover_vertex, etc.) -
Combine with
std::make_pair(nest pairs for 3+:std::make_pair(a, std::make_pair(b, c))) -
Wrap in an algorithm-specific adaptor (
make_bfs_visitor,make_dfs_visitor,make_dijkstra_visitor)
Writing a custom visitor
When the pre-built visitors are not enough, write your own class. Inherit from a default visitor to get empty implementations for all event points, then override only the ones you care about.
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <iostream>
// Inherit from default_bfs_visitor to get empty defaults for all events.
// Override only the events you care about.
struct my_visitor : public boost::default_bfs_visitor {
template <typename Vertex, typename Graph>
void discover_vertex(Vertex v, const Graph&) const {
std::cout << " discovered " << v << "\n";
}
template <typename Edge, typename Graph>
void tree_edge(Edge e, const Graph& g) const {
std::cout << " tree edge " << source(e, g)
<< " -> " << target(e, g) << "\n";
}
};
int main() {
using namespace boost;
using Graph = adjacency_list<vecS, vecS, undirectedS>;
Graph g(5);
add_edge(0, 1, g);
add_edge(0, 2, g);
add_edge(1, 3, g);
add_edge(2, 4, g);
std::cout << "BFS from vertex 0:\n";
breadth_first_search(g, vertex(0, g), visitor(my_visitor()));
}
BFS from vertex 0:
discovered 0
tree edge 0 -> 1
discovered 1
tree edge 0 -> 2
discovered 2
tree edge 1 -> 3
discovered 3
tree edge 2 -> 4
discovered 4
The base classes are:
| Base class | For use with |
|---|---|
|
|
|
|
|
|
|
|
|
|
Event points by algorithm
Each algorithm defines its own set of event points:
BFS events
initialize_vertex, discover_vertex, examine_vertex, examine_edge,
tree_edge, non_tree_edge, gray_target, black_target, finish_vertex
DFS events
initialize_vertex, start_vertex, discover_vertex, examine_edge,
tree_edge, back_edge, forward_or_cross_edge, finish_edge,
finish_vertex
Pre-built event visitors
| Visitor | What it does | Typical event |
|---|---|---|
|
Records the predecessor vertex in a property map |
|
|
Records the predecessor edge (useful with parallel edges) |
|
|
Records the hop distance from the source |
|
|
Records discover/finish timestamps |
|
|
Writes a property value to an output stream |
any |
|
Assigns a fixed value to a property |
any |
|
Does nothing (placeholder) |
any |
Algorithm adaptors
Pre-built event visitors cannot be passed directly to an algorithm. They must be wrapped in an algorithm-specific adaptor that converts them into the full visitor interface the algorithm expects.
| Adaptor | Wraps event visitors for |
|---|---|
|
|
|
|
|
|
|
|
|
|
The adaptor dispatches each algorithm event to the matching event visitor in the list. Events that have no matching visitor are silently ignored.
Early termination
The only way to stop an algorithm early from a visitor is to throw an exception. This is the standard BGL pattern for single-target searches:
struct found_target {};
struct stop_at : public boost::default_bfs_visitor {
stop_at(Vertex t) : m_target(t) {}
template <typename V, typename G>
void discover_vertex(V v, const G&) const {
if (v == m_target) { throw found_target(); }
}
Vertex m_target;
};
try {
breadth_first_search(g, src, visitor(stop_at(target)));
} catch (const found_target&) {
// target found, algorithm stopped
}
Event tags
Event tags connect an event visitor to a specific event point. Each tag corresponds to one event in one or more algorithms.
| Tag | Fires when |
|---|---|
|
A vertex is initialized before the algorithm starts |
|
A vertex is first reached |
|
An out-edge of a discovered vertex is examined |
|
An edge becomes part of the search tree |
|
An edge leads to an already-discovered vertex |
|
(DFS only) An edge leads to an already-finished vertex |
|
All out-edges of a vertex have been examined |
|
(Shortest paths) A shorter path to a vertex is found |
|
(Shortest paths) An edge does not improve the current distance |
|
(Bellman-Ford) An edge’s distance is minimized in this pass |
|
(Bellman-Ford) An edge’s distance was not improved in this pass |