Sat, 08 Feb 2025 20:38:05 +0100
adds documentation for destructor functions and collections
also invented some new macros for the collection.h
relates to #451
--- a/docs/Writerside/topics/allocator.h.md Sat Feb 08 14:24:22 2025 +0100 +++ b/docs/Writerside/topics/allocator.h.md Sat Feb 08 20:38:05 2025 +0100 @@ -107,6 +107,37 @@ } ``` +When you are implementing + +## Destructor Functions + +The `allocator.h` header also declares two function pointers for destructor functions. + +```C +typedef void (*cx_destructor_func)(void *memory); +typedef void (*cx_destructor_func2)(void *data, void *memory); +``` + +The first one is called _simple_ destructor (e.g. in the context of [collections](collection.h.md)), +and the second one is called _advanced_ destructor. +The only difference is that you can pass additional custom `data` to an advanced destructor function. + +Destructor functions play a vital role in deep de-allocations. +Another scenarios, besides destroying elements in a collection, are the de-allocation of objects +stored in a [memory pool](mempool.h.md) or de-allocations of deeply nested [JSON](json.h.md) objects. + +> Destructor functions are not to be confused with `free()`-like functions. +> The fundamental differences are that +> * it is not safe to pass `NULL` to a destructor function +> * a destructor may only de-allocate the contents inside an object but not the object itself, depending on context +> +{style="note"} + +> For example, when you are using a [list](list.h.md) that stores elements directly, a destructor function +> assigned to that collection may only destroy the element's contents but must not deallocate the element's memory. +> On the other hand, when the list is storing just pointers to the elements, you _may_ want the destructor +> function to also de-allocate the element's memory when the element is removed from that list. + <seealso> <category ref="apidoc"> <a href="https://ucx.sourceforge.io/api/allocator_8h.html">allocator.h</a>
--- a/docs/Writerside/topics/collection.h.md Sat Feb 08 14:24:22 2025 +0100 +++ b/docs/Writerside/topics/collection.h.md Sat Feb 08 20:38:05 2025 +0100 @@ -1,24 +1,77 @@ # Collections -<warning> -Outdated - Rewrite! -</warning> - -Collections in UCX 3 have several common features. +UCX defines common attributes for collections. If you want to implement an own collection data type that uses the same features, you can use the `CX_COLLECTION_BASE` macro at the beginning of your struct to roll out all members a usual UCX collection has. + +This macro will embed a structure in your collection that can be accessed with the member name `collection`. + ```c struct my_fancy_collection_s { - CX_COLLECTION_BASE; + CX_COLLECTION_BASE; // adds a member named 'collection' struct my_collection_data_s *data; }; ``` -Based on this structure, this header provides some convenience macros for invoking the destructor functions -that are part of the basic collection members. -The idea of having destructor functions within a collection is that you can destroy the collection _and_ the -contents with one single function call. -When you are implementing a collection, you are responsible for invoking the destructors at the right places, e.g. -when removing (and deleting) elements in the collection, clearing the collection, or - the most prominent case - -destroying the collection. + +> You can always look at the UCX list and map implementations if you need some inspiration. + +## Base Attributes of a Collection + +The following attributes are declared by the `CX_COLLECTION_BASE` macro: + +| Attribute | Description | +|-----------------------|----------------------------------------------------------------------------------------------------------------| +| `allocator` | The [allocator](allocator.h.md) that shall be used for the collection data. | +| `cmpfunc` | A function to [compare](compare.h.md) two elements. | +| `elem_size` | The size of one element in bytes. | +| `size` | The size, meaning the number of elements, currently stored. | +| `simple_destructor` | An optional simple [destructor function](allocator.h.md#destructor-functions). | +| `advanced_destructor` | An optional advanced destructor function. | +| `destructor_data` | A pointer to the custom data that shall be passed to the advanced destructor. | +| `store_pointer` | A `bool` indicating whether this collection stores pointers instead of the element's data. | +| `sorted` | A `bool` indicating whether the elements are currently guaranteed sorted with respect to the compare function. | + +The attributes can be accessed directly via the `collection` member of your struct, or with the following convenience macros. + +```C +cxCollectionSize(c) +cxCollectionElementSize(c) +cxCollectionStoresPointers(c) +cxCollectionSorted(c) +``` + +In each case the argument `c` is a pointer to your collection. The macro will then access the base data with `c->collection`. + +For working with destructors, the following macros are defined: -You can always look at the UCX list and map implementations if you need some inspiration. +```C +cxDefineDestructor(c, destr) +cxDefineAdvancedDestructor(c, destr, data) + +// use in your collection's implementation +cx_invoke_destructor(c, elem) + +// the following two should not be used +cx_invoke_simple_destructor(c, elem) +cx_invoke_advanced_destructor(c, elem) +``` + +With `cxDefineDestructor()` you can assign a simple [destructor function](allocator.h.md#destructor-functions) +to an _instance_ of your collection. +Similarly, you can assign an advanced destructor with custom `data` by using `cxDefineAdvancedDestructor`. + +Your collection _should_ be supporting destructors by invoking `cx_invoke_destructor()` whenever an element +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. +> If your collection is storing pointers (i.e. `cxCollectionStorePointers()` 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 pointer to the actual element. +{style="note"} + +<seealso> +<category ref="apidoc"> +<a href="https://ucx.sourceforge.io/api/collection_8h.html">collection.h</a> +</category> +</seealso>
--- a/src/cx/collection.h Sat Feb 08 14:24:22 2025 +0100 +++ b/src/cx/collection.h Sat Feb 08 20:38:05 2025 +0100 @@ -113,6 +113,45 @@ #define CX_COLLECTION_BASE struct cx_collection_s collection /** + * Returns the number of elements currently stored. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @return (@c size_t) the number of currently stored elements + */ +#define cxCollectionSize(c) ((c)->collection.size) + +/** + * Returns the size of one element. + * + * If #cxCollectionStoresPointers() returns true, this is the size of a pointer. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @return (@c size_t) the size of one element in bytes + */ +#define cxCollectionElementSize(c) ((c)->collection.elem_size) + +/** + * Indicates whether this collection only stores pointers instead of the actual data. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @retval true if this collection stores only pointers to data + * @retval false if this collection stores the actual element's data + */ +#define cxCollectionStoresPointers(c) ((c)->collection.store_pointer) + +/** + * Indicates whether the collection can guarantee that the stored elements are currently sorted. + * + * This may return false even when the elements are sorted. + * It is totally up to the implementation of the collection whether it keeps track of the order of its elements. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @retval true if the elements are currently sorted wrt. the collection's compare function + * @retval false if the order of elements is unknown + */ +#define cxCollectionSorted(c) ((c)->collection.sorted) + +/** * Sets a simple destructor function for this collection. * * @param c a pointer to a struct that contains #CX_COLLECTION_BASE