# HG changeset patch # User Mike Becker # Date 1767184795 -3600 # Node ID a5b7cf49dea755bf8d2bd910d8d45bfe104b5891 # Parent 27073814f654a2dd07dd942488c5ff285a2803df fix tree node destruction + reactivate all tests diff -r 27073814f654 -r a5b7cf49dea7 docs/Writerside/topics/collection.h.md --- 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"} diff -r 27073814f654 -r a5b7cf49dea7 src/cx/collection.h --- 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 diff -r 27073814f654 -r a5b7cf49dea7 src/tree.c --- 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; diff -r 27073814f654 -r a5b7cf49dea7 tests/test_tree.c --- 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; }