├── LICENSE.md ├── README.md ├── dep_sort.h ├── dep_sort.inl └── dep_sort_stl.h /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Dale Weiler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dependency sorting 2 | 3 | Generic topological sorting for sorting a list of dependencies in C++17 4 | 5 | # Use 6 | There are two interfaces to choose from: `dep_sort_stl` and `dep_sort`. 7 | 8 | The first will use `std::vector`, `std::unordered_set` and `std::unordered_map` 9 | to implement a highly efficent `O(V+E)` dependency resolver container. 10 | 11 | There's also however a `dep_sort` class which lets you supplement your 12 | own `vector`, `set` and `map` implementions. You can use drop in 13 | replacements like Google's `densehash` or `sparsehash` and stuff like 14 | Boost's `multimap` if working with highly dense or sparse data. 15 | 16 | As a result this code has no real dependencies other than a working C++17 17 | compiler. 18 | 19 | # Example 20 | ```cpp 21 | int main() { 22 | dep_sort_stl dep; 23 | dep.add_node("a"); 24 | dep.add_node("b"); 25 | dep.add_node("c"); 26 | dep.add_node("d"); 27 | std::vector as = { "b", "c" }; 28 | std::vector bs = { "c" }; 29 | std::vector cs = { "d" }; 30 | dep.add_dependencies("a", as); 31 | dep.add_dependencies("b", bs); 32 | dep.add_dependencies("c", cs); 33 | const auto& result = dep.sort(); 34 | if (!result.has_cycles()) { 35 | // print the sorted list 36 | for (const auto& value : result.sorted) { 37 | std::cout << value << std::endl; 38 | } 39 | } else { 40 | // print nodes that could not be sorted due to cycles 41 | for (const auto& value : result.unsorted) { 42 | std::cout << value << std::endl; 43 | } 44 | } 45 | } 46 | ``` 47 | 48 | # Documentation 49 | 50 | The interfaces are documented in `dep_sort.h` 51 | -------------------------------------------------------------------------------- /dep_sort.h: -------------------------------------------------------------------------------- 1 | #ifndef DEP_SORT_H 2 | #define DEP_SORT_H 3 | 4 | namespace detail { 5 | typedef decltype(sizeof 0) dep_size_type; 6 | template typename S> 7 | struct dep_relation { 8 | dep_size_type dependencies_; 9 | S dependents_; 10 | }; 11 | } 12 | 13 | template typename A> 14 | struct dep_sort_result { 15 | typedef T value_type; 16 | typedef A array_type; 17 | 18 | // check if sort contains any cycles, returns true if |unsorted| contains 19 | // any elements 20 | bool has_cycles() const; 21 | 22 | // list of sorted nodes that are in dependency order 23 | array_type sorted; 24 | 25 | // list of unsorted nodes that could not be sorted due to cycles 26 | array_type unsorted; 27 | }; 28 | 29 | template typename A, template typename S, template typename M> 30 | struct dep_sort { 31 | typedef T value_type; 32 | typedef dep_sort_result result_type; 33 | 34 | // add |node| to sort, returns false if |node| already exists 35 | bool add_node(const value_type& node); 36 | 37 | // add |dependency| to |node|, returns false if |node| and |dependency| 38 | // are the same or if |dependency| is already a dependency of |node| 39 | bool add_dependency(const value_type& node, const value_type& dependency); 40 | 41 | // add |dependencies| to |node|, returns false if |node| is the same 42 | // as any of the dependencies in |dependencies| or if any of the 43 | // dependencies are already a dependencey of |node| 44 | template typename C> 45 | bool add_dependencies(const value_type& node, const C& dependencies); 46 | 47 | // clear all nodes and dependencies 48 | void clear(); 49 | 50 | // sort the nodes and their dependencies 51 | result_type sort() const; 52 | 53 | // check if |node| exists, returns false if |node| does not exist 54 | bool has_node(const value_type& node) const; 55 | 56 | // check if |dependency| exists for |node|, returns false if |node| or 57 | // |dependency| do not exist 58 | bool has_dependency(const value_type& node, const value_type& dependency) const; 59 | 60 | private: 61 | void sort(result_type& result); 62 | 63 | typedef M> map_type; 64 | map_type map_; 65 | }; 66 | 67 | #include "dep_sort.inl" 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /dep_sort.inl: -------------------------------------------------------------------------------- 1 | template typename A> 2 | inline bool dep_sort_result::has_cycles() const { 3 | return !unsorted.empty(); 4 | } 5 | 6 | template typename A, template typename S, template typename M> 7 | inline bool dep_sort::add_node(const T& node) { 8 | if (has_node(node)) { 9 | return false; 10 | } 11 | map_.insert({node, {}}); 12 | return true; 13 | } 14 | 15 | template typename A, template typename S, template typename M> 16 | inline bool dep_sort::add_dependency(const T& node, const T& dependency) { 17 | if (dependency == node) { 18 | return false; 19 | } 20 | auto& dependents = map_[dependency].dependents_; 21 | if (dependents.find(node) == dependents.end()) { 22 | dependents.insert(node); 23 | ++map_[node].dependencies_; 24 | return true; 25 | } 26 | return false; 27 | } 28 | 29 | template typename A, template typename S, template typename M> 30 | template typename C> 31 | inline bool dep_sort::add_dependencies(const T& node, const C& dependencies) { 32 | for (const auto& dependency : dependencies) { 33 | if (!add_dependency(node, dependency)) { 34 | return false; 35 | } 36 | } 37 | return true; 38 | } 39 | 40 | template typename A, template typename S, template typename M> 41 | inline void dep_sort::clear() { 42 | map_.clear(); 43 | } 44 | 45 | template typename A, template typename S, template typename M> 46 | inline typename dep_sort::result_type dep_sort::sort() const { 47 | dep_sort copy(*this); 48 | dep_sort_result result; 49 | copy.sort(result); 50 | return result; 51 | } 52 | 53 | template typename A, template typename S, template typename M> 54 | inline bool dep_sort::has_node(const value_type& node) const { 55 | return map_.find(node) != map_.end(); 56 | } 57 | 58 | template typename A, template typename S, template typename M> 59 | inline bool dep_sort::has_dependency(const value_type& node, const value_type& dependency) const { 60 | if (has_node(node)) { 61 | const auto& dependencies = map_[node]; 62 | return dependencies.find(dependency) != dependencies.end(); 63 | } 64 | return false; 65 | } 66 | 67 | template typename A, template typename S, template typename M> 68 | inline void dep_sort::sort(result_type& result) { 69 | auto& sorted = result.sorted; 70 | auto& unsorted = result.unsorted; 71 | for (const auto& [node, relations] : map_) { 72 | if (relations.dependencies_ == 0) { 73 | sorted.push_back(node); 74 | } 75 | } 76 | for (detail::dep_size_type i = 0; i < sorted.size(); i++) { 77 | for (const auto& node : map_[sorted[i]].dependents_) { 78 | if (--map_[node].dependencies_ == 0) { 79 | sorted.push_back(node); 80 | } 81 | } 82 | } 83 | for (const auto& [node, relations] : map_) { 84 | if (relations.dependencies_ != 0) { 85 | unsorted.push_back(node); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /dep_sort_stl.h: -------------------------------------------------------------------------------- 1 | #ifndef DEP_SORT_STL_H 2 | #define DEP_SORT_STL_H 3 | 4 | #include "dep_sort.h" 5 | 6 | // template alias dep_sort around std::vector/std::unordered_set/std::unordered_map 7 | #include 8 | #include 9 | #include 10 | 11 | template 12 | using dep_sort_stl = dep_sort; 13 | 14 | #endif 15 | --------------------------------------------------------------------------------