C++ class templates for graph construction and search
The libgraph search algorithms have been consolidated using a modern strategy pattern approach, eliminating code duplication and providing a unified framework for all search algorithms.
dijkstra.hpp
- Original implementationdijkstra_threadsafe.hpp
- Thread-safe versionastar.hpp
- Original implementationastar_threadsafe.hpp
- Thread-safe versiondijkstra.hpp
- Single consolidated implementationastar.hpp
- Single consolidated implementationbfs.hpp
- New algorithm (demonstrates extensibility)search_algorithm.hpp
and strategy implementationsExisting code continues to work without changes:
// All these continue to work exactly as before
auto path = Dijkstra::Search(graph, start, goal);
auto path = AStar::Search(graph, start, goal, heuristic);
auto path = DijkstraThreadSafe::Search(graph, context, start, goal);
auto path = AStarThreadSafe::Search(graph, context, start, goal, heuristic);
The new implementation provides thread safety when using SearchContext
:
// Thread-safe (recommended for concurrent usage)
SearchContext<State, Transition, StateIndexer> context;
auto path = Dijkstra::Search(graph, context, start, goal);
// Legacy mode (backward compatible, but not thread-safe)
auto path = Dijkstra::Search(graph, start, goal);
Adding a new search algorithm now requires only a strategy implementation:
// Example: BFS strategy (see bfs_strategy.hpp)
template<typename State, typename Transition, typename StateIndexer>
class BfsStrategy : public SearchStrategy<BfsStrategy<...>, State, Transition, StateIndexer> {
CostType GetPriorityImpl(const SearchInfo& info) const noexcept {
return info.g_cost; // FIFO behavior
}
bool RelaxVertexImpl(...) const {
// BFS-specific logic
}
};
SearchContext
SearchAlgorithm<Strategy> (search_algorithm.hpp)
├── Common search loop logic
├── Priority queue management
├── Path reconstruction
└── Uses Strategy for:
├── Priority calculation
├── Vertex initialization
├── Edge relaxation
└── Goal checking
Concrete Strategies:
├── DijkstraStrategy (dijkstra_strategy.hpp)
├── AStarStrategy (astar_strategy.hpp)
└── BfsStrategy (bfs_strategy.hpp)
include/graph/search/
├── search_strategy.hpp # Base strategy interface (CRTP)
├── search_algorithm.hpp # Unified search template
├── search_context.hpp # Thread-safe search state + Path type alias
├── dijkstra.hpp # Dijkstra strategy + public API (consolidated)
├── astar.hpp # A* strategy + public API (consolidated)
└── bfs.hpp # BFS strategy + public API (consolidated)
Note: Each algorithm file now contains both the strategy implementation and public API in a single consolidated file, eliminating the previous dual-file approach.
If you want to implement custom search algorithms, use the strategy pattern:
template<typename State, typename Transition, typename StateIndexer>
class CustomStrategy : public SearchStrategy<CustomStrategy<...>, State, Transition, StateIndexer> {
public:
CostType GetPriorityImpl(const SearchInfo& info) const noexcept {
// Return priority for open list ordering
return info.f_cost;
}
void InitializeVertexImpl(SearchInfo& info, vertex_iterator vertex,
vertex_iterator goal_vertex) const {
// Initialize search information for starting vertex
info.g_cost = 0.0;
info.h_cost = CalculateHeuristic(vertex, goal_vertex);
info.f_cost = info.g_cost + info.h_cost;
}
bool RelaxVertexImpl(SearchInfo& current_info, SearchInfo& successor_info,
vertex_iterator successor_vertex, vertex_iterator goal_vertex,
CostType edge_cost) const {
// Return true if successor was improved
CostType new_cost = current_info.g_cost + edge_cost;
if (new_cost < successor_info.g_cost) {
successor_info.g_cost = new_cost;
successor_info.h_cost = CalculateHeuristic(successor_vertex, goal_vertex);
successor_info.f_cost = successor_info.g_cost + successor_info.h_cost;
return true;
}
return false;
}
};
// Pattern 1: Single search
SearchContext<State, Transition, StateIndexer> context;
auto path = Dijkstra::Search(graph, context, start, goal);
// Pattern 2: Multiple searches on same graph
std::thread t1([&]() {
SearchContext<State, Transition, StateIndexer> context1;
auto path1 = Dijkstra::Search(graph, context1, start1, goal1);
});
std::thread t2([&]() {
SearchContext<State, Transition, StateIndexer> context2;
auto path2 = AStar::Search(graph, context2, start2, goal2, heuristic);
});
The new framework enables easy addition of:
If you encounter build issues, ensure you’re including the correct headers:
// New consolidated headers
#include "graph/search/dijkstra.hpp"
#include "graph/search/astar.hpp"
#include "graph/search/bfs.hpp"
// Not needed anymore (aliased automatically)
// #include "graph/search/dijkstra_threadsafe.hpp"
// #include "graph/search/astar_threadsafe.hpp"
If you encounter template deduction issues, use explicit template parameters:
auto path = Dijkstra::Search<MyState, double, MyIndexer>(graph, context, start, goal);
The new search framework provides:
The consolidation is complete and all existing functionality is preserved while providing a much cleaner, more maintainable architecture.