Intrusive Containers
Loading...
Searching...
No Matches
ManyMany.hpp
Go to the documentation of this file.
1/**
2@file ManyMany.hpp
3@brief Intrusive Many to Many Relationship.
4
5This file defines a trio of templates (ManyManyLinkRoot, ManyManyLinkLink, and ManyManyLinkNode)
6that implement a many-to-many interconnect web.
7
8@warning The ManyMany classes are new, and have not been extensively tested
9@bug The Locking implemented in the ManyMany containers is likely not sufficient yet.
10
11@copyright (c) 2014-2024 Richard Damon
12@parblock
13MIT License:
14
15Permission is hereby granted, free of charge, to any person obtaining a copy
16of this software and associated documentation files (the "Software"), to deal
17in the Software without restriction, including without limitation the rights
18to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19copies of the Software, and to permit persons to whom the Software is
20furnished to do so, subject to the following conditions:
21
22The above copyright notice and this permission notice shall be included in
23all copies or substantial portions of the Software.
24
25THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31THE SOFTWARE.
32@endparblock
33
34@par Overview
35@parblock
36ListInRoot and ListInNode provide a simple API to allow classes to provide a
37basic List <-> Node relationship (1 List holding many Nodes), with a singly linked list.
38(c.f. DListin.h, DListInRoot, and DListInNode for doubly linked list)
39
40To create this relationship, the class to act as the list derives from the template ListInRoot,
41and the class to act as the Node from ListInNode, both with a first parameter of the class name
42of the List class, and a second parameter of the Node class. There is an optional integral 3rd parameter
43to allow the classes to inherent from these multiple times if you need to represent multiple simultaneous
44relationships. This inheritance should be public, or you need to add the ListInRoot and ListInNode templates as
45friends.
46
47At the point of declaration, both classes need to be declared, so you will typically have a forward of the other
48class before the definition of the class. At the point of usage of members, generally the full definition of both
49classes is needed to instance the code for the template.
50
51Inside this file, the following types have the following meanings:
52
53R: The "user" type that will be derived from ListInRoot
54
55N: The "user" type that will be derived from ListInNode
56
57n: The integer parameter for the template to allow multiple usage
58
59Root: The particular instance of ListInRoot<R, N, n> being used
60
61Node: The particular instance of ListInNode<R, N, n> being used
62
63node: will have type N*, to be used when walking the list.
64@endparblock
65
66@ingroup IntrusiveContainers
67*/
68
69
70#ifndef CONTAINERS_MANYMANY_HPP
71#define CONTAINERS_MANYMANY_HPP
72#include "ManyMany.h"
73
74
75template<class R, class N, ContainerThreadSafety s, int n, class L>
77 if (node) {
78 add(node, link);
79 }
80}
81
82template<class R, class N, ContainerThreadSafety s, int n, class L>
86
87/**
88 * Remove Node for list
89 * @tparam R
90 * @tparam N
91 * @tparam s
92 * @tparam n
93 * @tparam L
94 * @param node the node to add to this Root
95 * @return True if node was on list
96 */
97template<class R, class N, ContainerThreadSafety s, int n, class L>
98void ManyManyRoot<R, N, s, n, L>::add(N& node, L& link) {
99 link.Link::add(*static_cast<R*>(this), node);
100}
101
102template<class R, class N, ContainerThreadSafety s, int n, class L>
103void ManyManyRoot<R, N, s, n, L>::add(N& node, L* link) {
104 add(node, getLink(link));
105}
106
107template<class R, class N, ContainerThreadSafety s, int n, class L>
108void ManyManyRoot<R, N, s, n, L>::add(N* node, L& link) {
109 if (node) add(*node, link);
110}
111
112template<class R, class N, ContainerThreadSafety s, int n, class L>
113void ManyManyRoot<R, N, s, n, L>::add(N* node, L* link) {
114 if (node) add(*node, Link::getLink(link));
115}
116
117template<class R, class N, ContainerThreadSafety s, int n, class L>
119 // Link::add defaults to adding first
120 link.Link::add(*static_cast<R*>(this), node);
121}
122
123template<class R, class N, ContainerThreadSafety s, int n, class L>
125 addFirst(node, Link::getLink(link));
126}
127
128template<class R, class N, ContainerThreadSafety s, int n, class L>
130 if (node) addFirst(node, link);
131}
132
133template<class R, class N, ContainerThreadSafety s, int n, class L>
135 if (node) addFirst(*node, Link::getLink(link));
136}
137
138template<class R, class N, ContainerThreadSafety s, int n, class L>
140 // Get our last() pointer to add after
141 link.Link::add(static_cast<R*>(this), &node, last());
142}
143
144template<class R, class N, ContainerThreadSafety s, int n, class L>
146 addLast(node, Link::getLink(link));
147}
148
149template<class R, class N, ContainerThreadSafety s, int n, class L>
151 if (node) addLast(*node, link);
152}
153
154template<class R, class N, ContainerThreadSafety s, int n, class L>
156 if (node) addLast(*node, Link::getLink(link));
157}
158
159/**
160 * Remove Node for list
161 * @tparam R
162 * @tparam N
163 * @tparam L
164 * @tparam s
165 * @tparam n
166 * @param node the node to remove
167 * @return True if node was on list
168 */
169template<class R, class N, ContainerThreadSafety s, int n, class L>
171 for (Link* link = first(); link; link = link->nextNode()) {
172 if (link->node() == &node) {
173 link->remove();
174 return true;
175 }
176 }
177 return false; // didn't find the node;
178}
179
180/**
181 * Remove Node for list
182 * @tparam R
183 * @tparam N
184 * @tparam L
185 * @tparam s
186 * @tparam n
187 * @param node pointer to node on list, nullptr removes ALL nodes
188 * @return True if node was on list
189 */
190template<class R, class N, ContainerThreadSafety s, int n, class L>
192 if (node) {
193 return remove(*node);
194 } else {
195 while(first()) {
196 remove(first()->Link::node());
197 }
198 return true;
199 }
200}
201
202/*************************************************************************/
203
204template<class R, class N, ContainerThreadSafety s, int n, class L>
206 if (root) {
207 add(root, link);
208 }
209}
210
211template<class R, class N, ContainerThreadSafety s, int n, class L>
213 remove(nullptr);
214}
215
216template<class R, class N, ContainerThreadSafety s, int n, class L>
217void ManyManyNode<R, N, s, n, L>::add(R& root, L& link) {
218 link.Link::add(root, *static_cast<N*>(this));
219}
220
221template<class R, class N, ContainerThreadSafety s, int n, class L>
222void ManyManyNode<R, N, s, n, L>::add(R* root, L& link) {
223 if (root) add(*root, link);
224}
225
226template<class R, class N, ContainerThreadSafety s, int n, class L>
227void ManyManyNode<R, N, s, n, L>::add(R& root, L* link) {
228 add(root, Link::getLink(link));
229}
230
231template<class R, class N, ContainerThreadSafety s, int n, class L>
232void ManyManyNode<R, N, s, n, L>::add(R* root, L* link) {
233 if (root) add(*root, Link::getLink(link));
234}
235
236template<class R, class N, ContainerThreadSafety s, int n, class L>
238 // Link::add defaults to adding first
239 link.Link::add(root, *static_cast<N*>(this));
240}
241
242template<class R, class N, ContainerThreadSafety s, int n, class L>
244 if (root) addFirst(*root, link);
245}
246
247template<class R, class N, ContainerThreadSafety s, int n, class L>
249 addFirst(root, Link::getLink(link));
250}
251
252template<class R, class N, ContainerThreadSafety s, int n, class L>
254 if (root) addFirst(*root, Link::getLink(link));
255}
256
257
258template<class R, class N, ContainerThreadSafety s, int n, class L>
260 link.Link::add(&root, static_cast<N*>(this), last());
261}
262
263template<class R, class N, ContainerThreadSafety s, int n, class L>
265 if (root) addLast(*root, link);
266}
267
268template<class R, class N, ContainerThreadSafety s, int n, class L>
270 addLast(root, Link::getLink(link));
271}
272
273template<class R, class N, ContainerThreadSafety s, int n, class L>
275 if (root) addLast(*root, Link::getLink(link));
276}
277
278template<class R, class N, ContainerThreadSafety s, int n, class L>
280 for (Link* link=first(); link; link->nextRoot()) {
281 if (link->root() == &root) {
282 link->remove();
283 return true;
284 }
285 }
286 return false;
287}
288
289template<class R, class N, ContainerThreadSafety s, int n, class L>
291 if (root) {
292 return remove(*root);
293 }
294 // null root given, remove all
295 while(first()) {
296 first()->Link::remove();
297 }
298 return true;
299}
300
301/*************************************************************************/
302
303template<class R, class N, ContainerThreadSafety s, int n, class L>
304ManyManyLink<R, N, s, n, L>::ManyManyLink(R* root, N* node, L* link1, L* link2) {
305 if (root) {
306 add(root, node, link1, link2);
307 }
308}
309
310template<class R, class N, ContainerThreadSafety s, int n, class L>
312 m_dynamic = false; // Don't let remove try to delete us since we are going away anyway.
313 remove();
314}
315
316/**
317 * Convert a possible link pointer into an actual link object.
318 *
319 * If we are given a non-null pointer, us it, otherwise dynamically create the needed
320 * ManyManyLink object, and mark it to be deleted when removed.
321 */
322template<class R, class N, ContainerThreadSafety s, int n, class L>
324 if (link == nullptr) {
325 link = new L;
326 // TODO should do something on out of memory if throwing has been disabled
327 link->Link::m_dynamic = true;
328 }
329 return *link;
330}
331
332template<class R, class N, ContainerThreadSafety s, int n, class L>
334 L const* me = static_cast<L const*>(this);
335 bool flag = true;
336 Root* r = root();
337 if (root()) {
338 flag &= node() != nullptr;
339 if (!flag) return 0;
340 if (prevRoot()) {
341 flag &= (prevRoot()->Link::nextRoot() == me); // previous must point to us
342 flag &= (prevRoot()->Link::root() == root()); // previous must point to our same root
343 } else { // we are the first of the root chain
344 flag &= (r->first() == me);
345 }
346 if (nextRoot()) {
347 flag &= (nextRoot()->Link::prevRoot() == me);
348 flag &= (nextRoot()->Link::root() == root());
349 } else {
350 flag &= (r->last() == me);
351 }
352 if (prevNode()) {
353 flag &= (prevNode()->Link::nextNode() == me);
354 flag &= (prevNode()->Link::node() == node());
355 } else {
356 flag &= (node()->Node::first() == me);
357 }
358 if (nextNode()) {
359 flag &= (nextNode()->Link::prevNode() == me);
360 flag &= (nextNode()->Link::node() == node());
361 } else {
362 flag &= (node()-> Node::last() == me);
363 }
364 } else { // Link is disconnected
365 flag &= prevRoot() == nullptr;
366 flag &= nextRoot() == nullptr;
367 flag &= prevNode() == nullptr;
368 flag &= nextNode() == nullptr;
369 flag &= node() == nullptr;
370 }
371 return flag;
372}
373
374/**
375 * Link a Root to a Node with this Lisk
376 *
377 * @tparam R
378 * @tparam N
379 * @tparam L
380 * @tparam s
381 * @tparam n
382 * @param root
383 * @param node
384 * @param link1
385 * @param link2
386 *
387 * If link1 or link2 point to either the Root or Node we are to connect to, then we will link ourselves on that side
388 * just after that link. Otherwise we link at the front of the lists.
389 */
390template<class R, class N, ContainerThreadSafety s, int n, class L>
391void ManyManyLink<R, N, s, n, L>::add(R* root, N* node, L* link1, L* link2) {
392 L* me = static_cast<L*>(this);
393
394 // sanity checks, we can not add after ourselves
395 if (link1 == this) {
396 link1 = nullptr;
397 }
398 if (link2 == this) {
399 link2 = nullptr;
400 }
401
402 if (this->root()) {
403 // we are in use, this is an error, but best recovery is to disconnect us to be ready for the new use
404 // Do not let us be deleted as we get removed.
405 bool save = m_dynamic;
406 m_dynamic = false;
407 remove();
408 m_dynamic = save;
409 }
410
411 Link *rlink = nullptr, *nlink = nullptr;
412 unsigned saveRoot = BaseRoot::readLock(true);
413 unsigned saveNode = BaseNode::readLock(true);
414
415 if (link2) {
416 if (link2->Link::root() == root) rlink = link2;
417 if (link2->Link::node() == node) nlink = link2;
418 }
419 if (link1) {
420 if (link1->Link::root() == root) rlink = link1;
421 if (link1->Link::node() == node) nlink = link1;
422 }
423
424 if (rlink) {
425 rlink->BaseRoot::addAfter(me, true);
426 } else {
427 root->Root::Base::add(me);
428 }
429
430 if (nlink) {
431 nlink->BaseNode::addAfter(me, true);
432 } else {
433 node->Node::Base::add(me);
434 }
435 BaseRoot::readUnlock(saveRoot);
436 BaseNode::readUnlock(saveNode);
437}
438
439template<class R, class N, ContainerThreadSafety s, int n, class L>
440void ManyManyLink<R, N, s, n, L>::add(R& root, N& node) {
441 add(&root, &node);
442}
443
444template<class R, class N, ContainerThreadSafety s, int n, class L>
446 BaseRoot::remove();
447 BaseNode::remove();
448 if (m_dynamic) {
449 m_dynamic = false; // Don't repeat when called from destructor
450 delete this;
451 }
452}
453
454#endif
Intrusive Many to Many Relationship.
void addLast(R &root, L *link=nullptr)
Definition ManyMany.hpp:269
ManyManyNode(R *root=nullptr, L *link=nullptr)
Definition ManyMany.hpp:205
~ManyManyNode()
Definition ManyMany.hpp:212
void addFirst(R &root, L *link=nullptr)
Definition ManyMany.hpp:248
class ManyManyLink< R, N, s, n, L > Link
Definition ManyMany.h:233
bool remove(R &node)
Definition ManyMany.hpp:279
void add(R &root, L *link=nullptr)
Definition ManyMany.hpp:227
class ManyManyLink< R, N, s, n, L > Link
Definition ManyMany.h:174
ManyManyRoot(N *node=nullptr, L *link=nullptr)
Definition ManyMany.hpp:76
bool remove(N &node)
Remove Node for list.
Definition ManyMany.hpp:170
void add(N &node, L &link)
Remove Node for list.
Definition ManyMany.hpp:98
void addFirst(N &node, L &link)
Definition ManyMany.hpp:118
~ManyManyRoot()
Definition ManyMany.hpp:83
void addLast(N &node, L &link)
Definition ManyMany.hpp:139