1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

lib: add dependency graph implementation

This commit is contained in:
daniel-k 2017-12-19 18:58:51 +01:00
parent e590d1a350
commit 35d96ed277
2 changed files with 162 additions and 0 deletions

View file

@ -0,0 +1,55 @@
#ifndef VILLAS_DEPENDENCY_GRAPH_HPP
#define VILLAS_DEPENDENCY_GRAPH_HPP
#include <list>
#include <map>
#include <algorithm>
#include "log.hpp"
namespace villas {
namespace utils {
template<typename T>
class DependencyGraph {
public:
using NodeList = std::list<T>;
/// Create a node without dependencies if it not yet exists, return if a new
/// node has been created.
bool addNode(const T& node);
/// Remove a node and all other nodes that depend on it
void removeNode(const T& node);
/// Add a dependency to a node. Will create the node if it not yet exists
void addDependency(const T& node, const T& dependency);
void dump();
/// Return a sequential evaluation order list. If a circular dependency has been
/// detected, all nodes involved will not be part of that list.
NodeList getEvaluationOrder() const;
private:
/// Return whether a node already exists or not
bool nodeExists(const T& node)
{ return graph.find(node) != graph.end(); }
static bool
nodeInList(const NodeList& list, const T& node)
{ return list.end() != std::find(list.begin(), list.end(), node); }
private:
using Graph = std::map<T, NodeList>;
Graph graph;
};
} // namespace utils
} // namespace villas
#include "dependency_graph_impl.hpp"
#endif // VILLAS_DEPENDENCY_GRAPH_HPP

View file

@ -0,0 +1,107 @@
#ifndef VILLAS_DEPENDENCY_GRAPH_HPP
#error "Do not include this file directly, please include depedency_graph.hpp"
#endif
#include "dependency_graph.hpp"
#include <sstream>
namespace villas {
namespace utils {
template<typename T>
bool
DependencyGraph<T>::addNode(const T &node)
{
bool existedBefore = nodeExists(node);
// accessing is enough to create if not exists
graph[node];
return existedBefore;
}
template<typename T>
void
DependencyGraph<T>::removeNode(const T &node)
{
graph.erase(node);
// check if other nodes depend on this one
for(auto& [key, dependencies] : graph) {
if(nodeInList(dependencies, node)) {
// remove other node that depends on the one to delete
removeNode(key);
}
}
}
template<typename T>
void
DependencyGraph<T>::addDependency(const T &node, const T &dependency)
{
NodeList& dependencies = graph[node];
if(not nodeInList(dependencies, dependency))
dependencies.push_back(dependency);
}
template<typename T>
void
DependencyGraph<T>::dump() {
for(auto& node : graph) {
std::stringstream ss;
for(auto& dep : node.second) {
ss << dep << " ";
}
cpp_debug << node.first << ": " << ss.str();
}
}
template<typename T>
typename DependencyGraph<T>::NodeList
DependencyGraph<T>::getEvaluationOrder() const
{
// copy graph to preserve information (we have to delete entries later)
Graph graph = this->graph;
// output list
NodeList out;
while(graph.size() > 0) {
int added = 0;
// look for nodes with no dependencies
for(auto& [key, dependencies] : graph) {
for(auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {
if(nodeInList(out, *dep)) {
// dependency has been pushed to list in last round
dep = dependencies.erase(dep);
}
}
// nodes with no dependencies can be pushed to list
if(dependencies.empty()) {
out.push_back(key);
graph.erase(key);
added++;
}
}
// if a round doesn't add any elements and is not the last, then
// there is a circular dependency
if(added == 0 and graph.size() > 0) {
cpp_error << "Circular dependency detected! IPs not available:";
Logger::Indenter indent = cpp_debug.indent();
for(auto& [key, value] : graph) {
cpp_error << key;
}
break;
}
}
return out;
}
} // namespace utils
} // namespace villas