Aeon Engine c550894
AeonGames Open Source Game Engine
Loading...
Searching...
No Matches
DependencyMap.hpp
1/*
2Copyright (C) 2018,2019,2025,2026 Rodrigo Jose Hernandez Cordoba
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16#ifndef AEONGAMES_DEPENDENCY_MAP_H
17#define AEONGAMES_DEPENDENCY_MAP_H
18#include <vector>
19#include <unordered_map>
20#include <tuple>
21#include <algorithm>
22#include <iterator>
23
24namespace AeonGames
25{
34 template <
35 class Key,
36 class T = Key,
37 class Hash = std::hash<Key>,
38 class KeyEqual = std::equal_to<Key>,
39 class MapAllocator = std::allocator< std::pair<const Key, typename std::tuple <
40 Key,
41 size_t,
42 std::vector<Key, std::allocator<Key >>,
43 T
44 >>>,
45 class VectorAllocator = std::allocator<Key>
46 >
48 {
49 using GraphNode = typename std::tuple <
50 Key, //-> Parent Iterator
51 size_t, //-> Child Iterator
52 std::vector<Key, VectorAllocator>, //-> Dependencies
53 T //-> Payload
54 >;
55
56 public:
58 using triple = typename std::tuple<Key, std::vector<Key>, T>;
59
61 class iterator
62 {
63 typename std::vector<Key, VectorAllocator>::iterator mIterator{};
64 typename std::unordered_map <
65 Key,
66 GraphNode,
67 Hash,
68 KeyEqual,
69 MapAllocator
70 > * mGraph{};
71 friend class DependencyMap<Key, T, Hash, KeyEqual, MapAllocator, VectorAllocator>;
72 iterator (
73 const typename std::vector<Key, VectorAllocator>::iterator & aIterator,
74 std::unordered_map <
75 Key,
76 GraphNode,
77 Hash,
78 KeyEqual,
79 MapAllocator
80 > * aGraph ) :
81 mIterator ( aIterator ),
82 mGraph ( aGraph ) {}
83 public:
84 using difference_type = typename std::vector<Key, VectorAllocator>::iterator::difference_type;
85 using value_type = typename std::vector<Key, VectorAllocator>::iterator::value_type;
86 using reference = T &;
87 using pointer = T *;
88 using iterator_category = std::bidirectional_iterator_tag;
89 iterator() = default;
91 iterator ( const iterator& ) = default;
92 ~iterator() = default;
94 iterator& operator= ( const iterator& ) = default;
96 bool operator== ( const iterator & i ) const
97 {
98 return mIterator == i.mIterator;
99 }
100
101 bool operator!= ( const iterator & i ) const
102 {
103 return mIterator != i.mIterator;
104 }
105
106 iterator & operator++()
107 {
108 mIterator++;
109 return *this;
110 }
111
112 iterator & operator--()
113 {
114 mIterator--;
115 return *this;
116 }
117
119 {
120 return std::get<3> ( mGraph->at ( *mIterator ) );
121 }
122
124 {
125 return &std::get<3> ( mGraph->at ( *mIterator ) );
126 };
127
129 const Key & GetKey() const
130 {
131 return *mIterator;
132 };
133
135 const std::vector<Key, VectorAllocator>& GetDependencies() const
136 {
137 return std::get<2> ( mGraph->at ( *mIterator ) );
138 };
139 };
140
142 class const_iterator
143 {
144 typename std::vector<Key, VectorAllocator>::const_iterator mIterator{};
145 const typename std::unordered_map <
146 Key,
147 GraphNode,
148 Hash,
149 KeyEqual,
150 MapAllocator
151 > * mGraph{};
152 friend class DependencyMap<Key, T, Hash, KeyEqual, MapAllocator, VectorAllocator>;
153 const_iterator (
154 const typename std::vector<Key, VectorAllocator>::const_iterator & aIterator,
155 const std::unordered_map <
156 Key,
157 GraphNode,
158 Hash,
159 KeyEqual,
160 MapAllocator
161 > * aGraph ) :
162 mIterator ( aIterator ),
163 mGraph ( aGraph ) {}
164 public:
165 using difference_type = typename std::vector<Key, VectorAllocator>::const_iterator::difference_type;
166 using value_type = typename std::vector<Key, VectorAllocator>::const_iterator::value_type;
167 using reference = const T &;
168 using pointer = const T *;
169 using iterator_category = std::bidirectional_iterator_tag;
170 const_iterator() = default;
172 const_iterator ( const const_iterator& ) = default;
173 ~const_iterator() = default;
175 const_iterator& operator= ( const const_iterator& ) = default;
177 bool operator== ( const const_iterator & i ) const
178 {
179 return mIterator == i.mIterator;
180 }
181
182 bool operator!= ( const const_iterator & i ) const
183 {
184 return mIterator != i.mIterator;
185 }
186
187 const_iterator & operator++()
188 {
189 mIterator++;
190 return *this;
191 }
192
193 const_iterator & operator--()
194 {
195 mIterator--;
196 return *this;
197 }
198
200 {
201 return std::get<3> ( mGraph->at ( *mIterator ) );
202 }
203
205 {
206 return &std::get<3> ( mGraph->at ( *mIterator ) );
207 };
208
210 const Key & GetKey() const
211 {
212 return *mIterator;
213 };
214
216 const std::vector<Key, VectorAllocator>& GetDependencies() const
217 {
218 return std::get<2> ( mGraph->at ( *mIterator ) );
219 };
220 };
221
224 void Reserve ( size_t count )
225 {
226 graph.reserve ( count );
227 sorted.reserve ( count );
228 }
229
231 DependencyMap() = default;
233 ~DependencyMap() = default;
237 DependencyMap ( std::initializer_list<triple> aList )
238 {
239 Reserve ( aList.size() );
240 for ( auto& i : aList )
241 {
242 graph[std::get<0> ( i )] = GraphNode{{}, 0, std::get<1> ( i ), std::get<2> ( i ) };
243 }
244 for ( auto& i : graph )
245 {
246 // Only process the current child if it is not yet in the sorted vector.
247 if ( std::find ( sorted.begin(), sorted.end(), i.first ) == sorted.end() )
248 {
249 // Set initial node to the first valid child.
250 Key node = i.first;
251 // Set Parent node to itself
252 std::get<0> ( graph[i.first] ) = i.first;
253 // Non Recursive Depth First Search.
254 do
255 {
256 if ( std::get<1> ( graph[node] ) < std::get<2> ( graph[node] ).size() )
257 {
258 if ( std::get<2> ( graph[node] ) [std::get<1> ( graph[node] )] == i.first )
259 {
260 throw std::runtime_error ( "One of the provided nodes creates a circular dependency." );
261 }
262 // We get here if the current node still has further children to process
263 auto next = graph.find ( std::get<2> ( graph[node] ) [std::get<1> ( graph[node] )] );
264 ++std::get<1> ( graph[node] );
265 // Skip not (yet) existing nodes and nodes already inserted.
266 if ( next != graph.end() && std::find ( sorted.begin(), sorted.end(), ( *next ).first ) == sorted.end() )
267 {
268 std::get<0> ( ( *next ).second ) = node;
269 node = ( *next ).first;
270 }
271 }
272 else
273 {
274 // We get here if the current node has no further children to process
275 sorted.push_back ( node );
276 std::get<1> ( graph[node] ) = 0; // Reset counter for next traversal.
277 // Go back to the parent
278 node = std::get<0> ( graph[node] );
279 }
280 }
281 while ( node != std::get<0> ( graph[node] ) );
282 sorted.push_back ( node );
283 }
284 }
285 }
286
291 size_t Insert ( const triple& item )
292 {
293 // If the map is empty just insert the new node.
294 if ( sorted.empty() && graph.empty() )
295 {
296 graph[std::get<0> ( item )] = GraphNode{{}, 0, std::get<1> ( item ), std::get<2> ( item ) };
297 sorted.emplace_back ( std::get<0> ( item ) );
298 return 0;
299 }
300 // We'll move all dependencies and the new node to the start of the sorted vector.
301 auto insertion_cursor = sorted.begin();
302 bool circular_dependency = false;
303 // Iterate the new node's children to bring all dependencies to the front of the sorted vector.
304 for ( auto& i : std::get<1> ( item ) )
305 {
306 // Only process the current child if 1-it is already in the map and 2-is to the right of the insertion cursor
307 if ( graph.find ( i ) != graph.end() && std::find ( sorted.begin(), insertion_cursor, i ) == insertion_cursor )
308 {
309 // Set initial node to the current child.
310 Key node = i;
311 // Set Parent node
312 std::get<0> ( graph[i] ) = std::get<0> ( item );
313 // Non Recursive Depth First Search.
314 while ( node != std::get<0> ( item ) )
315 {
316 if ( std::get<1> ( graph[node] ) < std::get<2> ( graph[node] ).size() )
317 {
318 if ( std::get<2> ( graph[node] ) [std::get<1> ( graph[node] )] == std::get<0> ( item ) )
319 {
320 /*
321 If we get here, the new node would create a circular dependency,
322 so we must NOT insert it, if we break here however,
323 the instance would be left in an unusable state,
324 so record the circular dependency and let the proccess finish.
325 Later we'll just NOT insert the node but throw an exception.
326 */
327 circular_dependency = true;
328 }
329 // We get here if the current node still has further children to process
330 auto next = graph.find ( std::get<2> ( graph[node] ) [std::get<1> ( graph[node] )] );
331 ++std::get<1> ( graph[node] );
332 // Skip not (yet) existing nodes and nodes to the left of the insertion cursor.
333 if ( next != graph.end() && std::find ( sorted.begin(), insertion_cursor, ( *next ).first ) == insertion_cursor )
334 {
335 std::get<0> ( ( *next ).second ) = node;
336 node = ( *next ).first;
337 }
338 }
339 else
340 {
341 auto node_iterator = std::find ( sorted.begin(), sorted.end(), node );
342 std::rotate ( insertion_cursor++, node_iterator, node_iterator + 1 );
343 std::get<1> ( graph[node] ) = 0; // Reset counter for next traversal.
344 // Go back to the parent
345 node = std::get<0> ( graph[node] );
346 }
347 }
348 }
349 }
350 if ( !circular_dependency )
351 {
352 // Insert NEW node
353 graph[std::get<0> ( item )] = GraphNode{{}, 0, std::get<1> ( item ), std::get<2> ( item ) };
354 return sorted.insert ( insertion_cursor, std::get<0> ( item ) ) - sorted.begin();
355 }
356 else
357 {
358 throw std::runtime_error ( "New node would create a circular dependency." );
359 }
360 }
361
364 void Erase ( const Key& key )
365 {
366 graph.erase ( key );
367 sorted.erase ( std::remove ( sorted.begin(), sorted.end(), key ), sorted.end() );
368 }
369
374 const T& operator[] ( const std::size_t index ) const
375 {
376 if ( index >= sorted.size() )
377 {
378 throw std::out_of_range ( "Index out of range." );
379 }
380 return std::get<3> ( graph.at ( sorted[index] ) );
381 }
382
387 T& operator[] ( const std::size_t index )
388 {
389 return const_cast<T&> ( static_cast<const DependencyMap<Key, T, Hash, KeyEqual, MapAllocator, VectorAllocator>*> ( this )->operator[] ( index ) );
390 }
391
394 const std::size_t Size() const
395 {
396 return sorted.size();
397 }
398
402 const_iterator Find ( const Key& key ) const
403 {
404 return const_iterator
405 {
406 std::find ( sorted.begin(), sorted.end(), key ),
407 &graph};
408 }
409
413 iterator Find ( const Key& key )
414 {
415 return iterator
416 {
417 std::find ( sorted.begin(), sorted.end(), key ),
418 &graph};
419 }
420
424 {
425 return iterator{sorted.begin(), &graph};
426 }
427
430 {
431 return iterator{sorted.end(), &graph};
432 }
433
437 {
438 return const_iterator{sorted.begin(), &graph};
439 }
440
444 {
445 return const_iterator{sorted.end(), &graph};
446 }
447 private:
448 std::unordered_map
449 <
450 Key,
451 GraphNode,
452 Hash,
453 KeyEqual,
454 MapAllocator
455 > graph;
456 std::vector<Key, VectorAllocator> sorted;
457 };
458}
459#endif
Const bidirectional iterator over topologically sorted entries.
typename std::vector< Key, VectorAllocator >::const_iterator::difference_type difference_type
Iterator difference type.
const_iterator & operator--()
Pre-decrement operator.
const_iterator & operator=(const const_iterator &)=default
Copy assignment operator.
bool operator!=(const const_iterator &i) const
Inequality comparison operator.
const T & reference
Const reference to the mapped value type.
std::bidirectional_iterator_tag iterator_category
Bidirectional iterator category.
const std::vector< Key, VectorAllocator > & GetDependencies() const
Get the dependency list of the element this iterator points to.
const_iterator(const const_iterator &)=default
Copy constructor.
const T * pointer
Const pointer to the mapped value type.
typename std::vector< Key, VectorAllocator >::const_iterator::value_type value_type
Iterator value type.
const Key & GetKey() const
Get the key of the element this iterator points to.
reference operator*() const
Dereference operator.
const_iterator & operator++()
Pre-increment operator.
bool operator==(const const_iterator &i) const
Equality comparison operator.
pointer operator->() const
Member access operator.
Mutable bidirectional iterator over topologically sorted entries.
const std::vector< Key, VectorAllocator > & GetDependencies() const
Get the dependency list of the element this iterator points to.
iterator(const iterator &)=default
Copy constructor.
typename std::vector< Key, VectorAllocator >::iterator::difference_type difference_type
Iterator difference type.
std::bidirectional_iterator_tag iterator_category
Bidirectional iterator category.
T * pointer
Pointer to the mapped value type.
iterator & operator++()
Pre-increment operator.
const Key & GetKey() const
Get the key of the element this iterator points to.
reference operator*() const
Dereference operator.
iterator & operator--()
Pre-decrement operator.
bool operator==(const iterator &i) const
Equality comparison operator.
typename std::vector< Key, VectorAllocator >::iterator::value_type value_type
Iterator value type.
pointer operator->() const
Member access operator.
bool operator!=(const iterator &i) const
Inequality comparison operator.
iterator & operator=(const iterator &)=default
Copy assignment operator.
T & reference
Reference to the mapped value type.
const_iterator begin() const
Get a const iterator to the first element in sorted order.
void Reserve(size_t count)
Reserve storage for a given number of elements.
DependencyMap()=default
Default constructor.
void Erase(const Key &key)
Remove an element by key.
iterator Find(const Key &key)
Find an element by key (mutable).
const_iterator Find(const Key &key) const
Find an element by key (const).
iterator begin()
Get a mutable iterator to the first element in sorted order.
const std::size_t Size() const
Get the number of elements.
size_t Insert(const triple &item)
Insert a new element, maintaining topological order.
DependencyMap(std::initializer_list< triple > aList)
Construct from an initializer list, performing topological sort.
~DependencyMap()=default
Default destructor.
const_iterator end() const
Get a const iterator past the last element in sorted order.
const T & operator[](const std::size_t index) const
Access element by sorted index (const).
iterator end()
Get a mutable iterator past the last element in sorted order.
typename std::tuple< Key, std::vector< Key >, T > triple
Convenience type alias for an insertion tuple: (key, dependencies, payload).
<- This is here just for the literals
Definition AABB.hpp:31