Fri, 28 Mar 2025 21:51:31 +0100
document remove and dispose for tree.h
relates to #451
# Tree UCX provides several low-level function for working with arbitrary tree structures, as well as some high-level functions for trees that induce a certain order on the data they store. The following convenience macros allow you to declare and use simple tree structures. ```C #define CX_TREE_NODE_BASE(Type) #define cx_tree_node_base_layout ``` The `CX_TREE_NODE_BASE` macro declares four pointers of type `Type*`: `parent`, `children`, `last_child`, `prev`, and `next`, which must be placed as first member into your struct. You can use it for example like this: ```C typedef struct my_tree_node_s { CX_TREE_NODE_BASE(struct my_tree_node_s); int value; char *desc; } MyTreeNode; ``` The macro `cx_tree_node_base_layout` expands to the offsets of the above-mentioned pointers. It will become handy when calling the low-level tree functions which expect all offsets to be passed as arguments. <warning> TODO: forgot to mention that loc_last_child and loc_prev are optional in all functions. </warning> Before diving into the function definitions, there are four function pointer declarations you should know. ```C #include <cx/tree.h> typedef void *(*cx_tree_node_create_func)( const void *data, void *context); typedef int (*cx_tree_search_data_func)( const void *node, const void *data); typedef int (*cx_tree_search_func)( const void *node, const void *new_node); typedef void (*cx_tree_relink_func)( void *node, const void *old_parent, const void *new_parent); ``` A `cx_tree_node_create_func` takes a pointer to the `data` the new node shall contain, and a `context` (which might be an allocator, for example). It shall allocate and return the created node, or `NULL` when node creation is not possible. A `cx_tree_search_data_func` shall check if the `node` contains the `data`, or if a child node might contain the data. It shall return zero, when `node` contains the `data`. When a child node _might_ contain the data, the returned positive number shall indicate how close the match is. It is _not_ relevant if the data can actually be found in the subtree - the positive number also indicates that the data could be inserted in that subtree. Only when the entire subtree starting with `node` cannot contain the `data`, a negative number is supposed to be returned. A `cx_tree_search_func` behaves exactly the same, except that it does expect a pointer to the `data` but a pointer to a node structure. In combination with the `cx_tree_node_create_func`, when inserting nodes with the high-level functions, a `new_node` is created first, and then an appropriate insertion point is searched with the `cx_tree_search_func`. A `cx_tree_relink_func` is called when an intermediate node was removed from the tree and it's children need to be detached from the removed `old_parent` and attached to a `new_parent`. The function is called for every child of the removed node and can be used, for example, to update the contents of the node when needed. ## Create ```C #include <cx/tree.h> CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func, cx_tree_search_func search_func, cx_tree_search_data_func search_data_func, ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, ptrdiff_t loc_prev, ptrdiff_t loc_next); CxTree *cxTreeCreateSimple(const CxAllocator *allocator, cx_tree_node_create_func create_func, cx_tree_search_func search_func, cx_tree_search_data_func search_data_func); CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root, ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, ptrdiff_t loc_prev, ptrdiff_t loc_next); ``` The function `cxTreeCreate()` creates a `CxTree` structure where each node created by the `create_func` has the layout specified by the offset arguments. The function `cxTreeCreateSimple()` is exactly the same, except that it assumes the `cx_tree_node_base_layout`. In both cases the tree will be created empty, that is with no nodes, not even a root node. On the other hand, `cxTreeCreateWrapped()` creates a `CxTree` structure with the specified layout and `root` node where `root` may already be the root of a larger tree. Note, that a wrapped tree by default has no create or search functions assigned. Therefore, if you wish to use one of the functions below that needs those function pointers set, you will have to set them manually by assigning to the respective fields in the `CxTree` structure. ## Add Nodes ```C #include <cx/tree.h> void cx_tree_link(void *parent, void *node, ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, ptrdiff_t loc_prev, ptrdiff_t loc_next); extern unsigned int cx_tree_add_look_around_depth; size_t cx_tree_add_iter(struct cx_iterator_base_s *iter, size_t n, cx_tree_search_func sfunc, cx_tree_node_create_func cfunc, void *cdata, void **failed, void *root, ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, ptrdiff_t loc_prev, ptrdiff_t loc_next); size_t cx_tree_add_array(const void *src, size_t n, size_t elem_size, cx_tree_search_func sfunc, cx_tree_node_create_func cfunc, void *cdata, void **failed, void *root, ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, ptrdiff_t loc_prev, ptrdiff_t loc_next); int cx_tree_add(const void *src, cx_tree_search_func sfunc, cx_tree_node_create_func cfunc, void *cdata, void **cnode, void *root, ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, ptrdiff_t loc_prev, ptrdiff_t loc_next); int cxTreeAddChild(CxTree *tree, void *parent, const void *data); void cxTreeAddChildNode(CxTree *tree, void *parent, void *child); void cxTreeSetParent(CxTree *tree, void *parent, void *child); int cxTreeInsert(CxTree *tree, const void *data); size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n); size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n); ``` <warning> TODO: document </warning> ## Size and Depth ```C #include <cx/tree.h> size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root); size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root); size_t cxTreeDepth(CxTree *tree); ``` The function `cxTreeSubtreeSize()` counts all nodes belonging to the subtree starting with `subtree_root`. The function `cxTreeSubtreeDepth()` reports the maximum depth of the subtree starting with `subtree_root`. If the `subtree_root` does not have any children, this results in a depth of one. The function `cxTreeDepth()` is equivalent to `cxTreeSubtreeDepth()` where `subtree_root` is the root node of the entire tree. > Passing a `NULL` pointer as `subtree_root` makes those functions return zero. > In the current UCX version there is no separate function `cxTreeSize()`, because > the size attribute can be directly accessed in the `CxTree` structure. > The next UCX version is planned to have also a function for accessing that attribute. >{style="note"} ## Search ```C #include <cx/tree.h> #define CX_TREE_SEARCH_INFINITE_DEPTH int cx_tree_search_data(const void *root, size_t depth, const void *data, cx_tree_search_data_func sfunc, void **result, ptrdiff_t loc_children, ptrdiff_t loc_next); int cx_tree_search(const void *root, size_t depth, const void *node, cx_tree_search_func sfunc, void **result, ptrdiff_t loc_children, ptrdiff_t loc_next); void *cxTreeFind(CxTree *tree, const void *data); void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth); ``` <warning> TODO: document low level functions </warning> The function `cxTreeFind()` uses the `search_data_func` of the `CxTree` to find the `data` in the tree, and returns a pointer to the node when the data was found (or `NULL`, otherwise). The function `cxTreeFindInSubtree()` is equivalent, except that it restricts the search to nodes in the subtree starting with (and including) `subtree_root`, and skipping all nodes below the `max_depth`. Note, that the `max_depth` is specified in relation to the `subtree_root` and not in relation to the entire tree. ## Iterator and Visitor ```C #include <cx/tree.h> CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit, ptrdiff_t loc_children, ptrdiff_t loc_next); CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit); CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit); #define cxTreeIteratorContinue(iter) void cxTreeIteratorDispose(CxTreeIterator *iter); CxTreeVisitor cx_tree_visitor(void *root, ptrdiff_t loc_children, ptrdiff_t loc_next); CxTreeVisitor cxTreeVisit(CxTree *tree); CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) #define cxTreeVisitorContinue(visitor) void cxTreeVisitorDispose(CxTreeVisitor *visitor); ``` <warning> TODO: document </warning> ## Remove ```C #include <cx/tree.h> void cx_tree_unlink(void *node, ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, ptrdiff_t loc_prev, ptrdiff_t loc_next); int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func); void cxTreeRemoveSubtree(CxTree *tree, void *node); ``` The low-level function `cx_tree_unlink()` removes the specified `node`, as well as the entire subtree beneath that node, from its parent. The high-level counterpart is `cxTreeRemoveSubtree()`. The function `cxTreeRemoveNode()`, on the other hand, _only_ removes the `node` from the tree, links all children of `node` to the former parent of `node`, and calls the optional `relink_func` for every former child of `node` which will be relinked to a new parent. Therefore, calling `cxTreeRemoveNode()` on the `root` node is an error and the function returns non-zero. In all other cases, the function returns zero. > When your tree is storing a scene graph, for example, a possible use-case for the `relink_func` might be updating > the world transform matrices in the subtree of the removed node. ## Dispose ```C #include <cx/tree.h> int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func); void cxTreeDestroySubtree(CxTree *tree, void *node); void cxTreeClear(CxTree *tree); void cxTreeFree(CxTree *tree); ``` The function `cxTreeDestroyNode()` first [removes](#remove) the `node` from the tree, like `cxTreeRemoveNode()`, and then invokes the [destructor functions](collection.h.md#destructor-functions). It is guaranteed, that the simple destructor is called before the advanced destructor. If no destructor function is registered at all, the behavior is identical to `cxTreeRemoveNode()`. That means, this function does not deallocate the memory for the node on its own and leaves that entirely to the destructor functions. The function `cxTreeDestroySubtree()` performs the above actions for the entire subtree starting with `node`. The order in which the destructor functions for the nodes of the subtree are called are determined by a [tree iterator](#iterator-and-visitor). The function `cxTreeClear()` is a shorthand for invoking `cxTreeDestroySubtree()` on the root node of the tree. The function `cxTreeFree()` behaves like `cxTreeClear()` and then deallocates the memory for the `CxTree` structure. > Although `CxTree` supports the general concept of [destructor functions](collection.h.md#destructor-functions), > it is not based on `CX_COLLECTION_BASE`. > Therefore, the `cxDefineDestructor()` and `cxDefineAdvancedDestructor()` macros cannot be used on a `CxTree` and > the fields must be set manually. >{style="note"} <seealso> <category ref="apidoc"> <a href="https://ucx.sourceforge.io/api/tree_8h.html">tree.h</a> </category> </seealso>