fix tree node destruction + reactivate all tests

Wed, 31 Dec 2025 13:39:55 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 31 Dec 2025 13:39:55 +0100
changeset 1689
a5b7cf49dea7
parent 1688
27073814f654
child 1690
7d41291b3095

fix tree node destruction + reactivate all tests

docs/Writerside/topics/collection.h.md file | annotate | diff | comparison | revisions
src/cx/collection.h file | annotate | diff | comparison | revisions
src/tree.c file | annotate | diff | comparison | revisions
tests/test_tree.c file | annotate | diff | comparison | revisions
--- a/docs/Writerside/topics/collection.h.md	Wed Dec 31 12:51:12 2025 +0100
+++ b/docs/Writerside/topics/collection.h.md	Wed Dec 31 13:39:55 2025 +0100
@@ -106,6 +106,7 @@
 
 // use in your collection's implementation
 cx_invoke_destructor(c, elem)
+cx_invoke_destructor_raw(c, elem)
   
 // the following two can be used to optimize loops
 cx_invoke_simple_destructor(c, elem) 
@@ -120,12 +121,13 @@
 is removed from your collection _without_ being returned to the caller.
 This macro will invoke a simple destructor, if one is assigned, first, and then the advanced destructor (again, if assigned).
 
-> Destructor functions are always invoked with a pointer to the element in your collection.
+> Destructor functions are invoked with a pointer to the element in your collection, unless you use `cx_invoke_destructor_raw()`.
 > If your collection is storing pointers (i.e. `cxCollectionStoresPointers()` returns `true`)
 > the `cx_invoke_destructor()` will make sure that the pointer to the element is dereferenced first,
 > so that the destructor functions are _always_ invoked with a pointer to the actual element.
 > 
 > This is different to how [comparator functions](#comparator-functions) work.
+> If you want the same behavior as for comparators, use `cx_invoke_destructor_raw()`.
 {style="note"}
 
 <seealso>
--- a/src/cx/collection.h	Wed Dec 31 12:51:12 2025 +0100
+++ b/src/cx/collection.h	Wed Dec 31 13:39:55 2025 +0100
@@ -312,4 +312,22 @@
     if ((c)->collection.simple_destructor) cx_invoke_simple_destructor(c,e); \
     if ((c)->collection.advanced_destructor) cx_invoke_advanced_destructor(c,e)
 
+/**
+ * Invokes all available destructor functions for a specific element.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * In contrast to cx_invoke_destructor(), this macro does not automatically
+ * dereference pointers to the elements when cxCollectionStoresPointers()
+ * returns true.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param e pointer to the element
+ */
+#define cx_invoke_destructor_raw(c, e) \
+    if ((c)->collection.simple_destructor) (c)->collection.simple_destructor(e); \
+    if ((c)->collection.advanced_destructor) (c)->collection.advanced_destructor((c)->collection.destructor_data, e)
+
+
 #endif // UCX_COLLECTION_H
--- a/src/tree.c	Wed Dec 31 12:51:12 2025 +0100
+++ b/src/tree.c	Wed Dec 31 13:39:55 2025 +0100
@@ -458,6 +458,7 @@
 ) {
     CxTreeIterator ret;
     ret.visit_on_exit = false;
+    ret.exiting = false;
     ret.use_dfs = false;
     ret.loc_children = loc_children;
     ret.loc_next = loc_next;
@@ -568,6 +569,8 @@
 }
 
 void *cxTreeAddData(CxTree *tree, void *parent, const void *data) {
+    if (tree->loc_data < 0) return NULL;
+
     void *node = cxZalloc(tree->collection.allocator, tree->node_size);
     if (node == NULL) return NULL; // LCOV_EXCL_LINE
 
@@ -734,7 +737,7 @@
 ) {
     int result = cxTreeRemoveNode(tree, node, relink_func);
     if (result == 0) {
-        cx_invoke_destructor(tree, node);
+        cx_invoke_destructor_raw(tree, node);
         return 0;
     } else {
         return result;
@@ -749,7 +752,8 @@
     );
     cx_foreach(void *, child, iter) {
         if (iter.exiting) {
-            cx_invoke_destructor(tree, child);
+            // always call the destructors with the node!
+            cx_invoke_destructor_raw(tree, child);
         }
     }
     tree->collection.size -= iter.counter;
--- a/tests/test_tree.c	Wed Dec 31 12:51:12 2025 +0100
+++ b/tests/test_tree.c	Wed Dec 31 13:39:55 2025 +0100
@@ -1813,13 +1813,13 @@
 
     cx_test_register(suite, test_tree_high_create);
     cx_test_register(suite, test_tree_high_tree_depth);
-    // cx_test_register(suite, test_tree_high_set_parent);
-    // cx_test_register(suite, test_tree_high_add_find_remove_nodes);
-    // cx_test_register(suite, test_tree_high_add_find_destroy_nodes);
-    // cx_test_register(suite, test_tree_high_remove_or_destroy_root);
-    // cx_test_register(suite, test_tree_high_simple_destructor);
-    // cx_test_register(suite, test_tree_high_iterator);
-    // cx_test_register(suite, test_tree_high_visitor);
+    cx_test_register(suite, test_tree_high_set_parent);
+    cx_test_register(suite, test_tree_high_add_find_remove_nodes);
+    cx_test_register(suite, test_tree_high_add_find_destroy_nodes);
+    cx_test_register(suite, test_tree_high_remove_or_destroy_root);
+    cx_test_register(suite, test_tree_high_simple_destructor);
+    cx_test_register(suite, test_tree_high_iterator);
+    cx_test_register(suite, test_tree_high_visitor);
 
     return suite;
 }

mercurial