Refactor CycleDetector to be generic across graphs

This commit is contained in:
Oliver Hamlet
2018-10-20 12:48:24 +01:00
parent e06ce7efee
commit 9a0b492cb0
3 changed files with 49 additions and 84 deletions
+1 -42
View File
@@ -71,47 +71,6 @@ typedef boost::adjacency_list<boost::vecS,
typedef boost::graph_traits<GroupGraph>::vertex_descriptor vertex_t;
typedef boost::graph_traits<GroupGraph>::edge_descriptor edge_t;
class CycleDetector : public boost::dfs_visitor<> {
public:
void tree_edge(edge_t edge, const GroupGraph& graph) {
auto source = boost::source(edge, graph);
auto vertex = Vertex(graph[source].GetName(), graph[edge]);
// Check if the plugin already exists in the recorded trail.
auto it = find_if(begin(trail), end(trail), [&](const Vertex& v) {
return v.GetName() == graph[source].GetName();
});
if (it != end(trail)) {
// Erase everything from this position onwards, as it doesn't
// contribute to a forward-cycle.
trail.erase(it, end(trail));
}
trail.push_back(vertex);
}
void back_edge(edge_t edge, const GroupGraph& graph) {
auto source = boost::source(edge, graph);
auto target = boost::target(edge, graph);
auto vertex = Vertex(graph[source].GetName(), graph[edge]);
trail.push_back(vertex);
auto it = find_if(begin(trail), end(trail), [&](const Vertex& v) {
return v.GetName() == graph[target].GetName();
});
if (it != trail.end()) {
throw CyclicInteractionError(std::vector<Vertex>(it, trail.end()));
}
}
private:
std::vector<Vertex> trail;
};
class AfterGroupsVisitor : public boost::dfs_visitor<> {
public:
AfterGroupsVisitor(std::unordered_set<std::string>& visitedGroups) :
@@ -209,7 +168,7 @@ GetTransitiveAfterGroups(const std::unordered_set<Group>& masterlistGroups,
if (logger) {
logger->trace("Checking for cycles in the group graph");
}
boost::depth_first_search(graph, boost::visitor(CycleDetector()));
boost::depth_first_search(graph, boost::visitor(CycleDetector<GroupGraph>()));
std::unordered_map<std::string, std::unordered_set<std::string>>
transitiveAfterGroups;
+47
View File
@@ -28,7 +28,12 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
#include "loot/exception/cyclic_interaction_error.h"
#include "loot/metadata/group.h"
namespace loot {
@@ -36,5 +41,47 @@ namespace loot {
std::unordered_map<std::string, std::unordered_set<std::string>>
GetTransitiveAfterGroups(const std::unordered_set<Group>& masterlistGroups,
const std::unordered_set<Group>& userGroups);
template<typename G>
class CycleDetector : public boost::dfs_visitor<> {
public:
void tree_edge(typename boost::graph_traits<G>::edge_descriptor edge, const G& graph) {
auto source = boost::source(edge, graph);
auto vertex = Vertex(graph[source].GetName(), graph[edge]);
// Check if the vertex already exists in the recorded trail.
auto it = find_if(begin(trail), end(trail), [&](const Vertex& v) {
return v.GetName() == graph[source].GetName();
});
if (it != end(trail)) {
// Erase everything from this position onwards, as it doesn't
// contribute to a forward-cycle.
trail.erase(it, end(trail));
}
trail.push_back(vertex);
}
void back_edge(typename boost::graph_traits<G>::edge_descriptor edge, const G& graph) {
auto source = boost::source(edge, graph);
auto target = boost::target(edge, graph);
auto vertex = Vertex(graph[source].GetName(), graph[edge]);
trail.push_back(vertex);
auto it = find_if(begin(trail), end(trail), [&](const Vertex& v) {
return v.GetName() == graph[target].GetName();
});
if (it != trail.end()) {
throw CyclicInteractionError(std::vector<Vertex>(it, trail.end()));
}
}
private:
std::vector<Vertex> trail;
};
}
#endif
+1 -42
View File
@@ -49,47 +49,6 @@ typedef boost::graph_traits<PluginGraph>::vertex_iterator vertex_it;
typedef boost::graph_traits<PluginGraph>::edge_descriptor edge_t;
typedef boost::graph_traits<PluginGraph>::edge_iterator edge_it;
class CycleDetector : public boost::dfs_visitor<> {
public:
void tree_edge(edge_t edge, const PluginGraph& graph) {
const vertex_t source = boost::source(edge, graph);
auto vertex = Vertex(graph[source].GetName(), graph[edge]);
// Check if the plugin already exists in the recorded trail.
auto it = find_if(begin(trail), end(trail), [&](const Vertex& v) {
return v.GetName() == graph[source].GetName();
});
if (it != end(trail)) {
// Erase everything from this position onwards, as it doesn't
// contribute to a forward-cycle.
trail.erase(it, end(trail));
}
trail.push_back(vertex);
}
void back_edge(edge_t edge, const PluginGraph& graph) {
vertex_t source = boost::source(edge, graph);
vertex_t target = boost::target(edge, graph);
auto vertex = Vertex(graph[source].GetName(), graph[edge]);
trail.push_back(vertex);
auto it = find_if(begin(trail), end(trail), [&](const Vertex& v) {
return v.GetName() == graph[target].GetName();
});
if (it != trail.end()) {
throw CyclicInteractionError(std::vector<Vertex>(it, trail.end()));
}
}
private:
vector<Vertex> trail;
};
std::vector<std::string> PluginSorter::Sort(Game& game) {
logger_ = getLogger();
@@ -297,7 +256,7 @@ bool PluginSorter::GetVertexByName(const std::string& name,
void PluginSorter::CheckForCycles() const {
boost::depth_first_search(
graph_, visitor(CycleDetector()).vertex_index_map(vertexIndexMap_));
graph_, visitor(CycleDetector<PluginGraph>()).vertex_index_map(vertexIndexMap_));
}
bool PluginSorter::EdgeCreatesCycle(const vertex_t& fromVertex,