27 #ifndef COM_BORA_SOFTWARE__BALAU_CONTAINER__DEPENDENCY_GRAPH 28 #define COM_BORA_SOFTWARE__BALAU_CONTAINER__DEPENDENCY_GRAPH 30 #include <Balau/Container/Impl/ContainerLogger.hpp> 33 #include <boost/graph/adjacency_list.hpp> 34 #include <boost/graph/depth_first_search.hpp> 35 #include <boost/graph/breadth_first_search.hpp> 36 #include <boost/graph/topological_sort.hpp> 37 #include <boost/graph/visitors.hpp> 42 #pragma clang diagnostic push 43 #pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" 87 template <
typename VertexT = boost::no_property,
typename EdgeT = boost::no_property,
typename GraphT = boost::no_property>
89 private:
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS, VertexT, EdgeT, GraphT>;
90 private:
using Vertex =
typename Graph::vertex_descriptor;
91 private:
using Edge =
typename Graph::edge_descriptor;
92 private:
using VertexIterator =
typename Graph::vertex_iterator;
93 private:
using InEdgeIterator =
typename Graph::in_edge_iterator;
94 private:
using OutEdgeIterator =
typename Graph::out_edge_iterator;
104 : vertexIter(copy.vertexIter) {}
110 vertexIter = copy.vertexIter;
135 return graph[*vertexIter];
142 return graph[*vertexIter];
149 return &graph[*vertexIter];
156 return &graph[*vertexIter];
163 return vertexIter == rhs.vertexIter;
170 return vertexIter != rhs.vertexIter;
175 private:
explicit iterator(Graph & graph_, VertexIterator vertexIter_)
177 , vertexIter(vertexIter_) {}
179 private: Graph & graph;
180 private: VertexIterator vertexIter;
191 : vertexIter(copy.vertexIter) {}
197 vertexIter = copy.vertexIter;
222 return graph[*vertexIter];
229 return &graph[*vertexIter];
236 return vertexIter == rhs.vertexIter;
243 return vertexIter != rhs.vertexIter;
248 private:
explicit const_iterator(
const Graph & graph_, VertexIterator vertexIter_)
250 , vertexIter(vertexIter_) {}
252 private:
const Graph & graph;
253 private: VertexIterator vertexIter;
268 public:
void addDependency(
const VertexT & dependency,
bool throwIfExists =
true) {
269 if (reverseLookup.find(dependency) != reverseLookup.end()) {
277 Vertex vertex = boost::add_vertex(graph);
278 reverseLookup[dependency] = vertex;
279 graph[vertex] = dependency;
297 auto itemIter = reverseLookup.find(dependency);
299 if (itemIter == reverseLookup.end()) {
300 if (throwIfNotExists) {
307 const auto vertex = itemIter->second;
308 boost::clear_vertex(vertex, graph);
309 boost::remove_vertex(vertex, graph);
310 rebuildReverseLookup();
318 public:
void addRelationship(
const VertexT & independent,
const VertexT & dependent) {
319 auto independentIter = reverseLookup.find(independent);
320 auto dependentIter = reverseLookup.find(dependent);
322 if (independentIter == reverseLookup.end()) {
326 if (dependentIter == reverseLookup.end()) {
330 boost::add_edge(independentIter->second, dependentIter->second, graph);
336 public:
void addRelationship(
const VertexT & independent,
const VertexT & dependent,
const EdgeT & edgeData) {
337 auto independentIter = reverseLookup.find(independent);
338 auto dependentIter = reverseLookup.find(dependent);
340 if (independentIter == reverseLookup.end()) {
344 if (dependentIter == reverseLookup.end()) {
348 boost::add_edge(independentIter->second, dependentIter->second, edgeData, graph);
355 auto independentIter = reverseLookup.find(independent);
356 auto dependentIter = reverseLookup.find(dependent);
358 if (independentIter == reverseLookup.end()) {
362 if (dependentIter == reverseLookup.end()) {
366 boost::remove_edge(independentIter->second, dependentIter->second, graph);
369 public: EdgeT getRelation(
const VertexT & independent,
const VertexT & dependent)
const {
370 auto independentIter = reverseLookup.find(independent);
371 auto dependentIter = reverseLookup.find(dependent);
373 if (independentIter == reverseLookup.end()) {
377 if (dependentIter == reverseLookup.end()) {
381 auto independentVertex = independentIter->second;
382 auto dependentVertex = dependentIter->second;
383 auto e = boost::edge(independentVertex, dependentVertex, graph);
386 _ThrowBalauException_generateStackTrace
388 __FILE__, __LINE__, st, independent, dependent,
"" 392 return graph[e.first];
399 return reverseLookup.find(dependency) != reverseLookup.end();
406 auto vertex = boost::vertex(reverseLookup.at(dependency), graph);
407 InEdgeIterator iter,
end;
408 std::vector<VertexT> ret;
410 for (boost::tie(iter, end) = boost::in_edges(vertex, graph); iter !=
end; ++iter) {
411 auto s = source(*iter, graph);
412 ret.emplace_back(graph[s]);
424 auto iter = reverseLookup.find(key);
426 if (iter != reverseLookup.end()) {
437 std::vector<VertexT> ret;
438 std::list<Vertex> depOrder;
439 boost::topological_sort(graph, std::front_inserter(depOrder));
441 for (
auto vertex : depOrder) {
442 ret.push_back(graph[vertex]);
455 std::list<Vertex> depOrder;
456 std::vector<Vertex> order(reverseLookup.size(), 0);
457 boost::topological_sort(graph, std::front_inserter(depOrder));
458 size_t levelCount = 0;
460 for (
auto vertex : depOrder) {
461 if (in_degree(vertex, graph) > 0) {
463 InEdgeIterator edgeIter, edgeEnd;
465 for (boost::tie(edgeIter, edgeEnd) = in_edges(vertex, graph); edgeIter != edgeEnd; ++edgeIter) {
466 auto t = order[source(*edgeIter, graph)];
473 order[vertex] = maximum + 1;
475 if (levelCount < order[vertex]) {
476 levelCount = order[vertex];
481 std::vector<std::vector<VertexT>> results(levelCount + 1, std::vector<VertexT>());
482 VertexIterator vertexIter, vertexEnd;
484 for (boost::tie(vertexIter, vertexEnd) = vertices(graph); vertexIter != vertexEnd; ++vertexIter) {
485 const auto position = order[*vertexIter];
486 results[position].emplace_back(graph[*vertexIter]);
496 std::vector<std::pair<VertexT, VertexT>> cycleEdges;
497 CycleDetector cycleDetector(cycleEdges);
498 boost::depth_first_search(graph, boost::visitor(cycleDetector));
499 return !cycleEdges.empty();
507 public:
bool hasCycles(std::vector<std::pair<VertexT, VertexT>> & cycleEdges)
const {
508 const auto sz = cycleEdges.size();
509 CycleDetector cycleDetector(cycleEdges);
510 boost::depth_first_search(graph, boost::visitor(cycleDetector));
511 return cycleEdges.size() > sz;
519 boost::tie(begin, end) = boost::vertices(graph);
528 boost::tie(begin, end) = boost::vertices(graph);
537 boost::tie(begin, end) = boost::vertices(graph);
546 boost::tie(begin, end) = boost::vertices(graph);
555 if (!Impl::ContainerLogger::log().enabled(level)) {
559 std::ostringstream builder;
561 builder << title <<
"\n";
563 VertexIterator vertexIter, vertexEnd;
564 InEdgeIterator edgeIter, edgeEnd;
568 const int indexLength = (int)
toString(reverseLookup.size()).length();
570 for (boost::tie(vertexIter, vertexEnd) = vertices(graph); vertexIter != vertexEnd; ++vertexIter) {
571 auto vertex = *vertexIter;
572 auto value = graph[vertex];
574 builder << std::right << std::setw(indexLength) << vertex <<
": " <<
toString(value) <<
"\n";
576 std::string_view prefix =
" deps: ";
577 for (boost::tie(edgeIter, edgeEnd) = in_edges(vertex, graph); edgeIter != edgeEnd; ++edgeIter) {
578 auto s = source(*edgeIter, graph);
579 builder << prefix << s;
583 if (prefix ==
", ") {
588 Impl::ContainerLogger::log().log(level, builder.str().c_str());
593 private:
struct CycleDetector :
public boost::dfs_visitor<> {
594 explicit CycleDetector(std::vector<std::pair<VertexT, VertexT>> & cycleEdges_) : cycleEdges(cycleEdges_) {}
596 template <
class Edge,
class Graph>
void back_edge(Edge e, Graph & g) {
597 VertexT s = g[source(e, g)];
598 VertexT t = g[target(e, g)];
599 cycleEdges.emplace_back(std::pair<VertexT, VertexT>(s, t));
602 private: std::vector<std::pair<VertexT, VertexT>> & cycleEdges;
607 private:
void rebuildReverseLookup() {
608 reverseLookup.clear();
609 VertexIterator vertexIter, vertexEnd;
611 for (boost::tie(vertexIter, vertexEnd) = vertices(graph); vertexIter != vertexEnd; ++vertexIter) {
612 auto vertex = *vertexIter;
613 auto value = graph[vertex];
615 reverseLookup.insert(std::pair<VertexT, Vertex>(value, vertex));
619 private: Graph graph;
620 private: std::unordered_map<VertexT, Vertex> reverseLookup;
625 #pragma clang diagnostic pop 627 #endif // COM_BORA_SOFTWARE__BALAU_CONTAINER_DEPENDENCY_GRAPH std::vector< VertexT > dependencyOrder() const
Calculate the dependency order of the dependencies.
Definition: DependencyGraph.hpp:436
iterator & operator=(const iterator ©)
Set the current iterator to a copy of the supplied iterator.
Definition: DependencyGraph.hpp:109
const_iterator(const const_iterator ©)
Create an iterator by copying the supplied iterator.
Definition: DependencyGraph.hpp:190
const VertexT * lookupDependency(const VertexT &key) const
Lookups up the dependency that matches the supplied candidate object.
Definition: DependencyGraph.hpp:423
#define ThrowBalauException(ExceptionClass,...)
Throw a Balau style exception, with implicit file and line number, and optional stacktrace.
Definition: BalauException.hpp:45
Thrown when a non-existent relationship between two items is requested.
Definition: ContainerExceptions.hpp:84
const_iterator begin() const
Get a const iterator positioned at the beginning of the dependency graph.
Definition: DependencyGraph.hpp:535
bool hasCycles() const
Does the dependency graph have any cycles?
Definition: DependencyGraph.hpp:495
iterator begin()
Get an iterator positioned at the beginning of the dependency graph.
Definition: DependencyGraph.hpp:517
Various container classes, apart from interprocess containers.
Definition: ArrayBlockingQueue.hpp:25
LoggingLevel
The logging level.
Definition: LoggingLevel.hpp:32
Thrown when an attempt is made to add an existing item to a container.
Definition: ContainerExceptions.hpp:62
std::vector< std::vector< VertexT > > parallelDependencyOrder() const
Calculate the parallel dependency order of the dependencies.
Definition: DependencyGraph.hpp:454
Balau exceptions for containers.
Balau::U8String< AllocatorT > toString(const BalauException &e)
Base class toString<AllocatorT> function for Balau exceptions.
Definition: BalauException.hpp:122
bool hasCycles(std::vector< std::pair< VertexT, VertexT >> &cycleEdges) const
Does the dependency graph have any cycles?
Definition: DependencyGraph.hpp:507
bool hasDependency(const VertexT &dependency) const
Does the graph have the specified dependency?
Definition: DependencyGraph.hpp:398
void logGraph(LoggingLevel level, const char *title)
Log the dependency tree to the "balau.container" logger if the logger is enabled for the supplied log...
Definition: DependencyGraph.hpp:554
iterator(const iterator ©)
Create an iterator by copying the supplied iterator.
Definition: DependencyGraph.hpp:103
std::vector< VertexT > directDependenciesOf(const VertexT &dependency) const
What are the direct dependencies of the specified dependency.
Definition: DependencyGraph.hpp:405
Thrown when an attempt is made to remove a non-existing item in a container.
Definition: ContainerExceptions.hpp:73
bool operator!=(const iterator &rhs) const
returns true if the current iterator is not equal to the supplied iterator.
Definition: DependencyGraph.hpp:169
void addRelationship(const VertexT &independent, const VertexT &dependent, const EdgeT &edgeData)
Add a dependency relationship between two existing dependencies, with edge data.
Definition: DependencyGraph.hpp:336
void addDependency(const VertexT &dependency, bool throwIfExists=true)
Add a dependency.
Definition: DependencyGraph.hpp:268
iterator end()
Get an iterator positioned at the end of the dependency graph.
Definition: DependencyGraph.hpp:526
void removeRelationship(const VertexT &independent, const VertexT &dependent)
Remove a dependency relationship between two existing dependencies.
Definition: DependencyGraph.hpp:354
VertexT & operator->()
Dereference the iterator in order to obtain the object pointed to.
Definition: DependencyGraph.hpp:148
A graph that models the dependencies between a set of objects of type VertexT.
Definition: DependencyGraph.hpp:88
The DependencyGraph const iterator.
Definition: DependencyGraph.hpp:186
void addRelationship(const VertexT &independent, const VertexT &dependent)
Add a dependency relationship between two existing dependencies.
Definition: DependencyGraph.hpp:318
iterator & operator++()
Increment the iterator.
Definition: DependencyGraph.hpp:126
The DependencyGraph non-const iterator.
Definition: DependencyGraph.hpp:99
VertexT & operator*()
Dereference the iterator in order to obtain the object pointed to.
Definition: DependencyGraph.hpp:134
Balau::U8String< AllocatorT > toString(LoggingLevel level)
Print the logging level as a UTF-8 string.
Definition: LoggingLevel.hpp:73
bool operator==(const iterator &rhs) const
returns true if the current iterator is equal to the supplied iterator.
Definition: DependencyGraph.hpp:162
void removeDependency(const VertexT &dependency, bool throwIfNotExists=true)
Remove a dependency.
Definition: DependencyGraph.hpp:294
const_iterator end() const
Get a const iterator positioned at the end of the dependency graph.
Definition: DependencyGraph.hpp:544