fix members inherited by macro or include are not documented

Thu, 23 May 2024 20:29:28 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 23 May 2024 20:29:28 +0200
changeset 854
fe0d69d72bcd
parent 853
d4baf4dd55c3
child 855
35bcb3216c0d

fix members inherited by macro or include are not documented

CHANGELOG file | annotate | diff | comparison | revisions
docs/src/features.md file | annotate | diff | comparison | revisions
src/Makefile file | annotate | diff | comparison | revisions
src/array_list.c file | annotate | diff | comparison | revisions
src/cx/collection.h file | annotate | diff | comparison | revisions
src/cx/collection_base.h file | annotate | diff | comparison | revisions
src/cx/iterator.h file | annotate | diff | comparison | revisions
src/cx/list.h file | annotate | diff | comparison | revisions
src/cx/map.h file | annotate | diff | comparison | revisions
src/cx/tree.h file | annotate | diff | comparison | revisions
src/hash_map.c file | annotate | diff | comparison | revisions
src/iterator.c file | annotate | diff | comparison | revisions
src/linked_list.c file | annotate | diff | comparison | revisions
src/list.c file | annotate | diff | comparison | revisions
src/map.c file | annotate | diff | comparison | revisions
src/tree.c file | annotate | diff | comparison | revisions
tests/Makefile file | annotate | diff | comparison | revisions
tests/test_hash_map.c file | annotate | diff | comparison | revisions
tests/test_list.c file | annotate | diff | comparison | revisions
tests/test_tree.c file | annotate | diff | comparison | revisions
     1.1 --- a/CHANGELOG	Thu May 23 19:29:14 2024 +0200
     1.2 +++ b/CHANGELOG	Thu May 23 20:29:28 2024 +0200
     1.3 @@ -11,7 +11,7 @@
     1.4   * adds cx_sprintf() and several more variants
     1.5   * adds runtime constants to read out the actual SBO sizes
     1.6   * adds improved version of UCX 2 Test framework (now a self-contained header)
     1.7 - * the cx_compare_func symbol is now also declared by compare.h
     1.8 + * the cx_compare_func symbol is now declared by compare.h
     1.9   * fixes wrong link from UCX 2 documentation to UCX 3 documentation
    1.10   * fixes critical bug that produced wrong results when comparing lists of different type but same size
    1.11   * removes flag_removal function from iterator (unfortunately breaks binary compatibility)
     2.1 --- a/docs/src/features.md	Thu May 23 19:29:14 2024 +0200
     2.2 +++ b/docs/src/features.md	Thu May 23 20:29:28 2024 +0200
     2.3 @@ -187,7 +187,7 @@
     2.4  `slot` and `kv_data` fields (for example when iterating over maps).
     2.5  
     2.6  If the predefined fields are insufficient for your use case, you can alternatively create your own iterator structure
     2.7 -and place the `CX_ITERATOR_BASE` macro inside.
     2.8 +and place the `CX_ITERATOR_BASE` macro as first member of that structure.
     2.9  
    2.10  Usually an iterator is not mutating the collection it is iterating over.
    2.11  In some programming languages it is even disallowed to change the collection while iterating with foreach.
    2.12 @@ -204,10 +204,10 @@
    2.13  
    2.14  Collections in UCX 3 have several common features.
    2.15  If you want to implement an own collection data type that uses the same features, you can use the
    2.16 -`CX_COLLECTION_MEMBERS` macro at the beginning of your struct to roll out all members a usual UCX collection has.
    2.17 +`CX_COLLECTION_BASE` macro at the beginning of your struct to roll out all members a usual UCX collection has.
    2.18  ```c
    2.19  struct my_fancy_collection_s {
    2.20 -    CX_COLLECTION_MEMBERS
    2.21 +    CX_COLLECTION_BASE;
    2.22      struct my_collection_data_s *data;
    2.23  };
    2.24  ```
    2.25 @@ -225,21 +225,11 @@
    2.26  
    2.27  *Header file:* [list.h](api/list_8h.html)
    2.28  
    2.29 -This header defines a common interface for all list implementations, which is basically as simple as the following
    2.30 -structure.
    2.31 -```c
    2.32 -struct cx_list_s {
    2.33 -    CX_COLLECTION_MEMBERS       // size, capacity, etc.
    2.34 -    cx_list_class const *cl;    // The list class definition
    2.35 -};
    2.36 -```
    2.37 -The actual structure contains one more class pointer that is used when wrapping a list into a pointer-aware list
    2.38 -with `cxListStorePointers()`. What this means, is that - if you want to implement your own list structure - you
    2.39 -only need to cover the case where the list is storing copies of your objects.
    2.40 +This header defines a common interface for all list implementations.
    2.41  
    2.42 -UCX comes with two common list implementations (linked list and array list) that should cover most use cases.
    2.43 -But if you feel the need to implement an own list, the only thing you need to do is to define a struct where
    2.44 -`struct cx_list_s`, and set an appropriate list class that implements the functionality.
    2.45 +UCX already comes with two common list implementations (linked list and array list) that should cover most use cases.
    2.46 +But if you feel the need to implement an own list, the only thing you need to do is to define a struct with a
    2.47 +`struct cx_list_s` as first member, and set an appropriate list class that implements the functionality.
    2.48  It is strongly recommended that this class is shared among all instances of the same list type, because otherwise
    2.49  the `cxListCompare` function cannot use the optimized implementation of your class and will instead fall back to
    2.50  using iterators to compare the contents element-wise.
     3.1 --- a/src/Makefile	Thu May 23 19:29:14 2024 +0200
     3.2 +++ b/src/Makefile	Thu May 23 20:29:28 2024 +0200
     3.3 @@ -67,7 +67,8 @@
     3.4  	$(CC) -o $@ $(CFLAGS) -c $<
     3.5  
     3.6  $(build_dir)/array_list$(OBJ_EXT): array_list.c cx/array_list.h cx/list.h \
     3.7 - cx/common.h cx/collection.h cx/allocator.h cx/iterator.h cx/compare.h
     3.8 + cx/common.h cx/collection.h cx/allocator.h cx/iterator.h cx/compare.h \
     3.9 + cx/compare.h
    3.10  	@echo "Compiling $<"
    3.11  	$(CC) -o $@ $(CFLAGS) -c $<
    3.12  
    3.13 @@ -85,8 +86,8 @@
    3.14  	$(CC) -o $@ $(CFLAGS) -c $<
    3.15  
    3.16  $(build_dir)/hash_map$(OBJ_EXT): hash_map.c cx/hash_map.h cx/map.h \
    3.17 - cx/common.h cx/collection.h cx/allocator.h cx/iterator.h cx/string.h \
    3.18 - cx/hash_key.h cx/utils.h
    3.19 + cx/common.h cx/collection.h cx/allocator.h cx/iterator.h cx/compare.h \
    3.20 + cx/string.h cx/hash_key.h cx/utils.h
    3.21  	@echo "Compiling $<"
    3.22  	$(CC) -o $@ $(CFLAGS) -c $<
    3.23  
    3.24 @@ -96,17 +97,17 @@
    3.25  
    3.26  $(build_dir)/linked_list$(OBJ_EXT): linked_list.c cx/linked_list.h \
    3.27   cx/common.h cx/list.h cx/collection.h cx/allocator.h cx/iterator.h \
    3.28 - cx/utils.h cx/compare.h
    3.29 + cx/compare.h cx/utils.h cx/compare.h
    3.30  	@echo "Compiling $<"
    3.31  	$(CC) -o $@ $(CFLAGS) -c $<
    3.32  
    3.33  $(build_dir)/list$(OBJ_EXT): list.c cx/list.h cx/common.h cx/collection.h \
    3.34 - cx/allocator.h cx/iterator.h
    3.35 + cx/allocator.h cx/iterator.h cx/compare.h
    3.36  	@echo "Compiling $<"
    3.37  	$(CC) -o $@ $(CFLAGS) -c $<
    3.38  
    3.39  $(build_dir)/map$(OBJ_EXT): map.c cx/map.h cx/common.h cx/collection.h \
    3.40 - cx/allocator.h cx/iterator.h cx/string.h cx/hash_key.h
    3.41 + cx/allocator.h cx/iterator.h cx/compare.h cx/string.h cx/hash_key.h
    3.42  	@echo "Compiling $<"
    3.43  	$(CC) -o $@ $(CFLAGS) -c $<
    3.44  
    3.45 @@ -130,7 +131,7 @@
    3.46  	$(CC) -o $@ $(CFLAGS) -c $<
    3.47  
    3.48  $(build_dir)/tree$(OBJ_EXT): tree.c cx/tree.h cx/common.h cx/iterator.h \
    3.49 - cx/array_list.h cx/list.h cx/collection.h cx/allocator.h
    3.50 + cx/array_list.h cx/list.h cx/collection.h cx/allocator.h cx/compare.h
    3.51  	@echo "Compiling $<"
    3.52  	$(CC) -o $@ $(CFLAGS) -c $<
    3.53  
     4.1 --- a/src/array_list.c	Thu May 23 19:29:14 2024 +0200
     4.2 +++ b/src/array_list.c	Thu May 23 20:29:28 2024 +0200
     4.3 @@ -191,21 +191,21 @@
     4.4  
     4.5      char *ptr = arl->data;
     4.6  
     4.7 -    if (list->simple_destructor) {
     4.8 -        for (size_t i = 0; i < list->size; i++) {
     4.9 +    if (list->base.simple_destructor) {
    4.10 +        for (size_t i = 0; i < list->base.size; i++) {
    4.11              cx_invoke_simple_destructor(list, ptr);
    4.12 -            ptr += list->item_size;
    4.13 +            ptr += list->base.item_size;
    4.14          }
    4.15      }
    4.16 -    if (list->advanced_destructor) {
    4.17 -        for (size_t i = 0; i < list->size; i++) {
    4.18 +    if (list->base.advanced_destructor) {
    4.19 +        for (size_t i = 0; i < list->base.size; i++) {
    4.20              cx_invoke_advanced_destructor(list, ptr);
    4.21 -            ptr += list->item_size;
    4.22 +            ptr += list->base.item_size;
    4.23          }
    4.24      }
    4.25  
    4.26 -    cxFree(list->allocator, arl->data);
    4.27 -    cxFree(list->allocator, list);
    4.28 +    cxFree(list->base.allocator, arl->data);
    4.29 +    cxFree(list->base.allocator, list);
    4.30  }
    4.31  
    4.32  static size_t cx_arl_insert_array(
    4.33 @@ -215,25 +215,25 @@
    4.34          size_t n
    4.35  ) {
    4.36      // out of bounds and special case check
    4.37 -    if (index > list->size || n == 0) return 0;
    4.38 +    if (index > list->base.size || n == 0) return 0;
    4.39  
    4.40      // get a correctly typed pointer to the list
    4.41      cx_array_list *arl = (cx_array_list *) list;
    4.42  
    4.43      // do we need to move some elements?
    4.44 -    if (index < list->size) {
    4.45 +    if (index < list->base.size) {
    4.46          char const *first_to_move = (char const *) arl->data;
    4.47 -        first_to_move += index * list->item_size;
    4.48 -        size_t elems_to_move = list->size - index;
    4.49 +        first_to_move += index * list->base.item_size;
    4.50 +        size_t elems_to_move = list->base.size - index;
    4.51          size_t start_of_moved = index + n;
    4.52  
    4.53          if (CX_ARRAY_SUCCESS != cx_array_copy(
    4.54                  &arl->data,
    4.55 -                &list->size,
    4.56 +                &list->base.size,
    4.57                  &arl->capacity,
    4.58                  start_of_moved,
    4.59                  first_to_move,
    4.60 -                list->item_size,
    4.61 +                list->base.item_size,
    4.62                  elems_to_move,
    4.63                  &arl->reallocator
    4.64          )) {
    4.65 @@ -249,11 +249,11 @@
    4.66      // place the new elements
    4.67      if (CX_ARRAY_SUCCESS == cx_array_copy(
    4.68              &arl->data,
    4.69 -            &list->size,
    4.70 +            &list->base.size,
    4.71              &arl->capacity,
    4.72              index,
    4.73              array,
    4.74 -            list->item_size,
    4.75 +            list->base.item_size,
    4.76              n,
    4.77              &arl->reallocator
    4.78      )) {
    4.79 @@ -278,7 +278,7 @@
    4.80          int prepend
    4.81  ) {
    4.82      struct cx_list_s *list = iter->src_handle.m;
    4.83 -    if (iter->index < list->size) {
    4.84 +    if (iter->index < list->base.size) {
    4.85          int result = cx_arl_insert_element(
    4.86                  list,
    4.87                  iter->index + 1 - prepend,
    4.88 @@ -286,12 +286,12 @@
    4.89          );
    4.90          if (result == 0 && prepend != 0) {
    4.91              iter->index++;
    4.92 -            iter->elem_handle = ((char *) iter->elem_handle) + list->item_size;
    4.93 +            iter->elem_handle = ((char *) iter->elem_handle) + list->base.item_size;
    4.94          }
    4.95          return result;
    4.96      } else {
    4.97 -        int result = cx_arl_insert_element(list, list->size, elem);
    4.98 -        iter->index = list->size;
    4.99 +        int result = cx_arl_insert_element(list, list->base.size, elem);
   4.100 +        iter->index = list->base.size;
   4.101          return result;
   4.102      }
   4.103  }
   4.104 @@ -303,28 +303,28 @@
   4.105      cx_array_list *arl = (cx_array_list *) list;
   4.106  
   4.107      // out-of-bounds check
   4.108 -    if (index >= list->size) {
   4.109 +    if (index >= list->base.size) {
   4.110          return 1;
   4.111      }
   4.112  
   4.113      // content destruction
   4.114 -    cx_invoke_destructor(list, ((char *) arl->data) + index * list->item_size);
   4.115 +    cx_invoke_destructor(list, ((char *) arl->data) + index * list->base.item_size);
   4.116  
   4.117      // short-circuit removal of last element
   4.118 -    if (index == list->size - 1) {
   4.119 -        list->size--;
   4.120 +    if (index == list->base.size - 1) {
   4.121 +        list->base.size--;
   4.122          return 0;
   4.123      }
   4.124  
   4.125      // just move the elements starting at index to the left
   4.126      int result = cx_array_copy(
   4.127              &arl->data,
   4.128 -            &list->size,
   4.129 +            &list->base.size,
   4.130              &arl->capacity,
   4.131              index,
   4.132 -            ((char *) arl->data) + (index + 1) * list->item_size,
   4.133 -            list->item_size,
   4.134 -            list->size - index - 1,
   4.135 +            ((char *) arl->data) + (index + 1) * list->base.item_size,
   4.136 +            list->base.item_size,
   4.137 +            list->base.size - index - 1,
   4.138              &arl->reallocator
   4.139      );
   4.140  
   4.141 @@ -332,32 +332,32 @@
   4.142      assert(result == 0);
   4.143  
   4.144      // decrease the size
   4.145 -    list->size--;
   4.146 +    list->base.size--;
   4.147  
   4.148      return 0;
   4.149  }
   4.150  
   4.151  static void cx_arl_clear(struct cx_list_s *list) {
   4.152 -    if (list->size == 0) return;
   4.153 +    if (list->base.size == 0) return;
   4.154  
   4.155      cx_array_list *arl = (cx_array_list *) list;
   4.156      char *ptr = arl->data;
   4.157  
   4.158 -    if (list->simple_destructor) {
   4.159 -        for (size_t i = 0; i < list->size; i++) {
   4.160 +    if (list->base.simple_destructor) {
   4.161 +        for (size_t i = 0; i < list->base.size; i++) {
   4.162              cx_invoke_simple_destructor(list, ptr);
   4.163 -            ptr += list->item_size;
   4.164 +            ptr += list->base.item_size;
   4.165          }
   4.166      }
   4.167 -    if (list->advanced_destructor) {
   4.168 -        for (size_t i = 0; i < list->size; i++) {
   4.169 +    if (list->base.advanced_destructor) {
   4.170 +        for (size_t i = 0; i < list->base.size; i++) {
   4.171              cx_invoke_advanced_destructor(list, ptr);
   4.172 -            ptr += list->item_size;
   4.173 +            ptr += list->base.item_size;
   4.174          }
   4.175      }
   4.176  
   4.177 -    memset(arl->data, 0, list->size * list->item_size);
   4.178 -    list->size = 0;
   4.179 +    memset(arl->data, 0, list->base.size * list->base.item_size);
   4.180 +    list->base.size = 0;
   4.181  }
   4.182  
   4.183  static int cx_arl_swap(
   4.184 @@ -365,9 +365,9 @@
   4.185          size_t i,
   4.186          size_t j
   4.187  ) {
   4.188 -    if (i >= list->size || j >= list->size) return 1;
   4.189 +    if (i >= list->base.size || j >= list->base.size) return 1;
   4.190      cx_array_list *arl = (cx_array_list *) list;
   4.191 -    cx_array_swap(arl->data, list->item_size, i, j);
   4.192 +    cx_array_swap(arl->data, list->base.item_size, i, j);
   4.193      return 0;
   4.194  }
   4.195  
   4.196 @@ -375,10 +375,10 @@
   4.197          struct cx_list_s const *list,
   4.198          size_t index
   4.199  ) {
   4.200 -    if (index < list->size) {
   4.201 +    if (index < list->base.size) {
   4.202          cx_array_list const *arl = (cx_array_list const *) list;
   4.203          char *space = arl->data;
   4.204 -        return space + index * list->item_size;
   4.205 +        return space + index * list->base.item_size;
   4.206      } else {
   4.207          return NULL;
   4.208      }
   4.209 @@ -389,12 +389,12 @@
   4.210          void const *elem,
   4.211          bool remove
   4.212  ) {
   4.213 -    assert(list->cmpfunc != NULL);
   4.214 -    assert(list->size < SIZE_MAX / 2);
   4.215 +    assert(list->base.cmpfunc != NULL);
   4.216 +    assert(list->base.size < SIZE_MAX / 2);
   4.217      char *cur = ((cx_array_list const *) list)->data;
   4.218  
   4.219 -    for (ssize_t i = 0; i < (ssize_t) list->size; i++) {
   4.220 -        if (0 == list->cmpfunc(elem, cur)) {
   4.221 +    for (ssize_t i = 0; i < (ssize_t) list->base.size; i++) {
   4.222 +        if (0 == list->base.cmpfunc(elem, cur)) {
   4.223              if (remove) {
   4.224                  if (0 == cx_arl_remove(list, i)) {
   4.225                      return i;
   4.226 @@ -405,18 +405,18 @@
   4.227                  return i;
   4.228              }
   4.229          }
   4.230 -        cur += list->item_size;
   4.231 +        cur += list->base.item_size;
   4.232      }
   4.233  
   4.234      return -1;
   4.235  }
   4.236  
   4.237  static void cx_arl_sort(struct cx_list_s *list) {
   4.238 -    assert(list->cmpfunc != NULL);
   4.239 +    assert(list->base.cmpfunc != NULL);
   4.240      qsort(((cx_array_list *) list)->data,
   4.241 -          list->size,
   4.242 -          list->item_size,
   4.243 -          list->cmpfunc
   4.244 +          list->base.size,
   4.245 +          list->base.item_size,
   4.246 +          list->base.cmpfunc
   4.247      );
   4.248  }
   4.249  
   4.250 @@ -424,37 +424,37 @@
   4.251          struct cx_list_s const *list,
   4.252          struct cx_list_s const *other
   4.253  ) {
   4.254 -    assert(list->cmpfunc != NULL);
   4.255 -    if (list->size == other->size) {
   4.256 +    assert(list->base.cmpfunc != NULL);
   4.257 +    if (list->base.size == other->base.size) {
   4.258          char const *left = ((cx_array_list const *) list)->data;
   4.259          char const *right = ((cx_array_list const *) other)->data;
   4.260 -        for (size_t i = 0; i < list->size; i++) {
   4.261 -            int d = list->cmpfunc(left, right);
   4.262 +        for (size_t i = 0; i < list->base.size; i++) {
   4.263 +            int d = list->base.cmpfunc(left, right);
   4.264              if (d != 0) {
   4.265                  return d;
   4.266              }
   4.267 -            left += list->item_size;
   4.268 -            right += other->item_size;
   4.269 +            left += list->base.item_size;
   4.270 +            right += other->base.item_size;
   4.271          }
   4.272          return 0;
   4.273      } else {
   4.274 -        return list->size < other->size ? -1 : 1;
   4.275 +        return list->base.size < other->base.size ? -1 : 1;
   4.276      }
   4.277  }
   4.278  
   4.279  static void cx_arl_reverse(struct cx_list_s *list) {
   4.280 -    if (list->size < 2) return;
   4.281 +    if (list->base.size < 2) return;
   4.282      void *data = ((cx_array_list const *) list)->data;
   4.283 -    size_t half = list->size / 2;
   4.284 +    size_t half = list->base.size / 2;
   4.285      for (size_t i = 0; i < half; i++) {
   4.286 -        cx_array_swap(data, list->item_size, i, list->size - 1 - i);
   4.287 +        cx_array_swap(data, list->base.item_size, i, list->base.size - 1 - i);
   4.288      }
   4.289  }
   4.290  
   4.291  static bool cx_arl_iter_valid(void const *it) {
   4.292      struct cx_iterator_s const *iter = it;
   4.293      struct cx_list_s const *list = iter->src_handle.c;
   4.294 -    return iter->index < list->size;
   4.295 +    return iter->index < list->base.size;
   4.296  }
   4.297  
   4.298  static void *cx_arl_iter_current(void const *it) {
   4.299 @@ -464,28 +464,28 @@
   4.300  
   4.301  static void cx_arl_iter_next(void *it) {
   4.302      struct cx_iterator_s *iter = it;
   4.303 -    if (iter->remove) {
   4.304 -        iter->remove = false;
   4.305 +    if (iter->base.remove) {
   4.306 +        iter->base.remove = false;
   4.307          cx_arl_remove(iter->src_handle.m, iter->index);
   4.308      } else {
   4.309          iter->index++;
   4.310          iter->elem_handle =
   4.311                  ((char *) iter->elem_handle)
   4.312 -                + ((struct cx_list_s const *) iter->src_handle.c)->item_size;
   4.313 +                + ((struct cx_list_s const *) iter->src_handle.c)->base.item_size;
   4.314      }
   4.315  }
   4.316  
   4.317  static void cx_arl_iter_prev(void *it) {
   4.318      struct cx_iterator_s *iter = it;
   4.319      cx_array_list const* list = iter->src_handle.c;
   4.320 -    if (iter->remove) {
   4.321 -        iter->remove = false;
   4.322 +    if (iter->base.remove) {
   4.323 +        iter->base.remove = false;
   4.324          cx_arl_remove(iter->src_handle.m, iter->index);
   4.325      }
   4.326      iter->index--;
   4.327 -    if (iter->index < list->base.size) {
   4.328 +    if (iter->index < list->base.base.size) {
   4.329          iter->elem_handle = ((char *) list->data)
   4.330 -                            + iter->index * list->base.item_size;
   4.331 +                            + iter->index * list->base.base.item_size;
   4.332      }
   4.333  }
   4.334  
   4.335 @@ -500,13 +500,13 @@
   4.336      iter.index = index;
   4.337      iter.src_handle.c = list;
   4.338      iter.elem_handle = cx_arl_at(list, index);
   4.339 -    iter.elem_size = list->item_size;
   4.340 -    iter.elem_count = list->size;
   4.341 -    iter.valid = cx_arl_iter_valid;
   4.342 -    iter.current = cx_arl_iter_current;
   4.343 -    iter.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
   4.344 -    iter.remove = false;
   4.345 -    iter.mutating = false;
   4.346 +    iter.elem_size = list->base.item_size;
   4.347 +    iter.elem_count = list->base.size;
   4.348 +    iter.base.valid = cx_arl_iter_valid;
   4.349 +    iter.base.current = cx_arl_iter_current;
   4.350 +    iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
   4.351 +    iter.base.remove = false;
   4.352 +    iter.base.mutating = false;
   4.353  
   4.354      return iter;
   4.355  }
   4.356 @@ -541,15 +541,15 @@
   4.357      if (list == NULL) return NULL;
   4.358  
   4.359      list->base.cl = &cx_array_list_class;
   4.360 -    list->base.allocator = allocator;
   4.361 +    list->base.base.allocator = allocator;
   4.362      list->capacity = initial_capacity;
   4.363  
   4.364      if (item_size > 0) {
   4.365 -        list->base.item_size = item_size;
   4.366 -        list->base.cmpfunc = comparator;
   4.367 +        list->base.base.item_size = item_size;
   4.368 +        list->base.base.cmpfunc = comparator;
   4.369      } else {
   4.370          item_size = sizeof(void *);
   4.371 -        list->base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator;
   4.372 +        list->base.base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator;
   4.373          cxListStorePointers((CxList *) list);
   4.374      }
   4.375  
     5.1 --- a/src/cx/collection.h	Thu May 23 19:29:14 2024 +0200
     5.2 +++ b/src/cx/collection.h	Thu May 23 20:29:28 2024 +0200
     5.3 @@ -38,6 +38,7 @@
     5.4  
     5.5  #include "allocator.h"
     5.6  #include "iterator.h"
     5.7 +#include "compare.h"
     5.8  
     5.9  #ifdef __cplusplus
    5.10  extern "C" {
    5.11 @@ -48,60 +49,55 @@
    5.12   */
    5.13  #define CX_STORE_POINTERS 0
    5.14  
    5.15 -#ifndef CX_COMPARE_FUNC_DEFINED
    5.16 -#define CX_COMPARE_FUNC_DEFINED
    5.17  /**
    5.18 - * A comparator function comparing two collection elements.
    5.19 + * Base attributes of a collection.
    5.20   */
    5.21 -typedef int(*cx_compare_func)(
    5.22 -        void const *left,
    5.23 -        void const *right
    5.24 -);
    5.25 -#endif // CX_COMPARE_FUNC_DEFINED
    5.26 +struct cx_collection_s {
    5.27 +    /**
    5.28 +     * The allocator to use.
    5.29 +     */
    5.30 +    CxAllocator const *allocator;
    5.31 +    /**
    5.32 +     * The comparator function for the elements.
    5.33 +     */
    5.34 +    cx_compare_func cmpfunc;
    5.35 +    /**
    5.36 +     * The size of each element.
    5.37 +     */
    5.38 +    size_t item_size;
    5.39 +    /**
    5.40 +     * The number of currently stored elements.
    5.41 +     */
    5.42 +    size_t size;
    5.43 +    /**
    5.44 +     * An optional simple destructor for the collection's elements.
    5.45 +     *
    5.46 +     * @attention Read the documentation of the particular collection implementation
    5.47 +     * whether this destructor shall only destroy the contents or also free the memory.
    5.48 +     */
    5.49 +    cx_destructor_func simple_destructor;
    5.50 +    /**
    5.51 +     * An optional advanced destructor for the collection's elements.
    5.52 +     *
    5.53 +     * @attention Read the documentation of the particular collection implementation
    5.54 +     * whether this destructor shall only destroy the contents or also free the memory.
    5.55 +     */
    5.56 +    cx_destructor_func2 advanced_destructor;
    5.57 +    /**
    5.58 +     * The pointer to additional data that is passed to the advanced destructor.
    5.59 +     */
    5.60 +    void *destructor_data;
    5.61 +    /**
    5.62 +     * Indicates if this list is supposed to store pointers
    5.63 +     * instead of copies of the actual objects.
    5.64 +     */
    5.65 +    bool store_pointer;
    5.66 +};
    5.67  
    5.68  /**
    5.69   * Use this macro to declare common members for a collection structure.
    5.70   */
    5.71 -#define CX_COLLECTION_MEMBERS \
    5.72 -    /** \
    5.73 -     * The allocator to use. \
    5.74 -     */ \
    5.75 -    CxAllocator const *allocator; \
    5.76 -    /** \
    5.77 -     * The comparator function for the elements. \
    5.78 -     */ \
    5.79 -    cx_compare_func cmpfunc; \
    5.80 -    /** \
    5.81 -     * The size of each element. \
    5.82 -     */ \
    5.83 -    size_t item_size; \
    5.84 -    /** \
    5.85 -     * The number of currently stored elements. \
    5.86 -     */ \
    5.87 -    size_t size; \
    5.88 -    /** \
    5.89 -     * An optional simple destructor for the collection's elements. \
    5.90 -     * \
    5.91 -     * @attention Read the documentation of the particular collection implementation \
    5.92 -     * whether this destructor shall only destroy the contents or also free the memory. \
    5.93 -     */ \
    5.94 -    cx_destructor_func simple_destructor; \
    5.95 -    /** \
    5.96 -     * An optional advanced destructor for the collection's elements. \
    5.97 -     * \
    5.98 -     * @attention Read the documentation of the particular collection implementation \
    5.99 -     * whether this destructor shall only destroy the contents or also free the memory. \
   5.100 -     */ \
   5.101 -    cx_destructor_func2 advanced_destructor; \
   5.102 -    /** \
   5.103 -     * The pointer to additional data that is passed to the advanced destructor. \
   5.104 -     */ \
   5.105 -    void *destructor_data; \
   5.106 -    /** \
   5.107 -     * Indicates if this instance of a collection is supposed to store pointers \
   5.108 -     * instead of copies of the actual objects. \
   5.109 -     */ \
   5.110 -    bool store_pointer;
   5.111 +#define CX_COLLECTION_BASE struct cx_collection_s base
   5.112  
   5.113  /**
   5.114   * Invokes the simple destructor function for a specific element.
   5.115 @@ -113,7 +109,7 @@
   5.116   * @param e the element
   5.117   */
   5.118  #define cx_invoke_simple_destructor(c, e) \
   5.119 -    (c)->simple_destructor((c)->store_pointer ? (*((void **) (e))) : (e))
   5.120 +    (c)->base.simple_destructor((c)->base.store_pointer ? (*((void **) (e))) : (e))
   5.121  
   5.122  /**
   5.123   * Invokes the advanced destructor function for a specific element.
   5.124 @@ -125,8 +121,8 @@
   5.125   * @param e the element
   5.126   */
   5.127  #define cx_invoke_advanced_destructor(c, e) \
   5.128 -    (c)->advanced_destructor((c)->destructor_data, \
   5.129 -    (c)->store_pointer ? (*((void **) (e))) : (e))
   5.130 +    (c)->base.advanced_destructor((c)->base.destructor_data, \
   5.131 +    (c)->base.store_pointer ? (*((void **) (e))) : (e))
   5.132  
   5.133  
   5.134  /**
   5.135 @@ -139,8 +135,8 @@
   5.136   * @param e the element
   5.137   */
   5.138  #define cx_invoke_destructor(c, e) \
   5.139 -    if ((c)->simple_destructor) cx_invoke_simple_destructor(c,e); \
   5.140 -    if ((c)->advanced_destructor) cx_invoke_advanced_destructor(c,e)
   5.141 +    if ((c)->base.simple_destructor) cx_invoke_simple_destructor(c,e); \
   5.142 +    if ((c)->base.advanced_destructor) cx_invoke_advanced_destructor(c,e)
   5.143  
   5.144  #ifdef __cplusplus
   5.145  } // extern "C"
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/cx/collection_base.h	Thu May 23 20:29:28 2024 +0200
     6.3 @@ -0,0 +1,35 @@
     6.4 +/*
     6.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     6.6 + *
     6.7 + * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved.
     6.8 + *
     6.9 + * Redistribution and use in source and binary forms, with or without
    6.10 + * modification, are permitted provided that the following conditions are met:
    6.11 + *
    6.12 + *   1. Redistributions of source code must retain the above copyright
    6.13 + *      notice, this list of conditions and the following disclaimer.
    6.14 + *
    6.15 + *   2. Redistributions in binary form must reproduce the above copyright
    6.16 + *      notice, this list of conditions and the following disclaimer in the
    6.17 + *      documentation and/or other materials provided with the distribution.
    6.18 + *
    6.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    6.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    6.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    6.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    6.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    6.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    6.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    6.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    6.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    6.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    6.29 + * POSSIBILITY OF SUCH DAMAGE.
    6.30 + */
    6.31 +/**
    6.32 + * \file collection_base.h
    6.33 + * \brief Include this file in your collection struct to declare common members.
    6.34 + * \author Mike Becker
    6.35 + * \author Olaf Wintermann
    6.36 + * \copyright 2-Clause BSD License
    6.37 + */
    6.38 +
     7.1 --- a/src/cx/iterator.h	Thu May 23 19:29:14 2024 +0200
     7.2 +++ b/src/cx/iterator.h	Thu May 23 20:29:28 2024 +0200
     7.3 @@ -38,45 +38,54 @@
     7.4  
     7.5  #include "common.h"
     7.6  
     7.7 -#define CX_ITERATOR_BASE \
     7.8 -    /** \
     7.9 -     * True iff the iterator points to valid data. \
    7.10 -     */ \
    7.11 -    __attribute__ ((__nonnull__)) \
    7.12 -    bool (*valid)(void const *); \
    7.13 -    /** \
    7.14 -     * Returns a pointer to the current element. \
    7.15 -     * \
    7.16 -     * When valid returns false, the behavior of this function is undefined. \
    7.17 -     */ \
    7.18 -    __attribute__ ((__nonnull__)) \
    7.19 -    void *(*current)(void const *); \
    7.20 -    /** \
    7.21 -     * Original implementation in case the function needs to be wrapped. \
    7.22 -     */ \
    7.23 -    __attribute__ ((__nonnull__)) \
    7.24 -    void *(*current_impl)(void const *); \
    7.25 -    /** \
    7.26 -     * Advances the iterator. \
    7.27 -     * \
    7.28 -     * When valid returns false, the behavior of this function is undefined. \
    7.29 -     */ \
    7.30 -    __attribute__ ((__nonnull__)) \
    7.31 -    void (*next)(void *); \
    7.32 -    /** \
    7.33 -     * Indicates whether this iterator may remove elements. \
    7.34 -     */ \
    7.35 -    bool mutating; \
    7.36 -    /** \
    7.37 -     * Internal flag for removing the current element when advancing. \
    7.38 -     */ \
    7.39 +struct cx_iterator_base_s {
    7.40 +    /**
    7.41 +     * True iff the iterator points to valid data.
    7.42 +     */
    7.43 +    __attribute__ ((__nonnull__))
    7.44 +    bool (*valid)(void const *);
    7.45 +
    7.46 +    /**
    7.47 +     * Returns a pointer to the current element.
    7.48 +     *
    7.49 +     * When valid returns false, the behavior of this function is undefined.
    7.50 +     */
    7.51 +    __attribute__ ((__nonnull__))
    7.52 +    void *(*current)(void const *);
    7.53 +
    7.54 +    /**
    7.55 +     * Original implementation in case the function needs to be wrapped.
    7.56 +     */
    7.57 +    __attribute__ ((__nonnull__))
    7.58 +    void *(*current_impl)(void const *);
    7.59 +
    7.60 +    /**
    7.61 +     * Advances the iterator.
    7.62 +     *
    7.63 +     * When valid returns false, the behavior of this function is undefined.
    7.64 +     */
    7.65 +    __attribute__ ((__nonnull__))
    7.66 +    void (*next)(void *);
    7.67 +    /**
    7.68 +     * Indicates whether this iterator may remove elements.
    7.69 +     */
    7.70 +    bool mutating;
    7.71 +    /**
    7.72 +     * Internal flag for removing the current element when advancing.
    7.73 +     */
    7.74      bool remove;
    7.75 +};
    7.76 +
    7.77 +/**
    7.78 + * Declares base attributes for an iterator.
    7.79 + */
    7.80 +#define CX_ITERATOR_BASE struct cx_iterator_base_s base
    7.81  
    7.82  /**
    7.83   * Internal iterator struct - use CxIterator.
    7.84   */
    7.85  struct cx_iterator_s {
    7.86 -    CX_ITERATOR_BASE
    7.87 +    CX_ITERATOR_BASE;
    7.88  
    7.89      /**
    7.90       * Handle for the current element.
    7.91 @@ -157,7 +166,7 @@
    7.92   * @param iter the iterator
    7.93   * @return true iff the iterator points to valid data
    7.94   */
    7.95 -#define cxIteratorValid(iter) (iter).valid(&(iter))
    7.96 +#define cxIteratorValid(iter) (iter).base.valid(&(iter))
    7.97  
    7.98  /**
    7.99   * Returns a pointer to the current element.
   7.100 @@ -167,21 +176,21 @@
   7.101   * @param iter the iterator
   7.102   * @return a pointer to the current element
   7.103   */
   7.104 -#define cxIteratorCurrent(iter) (iter).current(&iter)
   7.105 +#define cxIteratorCurrent(iter) (iter).base.current(&iter)
   7.106  
   7.107  /**
   7.108   * Advances the iterator to the next element.
   7.109   *
   7.110   * @param iter the iterator
   7.111   */
   7.112 -#define cxIteratorNext(iter) (iter).next(&iter)
   7.113 +#define cxIteratorNext(iter) (iter).base.next(&iter)
   7.114  
   7.115  /**
   7.116   * Flags the current element for removal, if this iterator is mutating.
   7.117   *
   7.118   * @param iter the iterator
   7.119   */
   7.120 -#define cxIteratorFlagRemoval(iter) (iter).remove |= (iter).mutating
   7.121 +#define cxIteratorFlagRemoval(iter) (iter).base.remove |= (iter).base.mutating
   7.122  
   7.123  /**
   7.124   * Loops over an iterator.
     8.1 --- a/src/cx/list.h	Thu May 23 19:29:14 2024 +0200
     8.2 +++ b/src/cx/list.h	Thu May 23 20:29:28 2024 +0200
     8.3 @@ -52,7 +52,7 @@
     8.4   * Structure for holding the base data of a list.
     8.5   */
     8.6  struct cx_list_s {
     8.7 -    CX_COLLECTION_MEMBERS
     8.8 +    CX_COLLECTION_BASE;
     8.9      /**
    8.10       * The list class definition.
    8.11       */
    8.12 @@ -213,7 +213,7 @@
    8.13   */
    8.14  __attribute__((__nonnull__))
    8.15  static inline bool cxListIsStoringPointers(CxList const *list) {
    8.16 -    return list->store_pointer;
    8.17 +    return list->base.store_pointer;
    8.18  }
    8.19  
    8.20  /**
    8.21 @@ -224,7 +224,7 @@
    8.22   */
    8.23  __attribute__((__nonnull__))
    8.24  static inline size_t cxListSize(CxList const *list) {
    8.25 -    return list->size;
    8.26 +    return list->base.size;
    8.27  }
    8.28  
    8.29  /**
    8.30 @@ -240,7 +240,7 @@
    8.31          CxList *list,
    8.32          void const *elem
    8.33  ) {
    8.34 -    return list->cl->insert_element(list, list->size, elem);
    8.35 +    return list->cl->insert_element(list, list->base.size, elem);
    8.36  }
    8.37  
    8.38  /**
    8.39 @@ -265,7 +265,7 @@
    8.40          void const *array,
    8.41          size_t n
    8.42  ) {
    8.43 -    return list->cl->insert_array(list, list->size, array, n);
    8.44 +    return list->cl->insert_array(list, list->base.size, array, n);
    8.45  }
    8.46  
    8.47  /**
    8.48 @@ -547,7 +547,7 @@
    8.49   */
    8.50  __attribute__((__nonnull__, __warn_unused_result__))
    8.51  static inline CxIterator cxListBackwardsIterator(CxList const *list) {
    8.52 -    return list->cl->iterator(list, list->size - 1, true);
    8.53 +    return list->cl->iterator(list, list->base.size - 1, true);
    8.54  }
    8.55  
    8.56  /**
    8.57 @@ -562,7 +562,7 @@
    8.58   */
    8.59  __attribute__((__nonnull__, __warn_unused_result__))
    8.60  static inline CxIterator cxListMutBackwardsIterator(CxList *list) {
    8.61 -    return cxListMutBackwardsIteratorAt(list, list->size - 1);
    8.62 +    return cxListMutBackwardsIteratorAt(list, list->base.size - 1);
    8.63  }
    8.64  
    8.65  /**
     9.1 --- a/src/cx/map.h	Thu May 23 19:29:14 2024 +0200
     9.2 +++ b/src/cx/map.h	Thu May 23 20:29:28 2024 +0200
     9.3 @@ -56,7 +56,10 @@
     9.4  
     9.5  /** Structure for the UCX map. */
     9.6  struct cx_map_s {
     9.7 -    CX_COLLECTION_MEMBERS
     9.8 +    /**
     9.9 +     * Base attributes.
    9.10 +     */
    9.11 +    CX_COLLECTION_BASE;
    9.12      /** The map class definition. */
    9.13      cx_map_class *cl;
    9.14  };
    9.15 @@ -163,7 +166,7 @@
    9.16   */
    9.17  __attribute__((__nonnull__))
    9.18  static inline void cxMapStoreObjects(CxMap *map) {
    9.19 -    map->store_pointer = false;
    9.20 +    map->base.store_pointer = false;
    9.21  }
    9.22  
    9.23  /**
    9.24 @@ -180,8 +183,8 @@
    9.25   */
    9.26  __attribute__((__nonnull__))
    9.27  static inline void cxMapStorePointers(CxMap *map) {
    9.28 -    map->store_pointer = true;
    9.29 -    map->item_size = sizeof(void *);
    9.30 +    map->base.store_pointer = true;
    9.31 +    map->base.item_size = sizeof(void *);
    9.32  }
    9.33  
    9.34  
    9.35 @@ -1050,7 +1053,7 @@
    9.36          CxMap *map,
    9.37          CxHashKey key
    9.38  ) {
    9.39 -    return map->cl->remove(map, key, !map->store_pointer);
    9.40 +    return map->cl->remove(map, key, !map->base.store_pointer);
    9.41  }
    9.42  
    9.43  /**
    9.44 @@ -1066,7 +1069,7 @@
    9.45          CxMap *map,
    9.46          cxstring key
    9.47  ) {
    9.48 -    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
    9.49 +    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->base.store_pointer);
    9.50  }
    9.51  
    9.52  /**
    9.53 @@ -1082,7 +1085,7 @@
    9.54          CxMap *map,
    9.55          cxmutstr key
    9.56  ) {
    9.57 -    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
    9.58 +    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->base.store_pointer);
    9.59  }
    9.60  
    9.61  /**
    9.62 @@ -1098,7 +1101,7 @@
    9.63          CxMap *map,
    9.64          char const *key
    9.65  ) {
    9.66 -    return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer);
    9.67 +    return map->cl->remove(map, cx_hash_key_str(key), !map->base.store_pointer);
    9.68  }
    9.69  
    9.70  /**
    10.1 --- a/src/cx/tree.h	Thu May 23 19:29:14 2024 +0200
    10.2 +++ b/src/cx/tree.h	Thu May 23 20:29:28 2024 +0200
    10.3 @@ -58,7 +58,10 @@
    10.4   * @see CxIterator
    10.5   */
    10.6  typedef struct cx_tree_iterator_s {
    10.7 -    CX_ITERATOR_BASE
    10.8 +    /**
    10.9 +     * Base members.
   10.10 +     */
   10.11 +    CX_ITERATOR_BASE;
   10.12      /**
   10.13       * Indicates whether the subtree below the current node shall be skipped.
   10.14       */
   10.15 @@ -154,7 +157,10 @@
   10.16   * @see CxIterator
   10.17   */
   10.18  typedef struct cx_tree_visitor_s {
   10.19 -    CX_ITERATOR_BASE
   10.20 +    /**
   10.21 +     * Base members.
   10.22 +     */
   10.23 +    CX_ITERATOR_BASE;
   10.24      /**
   10.25       * Indicates whether the subtree below the current node shall be skipped.
   10.26       */
    11.1 --- a/src/hash_map.c	Thu May 23 19:29:14 2024 +0200
    11.2 +++ b/src/hash_map.c	Thu May 23 20:29:28 2024 +0200
    11.3 @@ -53,9 +53,9 @@
    11.4                  // invoke the destructor
    11.5                  cx_invoke_destructor(map, elem->data);
    11.6                  // free the key data
    11.7 -                cxFree(map->allocator, (void *) elem->key.data);
    11.8 +                cxFree(map->base.allocator, (void *) elem->key.data);
    11.9                  // free the node
   11.10 -                cxFree(map->allocator, elem);
   11.11 +                cxFree(map->base.allocator, elem);
   11.12                  // proceed
   11.13                  elem = next;
   11.14              } while (elem != NULL);
   11.15 @@ -64,7 +64,7 @@
   11.16              hash_map->buckets[i] = NULL;
   11.17          }
   11.18      }
   11.19 -    map->size = 0;
   11.20 +    map->base.size = 0;
   11.21  }
   11.22  
   11.23  static void cx_hash_map_destructor(struct cx_map_s *map) {
   11.24 @@ -72,10 +72,10 @@
   11.25  
   11.26      // free the buckets
   11.27      cx_hash_map_clear(map);
   11.28 -    cxFree(map->allocator, hash_map->buckets);
   11.29 +    cxFree(map->base.allocator, hash_map->buckets);
   11.30  
   11.31      // free the map structure
   11.32 -    cxFree(map->allocator, map);
   11.33 +    cxFree(map->base.allocator, map);
   11.34  }
   11.35  
   11.36  static int cx_hash_map_put(
   11.37 @@ -84,7 +84,7 @@
   11.38          void *value
   11.39  ) {
   11.40      struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
   11.41 -    CxAllocator const *allocator = map->allocator;
   11.42 +    CxAllocator const *allocator = map->base.allocator;
   11.43  
   11.44      unsigned hash = key.hash;
   11.45      if (hash == 0) {
   11.46 @@ -104,26 +104,26 @@
   11.47      if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len &&
   11.48          memcmp(elm->key.data, key.data, key.len) == 0) {
   11.49          // overwrite existing element
   11.50 -        if (map->store_pointer) {
   11.51 +        if (map->base.store_pointer) {
   11.52              memcpy(elm->data, &value, sizeof(void *));
   11.53          } else {
   11.54 -            memcpy(elm->data, value, map->item_size);
   11.55 +            memcpy(elm->data, value, map->base.item_size);
   11.56          }
   11.57      } else {
   11.58          // allocate new element
   11.59          struct cx_hash_map_element_s *e = cxMalloc(
   11.60                  allocator,
   11.61 -                sizeof(struct cx_hash_map_element_s) + map->item_size
   11.62 +                sizeof(struct cx_hash_map_element_s) + map->base.item_size
   11.63          );
   11.64          if (e == NULL) {
   11.65              return -1;
   11.66          }
   11.67  
   11.68          // write the value
   11.69 -        if (map->store_pointer) {
   11.70 +        if (map->base.store_pointer) {
   11.71              memcpy(e->data, &value, sizeof(void *));
   11.72          } else {
   11.73 -            memcpy(e->data, value, map->item_size);
   11.74 +            memcpy(e->data, value, map->base.item_size);
   11.75          }
   11.76  
   11.77          // copy the key
   11.78 @@ -145,7 +145,7 @@
   11.79          e->next = elm;
   11.80  
   11.81          // increase the size
   11.82 -        map->size++;
   11.83 +        map->base.size++;
   11.84      }
   11.85  
   11.86      return 0;
   11.87 @@ -164,10 +164,10 @@
   11.88          prev->next = elm->next;
   11.89      }
   11.90      // free element
   11.91 -    cxFree(hash_map->base.allocator, (void *) elm->key.data);
   11.92 -    cxFree(hash_map->base.allocator, elm);
   11.93 +    cxFree(hash_map->base.base.allocator, (void *) elm->key.data);
   11.94 +    cxFree(hash_map->base.base.allocator, elm);
   11.95      // decrease size
   11.96 -    hash_map->base.size--;
   11.97 +    hash_map->base.base.size--;
   11.98  }
   11.99  
  11.100  /**
  11.101 @@ -203,7 +203,7 @@
  11.102                  if (destroy) {
  11.103                      cx_invoke_destructor(map, elm->data);
  11.104                  } else {
  11.105 -                    if (map->store_pointer) {
  11.106 +                    if (map->base.store_pointer) {
  11.107                          data = *(void **) elm->data;
  11.108                      } else {
  11.109                          data = elm->data;
  11.110 @@ -254,7 +254,7 @@
  11.111      struct cx_iterator_s const *iter = it;
  11.112      struct cx_hash_map_s const *map = iter->src_handle.c;
  11.113      struct cx_hash_map_element_s *elm = iter->elem_handle;
  11.114 -    if (map->base.store_pointer) {
  11.115 +    if (map->base.base.store_pointer) {
  11.116          return *(void **) elm->data;
  11.117      } else {
  11.118          return elm->data;
  11.119 @@ -272,10 +272,10 @@
  11.120      struct cx_hash_map_s *map = iter->src_handle.m;
  11.121  
  11.122      // remove current element, if asked
  11.123 -    if (iter->remove) {
  11.124 +    if (iter->base.remove) {
  11.125  
  11.126          // clear the flag
  11.127 -        iter->remove = false;
  11.128 +        iter->base.remove = false;
  11.129  
  11.130          // determine the next element
  11.131          struct cx_hash_map_element_s *next = elm->next;
  11.132 @@ -315,7 +315,7 @@
  11.133          iter->kv_data.value = NULL;
  11.134      } else {
  11.135          iter->kv_data.key = &elm->key;
  11.136 -        if (map->base.store_pointer) {
  11.137 +        if (map->base.base.store_pointer) {
  11.138              iter->kv_data.value = *(void **) elm->data;
  11.139          } else {
  11.140              iter->kv_data.value = elm->data;
  11.141 @@ -330,34 +330,34 @@
  11.142      CxIterator iter;
  11.143  
  11.144      iter.src_handle.c = map;
  11.145 -    iter.elem_count = map->size;
  11.146 +    iter.elem_count = map->base.size;
  11.147  
  11.148      switch (type) {
  11.149          case CX_MAP_ITERATOR_PAIRS:
  11.150              iter.elem_size = sizeof(CxMapEntry);
  11.151 -            iter.current = cx_hash_map_iter_current_entry;
  11.152 +            iter.base.current = cx_hash_map_iter_current_entry;
  11.153              break;
  11.154          case CX_MAP_ITERATOR_KEYS:
  11.155              iter.elem_size = sizeof(CxHashKey);
  11.156 -            iter.current = cx_hash_map_iter_current_key;
  11.157 +            iter.base.current = cx_hash_map_iter_current_key;
  11.158              break;
  11.159          case CX_MAP_ITERATOR_VALUES:
  11.160 -            iter.elem_size = map->item_size;
  11.161 -            iter.current = cx_hash_map_iter_current_value;
  11.162 +            iter.elem_size = map->base.item_size;
  11.163 +            iter.base.current = cx_hash_map_iter_current_value;
  11.164              break;
  11.165          default:
  11.166              assert(false);
  11.167      }
  11.168  
  11.169 -    iter.valid = cx_hash_map_iter_valid;
  11.170 -    iter.next = cx_hash_map_iter_next;
  11.171 -    iter.remove = false;
  11.172 -    iter.mutating = false;
  11.173 +    iter.base.valid = cx_hash_map_iter_valid;
  11.174 +    iter.base.next = cx_hash_map_iter_next;
  11.175 +    iter.base.remove = false;
  11.176 +    iter.base.mutating = false;
  11.177  
  11.178      iter.slot = 0;
  11.179      iter.index = 0;
  11.180  
  11.181 -    if (map->size > 0) {
  11.182 +    if (map->base.size > 0) {
  11.183          struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
  11.184          struct cx_hash_map_element_s *elm = hash_map->buckets[0];
  11.185          while (elm == NULL) {
  11.186 @@ -365,7 +365,7 @@
  11.187          }
  11.188          iter.elem_handle = elm;
  11.189          iter.kv_data.key = &elm->key;
  11.190 -        if (map->store_pointer) {
  11.191 +        if (map->base.store_pointer) {
  11.192              iter.kv_data.value = *(void **) elm->data;
  11.193          } else {
  11.194              iter.kv_data.value = elm->data;
  11.195 @@ -413,14 +413,14 @@
  11.196  
  11.197      // initialize base members
  11.198      map->base.cl = &cx_hash_map_class;
  11.199 -    map->base.allocator = allocator;
  11.200 +    map->base.base.allocator = allocator;
  11.201  
  11.202      if (itemsize > 0) {
  11.203 -        map->base.store_pointer = false;
  11.204 -        map->base.item_size = itemsize;
  11.205 +        map->base.base.store_pointer = false;
  11.206 +        map->base.base.item_size = itemsize;
  11.207      } else {
  11.208 -        map->base.store_pointer = true;
  11.209 -        map->base.item_size = sizeof(void *);
  11.210 +        map->base.base.store_pointer = true;
  11.211 +        map->base.base.item_size = sizeof(void *);
  11.212      }
  11.213  
  11.214      return (CxMap *) map;
  11.215 @@ -428,11 +428,11 @@
  11.216  
  11.217  int cxMapRehash(CxMap *map) {
  11.218      struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
  11.219 -    if (map->size > ((hash_map->bucket_count * 3) >> 2)) {
  11.220 +    if (map->base.size > ((hash_map->bucket_count * 3) >> 2)) {
  11.221  
  11.222 -        size_t new_bucket_count = (map->size * 5) >> 1;
  11.223 +        size_t new_bucket_count = (map->base.size * 5) >> 1;
  11.224          struct cx_hash_map_element_s **new_buckets = cxCalloc(
  11.225 -                map->allocator,
  11.226 +                map->base.allocator,
  11.227                  new_bucket_count, sizeof(struct cx_hash_map_element_s *)
  11.228          );
  11.229  
  11.230 @@ -472,7 +472,7 @@
  11.231  
  11.232          // assign result to the map
  11.233          hash_map->bucket_count = new_bucket_count;
  11.234 -        cxFree(map->allocator, hash_map->buckets);
  11.235 +        cxFree(map->base.allocator, hash_map->buckets);
  11.236          hash_map->buckets = new_buckets;
  11.237      }
  11.238      return 0;
    12.1 --- a/src/iterator.c	Thu May 23 19:29:14 2024 +0200
    12.2 +++ b/src/iterator.c	Thu May 23 20:29:28 2024 +0200
    12.3 @@ -42,8 +42,8 @@
    12.4  
    12.5  static void cx_iter_next_fast(void *it) {
    12.6      struct cx_iterator_s *iter = it;
    12.7 -    if (iter->remove) {
    12.8 -        iter->remove = false;
    12.9 +    if (iter->base.remove) {
   12.10 +        iter->base.remove = false;
   12.11          iter->elem_count--;
   12.12          // only move the last element when we are not currently aiming
   12.13          // at the last element already
   12.14 @@ -60,8 +60,8 @@
   12.15  
   12.16  static void cx_iter_next_slow(void *it) {
   12.17      struct cx_iterator_s *iter = it;
   12.18 -    if (iter->remove) {
   12.19 -        iter->remove = false;
   12.20 +    if (iter->base.remove) {
   12.21 +        iter->base.remove = false;
   12.22          iter->elem_count--;
   12.23  
   12.24          // number of elements to move
   12.25 @@ -92,11 +92,11 @@
   12.26      iter.elem_handle = array;
   12.27      iter.elem_size = elem_size;
   12.28      iter.elem_count = array == NULL ? 0 : elem_count;
   12.29 -    iter.valid = cx_iter_valid;
   12.30 -    iter.current = cx_iter_current;
   12.31 -    iter.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast;
   12.32 -    iter.remove = false;
   12.33 -    iter.mutating = true;
   12.34 +    iter.base.valid = cx_iter_valid;
   12.35 +    iter.base.current = cx_iter_current;
   12.36 +    iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast;
   12.37 +    iter.base.remove = false;
   12.38 +    iter.base.mutating = true;
   12.39  
   12.40      return iter;
   12.41  }
   12.42 @@ -107,6 +107,6 @@
   12.43          size_t elem_count
   12.44  ) {
   12.45      CxIterator iter = cxMutIterator((void*)array, elem_size, elem_count, false);
   12.46 -    iter.mutating = false;
   12.47 +    iter.base.mutating = false;
   12.48      return iter;
   12.49  }
    13.1 --- a/src/linked_list.c	Thu May 23 19:29:14 2024 +0200
    13.2 +++ b/src/linked_list.c	Thu May 23 20:29:28 2024 +0200
    13.3 @@ -501,10 +501,10 @@
    13.4          cx_linked_list const *list,
    13.5          size_t index
    13.6  ) {
    13.7 -    if (index >= list->base.size) {
    13.8 +    if (index >= list->base.base.size) {
    13.9          return NULL;
   13.10 -    } else if (index > list->base.size / 2) {
   13.11 -        return cx_linked_list_at(list->end, list->base.size - 1, CX_LL_LOC_PREV, index);
   13.12 +    } else if (index > list->base.base.size / 2) {
   13.13 +        return cx_linked_list_at(list->end, list->base.base.size - 1, CX_LL_LOC_PREV, index);
   13.14      } else {
   13.15          return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index);
   13.16      }
   13.17 @@ -517,15 +517,15 @@
   13.18  ) {
   13.19  
   13.20      // create the new new_node
   13.21 -    cx_linked_list_node *new_node = cxMalloc(list->allocator,
   13.22 -                                             sizeof(cx_linked_list_node) + list->item_size);
   13.23 +    cx_linked_list_node *new_node = cxMalloc(list->base.allocator,
   13.24 +                                             sizeof(cx_linked_list_node) + list->base.item_size);
   13.25  
   13.26      // sortir if failed
   13.27      if (new_node == NULL) return 1;
   13.28  
   13.29      // initialize new new_node
   13.30      new_node->prev = new_node->next = NULL;
   13.31 -    memcpy(new_node->payload, elem, list->item_size);
   13.32 +    memcpy(new_node->payload, elem, list->base.item_size);
   13.33  
   13.34      // insert
   13.35      cx_linked_list *ll = (cx_linked_list *) list;
   13.36 @@ -536,7 +536,7 @@
   13.37      );
   13.38  
   13.39      // increase the size and return
   13.40 -    list->size++;
   13.41 +    list->base.size++;
   13.42      return 0;
   13.43  }
   13.44  
   13.45 @@ -547,7 +547,7 @@
   13.46          size_t n
   13.47  ) {
   13.48      // out-of bounds and corner case check
   13.49 -    if (index > list->size || n == 0) return 0;
   13.50 +    if (index > list->base.size || n == 0) return 0;
   13.51  
   13.52      // find position efficiently
   13.53      cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
   13.54 @@ -566,7 +566,7 @@
   13.55      // we can add the remaining nodes and immedately advance to the inserted node
   13.56      char const *source = array;
   13.57      for (size_t i = 1; i < n; i++) {
   13.58 -        source += list->item_size;
   13.59 +        source += list->base.item_size;
   13.60          if (0 != cx_ll_insert_at(list, node, source)) {
   13.61              return i;
   13.62          }
   13.63 @@ -601,27 +601,27 @@
   13.64                            CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
   13.65  
   13.66      // adjust size
   13.67 -    list->size--;
   13.68 +    list->base.size--;
   13.69  
   13.70      // free and return
   13.71 -    cxFree(list->allocator, node);
   13.72 +    cxFree(list->base.allocator, node);
   13.73  
   13.74      return 0;
   13.75  }
   13.76  
   13.77  static void cx_ll_clear(struct cx_list_s *list) {
   13.78 -    if (list->size == 0) return;
   13.79 +    if (list->base.size == 0) return;
   13.80  
   13.81      cx_linked_list *ll = (cx_linked_list *) list;
   13.82      cx_linked_list_node *node = ll->begin;
   13.83      while (node != NULL) {
   13.84          cx_invoke_destructor(list, node->payload);
   13.85          cx_linked_list_node *next = node->next;
   13.86 -        cxFree(list->allocator, node);
   13.87 +        cxFree(list->base.allocator, node);
   13.88          node = next;
   13.89      }
   13.90      ll->begin = ll->end = NULL;
   13.91 -    list->size = 0;
   13.92 +    list->base.size = 0;
   13.93  }
   13.94  
   13.95  #ifndef CX_LINKED_LIST_SWAP_SBO_SIZE
   13.96 @@ -634,12 +634,12 @@
   13.97          size_t i,
   13.98          size_t j
   13.99  ) {
  13.100 -    if (i >= list->size || j >= list->size) return 1;
  13.101 +    if (i >= list->base.size || j >= list->base.size) return 1;
  13.102      if (i == j) return 0;
  13.103  
  13.104      // perform an optimized search that finds both elements in one run
  13.105      cx_linked_list *ll = (cx_linked_list *) list;
  13.106 -    size_t mid = list->size / 2;
  13.107 +    size_t mid = list->base.size / 2;
  13.108      size_t left, right;
  13.109      if (i < j) {
  13.110          left = i;
  13.111 @@ -671,7 +671,7 @@
  13.112          // chose the closest to begin / end
  13.113          size_t closest;
  13.114          size_t other;
  13.115 -        size_t diff2boundary = list->size - right - 1;
  13.116 +        size_t diff2boundary = list->base.size - right - 1;
  13.117          if (left <= diff2boundary) {
  13.118              closest = left;
  13.119              other = right;
  13.120 @@ -707,7 +707,7 @@
  13.121          }
  13.122      }
  13.123  
  13.124 -    if (list->item_size > CX_LINKED_LIST_SWAP_SBO_SIZE) {
  13.125 +    if (list->base.item_size > CX_LINKED_LIST_SWAP_SBO_SIZE) {
  13.126          cx_linked_list_node *prev = nleft->prev;
  13.127          cx_linked_list_node *next = nright->next;
  13.128          cx_linked_list_node *midstart = nleft->next;
  13.129 @@ -739,9 +739,9 @@
  13.130      } else {
  13.131          // swap payloads to avoid relinking
  13.132          char buf[CX_LINKED_LIST_SWAP_SBO_SIZE];
  13.133 -        memcpy(buf, nleft->payload, list->item_size);
  13.134 -        memcpy(nleft->payload, nright->payload, list->item_size);
  13.135 -        memcpy(nright->payload, buf, list->item_size);
  13.136 +        memcpy(buf, nleft->payload, list->base.item_size);
  13.137 +        memcpy(nleft->payload, nright->payload, list->base.item_size);
  13.138 +        memcpy(nright->payload, buf, list->base.item_size);
  13.139      }
  13.140  
  13.141      return 0;
  13.142 @@ -768,21 +768,21 @@
  13.143                  (void **) &node,
  13.144                  ll->begin,
  13.145                  CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
  13.146 -                list->cmpfunc, elem
  13.147 +                list->base.cmpfunc, elem
  13.148          );
  13.149          if (node != NULL) {
  13.150              cx_invoke_destructor(list, node->payload);
  13.151              cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
  13.152                                    CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
  13.153 -            list->size--;
  13.154 -            cxFree(list->allocator, node);
  13.155 +            list->base.size--;
  13.156 +            cxFree(list->base.allocator, node);
  13.157          }
  13.158          return index;
  13.159      } else {
  13.160          return cx_linked_list_find(
  13.161                  ((cx_linked_list *) list)->begin,
  13.162                  CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
  13.163 -                list->cmpfunc, elem
  13.164 +                list->base.cmpfunc, elem
  13.165          );
  13.166      }
  13.167  }
  13.168 @@ -791,7 +791,7 @@
  13.169      cx_linked_list *ll = (cx_linked_list *) list;
  13.170      cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end,
  13.171                          CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
  13.172 -                        list->cmpfunc);
  13.173 +                        list->base.cmpfunc);
  13.174  }
  13.175  
  13.176  static void cx_ll_reverse(struct cx_list_s *list) {
  13.177 @@ -807,7 +807,7 @@
  13.178      cx_linked_list *right = (cx_linked_list *) other;
  13.179      return cx_linked_list_compare(left->begin, right->begin,
  13.180                                    CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
  13.181 -                                  list->cmpfunc);
  13.182 +                                  list->base.cmpfunc);
  13.183  }
  13.184  
  13.185  static bool cx_ll_iter_valid(void const *it) {
  13.186 @@ -817,8 +817,8 @@
  13.187  
  13.188  static void cx_ll_iter_next(void *it) {
  13.189      struct cx_iterator_s *iter = it;
  13.190 -    if (iter->remove) {
  13.191 -        iter->remove = false;
  13.192 +    if (iter->base.remove) {
  13.193 +        iter->base.remove = false;
  13.194          struct cx_list_s *list = iter->src_handle.m;
  13.195          cx_linked_list *ll = iter->src_handle.m;
  13.196          cx_linked_list_node *node = iter->elem_handle;
  13.197 @@ -826,8 +826,8 @@
  13.198          cx_invoke_destructor(list, node->payload);
  13.199          cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
  13.200                                CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
  13.201 -        list->size--;
  13.202 -        cxFree(list->allocator, node);
  13.203 +        list->base.size--;
  13.204 +        cxFree(list->base.allocator, node);
  13.205      } else {
  13.206          iter->index++;
  13.207          cx_linked_list_node *node = iter->elem_handle;
  13.208 @@ -837,8 +837,8 @@
  13.209  
  13.210  static void cx_ll_iter_prev(void *it) {
  13.211      struct cx_iterator_s *iter = it;
  13.212 -    if (iter->remove) {
  13.213 -        iter->remove = false;
  13.214 +    if (iter->base.remove) {
  13.215 +        iter->base.remove = false;
  13.216          struct cx_list_s *list = iter->src_handle.m;
  13.217          cx_linked_list *ll = iter->src_handle.m;
  13.218          cx_linked_list_node *node = iter->elem_handle;
  13.219 @@ -847,8 +847,8 @@
  13.220          cx_invoke_destructor(list, node->payload);
  13.221          cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
  13.222                                CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
  13.223 -        list->size--;
  13.224 -        cxFree(list->allocator, node);
  13.225 +        list->base.size--;
  13.226 +        cxFree(list->base.allocator, node);
  13.227      } else {
  13.228          iter->index--;
  13.229          cx_linked_list_node *node = iter->elem_handle;
  13.230 @@ -871,13 +871,13 @@
  13.231      iter.index = index;
  13.232      iter.src_handle.c = list;
  13.233      iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index);
  13.234 -    iter.elem_size = list->item_size;
  13.235 -    iter.elem_count = list->size;
  13.236 -    iter.valid = cx_ll_iter_valid;
  13.237 -    iter.current = cx_ll_iter_current;
  13.238 -    iter.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next;
  13.239 -    iter.mutating = false;
  13.240 -    iter.remove = false;
  13.241 +    iter.elem_size = list->base.item_size;
  13.242 +    iter.elem_count = list->base.size;
  13.243 +    iter.base.valid = cx_ll_iter_valid;
  13.244 +    iter.base.current = cx_ll_iter_current;
  13.245 +    iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next;
  13.246 +    iter.base.mutating = false;
  13.247 +    iter.base.remove = false;
  13.248      return iter;
  13.249  }
  13.250  
  13.251 @@ -895,8 +895,8 @@
  13.252          iter->index += prepend * (0 == result);
  13.253          return result;
  13.254      } else {
  13.255 -        int result = cx_ll_insert_element(list, list->size, elem);
  13.256 -        iter->index = list->size;
  13.257 +        int result = cx_ll_insert_element(list, list->base.size, elem);
  13.258 +        iter->index = list->base.size;
  13.259          return result;
  13.260      }
  13.261  }
  13.262 @@ -908,11 +908,11 @@
  13.263      while (node) {
  13.264          cx_invoke_destructor(list, node->payload);
  13.265          void *next = node->next;
  13.266 -        cxFree(list->allocator, node);
  13.267 +        cxFree(list->base.allocator, node);
  13.268          node = next;
  13.269      }
  13.270  
  13.271 -    cxFree(list->allocator, list);
  13.272 +    cxFree(list->base.allocator, list);
  13.273  }
  13.274  
  13.275  static cx_list_class cx_linked_list_class = {
  13.276 @@ -944,13 +944,13 @@
  13.277      if (list == NULL) return NULL;
  13.278  
  13.279      list->base.cl = &cx_linked_list_class;
  13.280 -    list->base.allocator = allocator;
  13.281 +    list->base.base.allocator = allocator;
  13.282  
  13.283      if (item_size > 0) {
  13.284 -        list->base.item_size = item_size;
  13.285 -        list->base.cmpfunc = comparator;
  13.286 +        list->base.base.item_size = item_size;
  13.287 +        list->base.base.cmpfunc = comparator;
  13.288      } else {
  13.289 -        list->base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator;
  13.290 +        list->base.base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator;
  13.291          cxListStorePointers((CxList *) list);
  13.292      }
  13.293  
    14.1 --- a/src/list.c	Thu May 23 19:29:14 2024 +0200
    14.2 +++ b/src/list.c	Thu May 23 20:29:28 2024 +0200
    14.3 @@ -47,14 +47,14 @@
    14.4  
    14.5  static void cx_pl_hack_cmpfunc(struct cx_list_s const *list) {
    14.6      // cast away const - this is the hacky thing
    14.7 -    struct cx_list_s *l = (struct cx_list_s *) list;
    14.8 +    struct cx_collection_s *l = (struct cx_collection_s*) &list->base;
    14.9      cx_pl_cmpfunc_impl = l->cmpfunc;
   14.10      l->cmpfunc = cx_pl_cmpfunc;
   14.11  }
   14.12  
   14.13  static void cx_pl_unhack_cmpfunc(struct cx_list_s const *list) {
   14.14      // cast away const - this is the hacky thing
   14.15 -    struct cx_list_s *l = (struct cx_list_s *) list;
   14.16 +    struct cx_collection_s *l = (struct cx_collection_s*) &list->base;
   14.17      l->cmpfunc = cx_pl_cmpfunc_impl;
   14.18  }
   14.19  
   14.20 @@ -148,7 +148,7 @@
   14.21  
   14.22  static void *cx_pl_iter_current(void const *it) {
   14.23      struct cx_iterator_s const *iter = it;
   14.24 -    void **ptr = iter->current_impl(it);
   14.25 +    void **ptr = iter->base.current_impl(it);
   14.26      return ptr == NULL ? NULL : *ptr;
   14.27  }
   14.28  
   14.29 @@ -158,8 +158,8 @@
   14.30          bool backwards
   14.31  ) {
   14.32      struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards);
   14.33 -    iter.current_impl = iter.current;
   14.34 -    iter.current = cx_pl_iter_current;
   14.35 +    iter.base.current_impl = iter.base.current;
   14.36 +    iter.base.current = cx_pl_iter_current;
   14.37      return iter;
   14.38  }
   14.39  
   14.40 @@ -180,7 +180,7 @@
   14.41  };
   14.42  
   14.43  void cxListStoreObjects(CxList *list) {
   14.44 -    list->store_pointer = false;
   14.45 +    list->base.store_pointer = false;
   14.46      if (list->climpl != NULL) {
   14.47          list->cl = list->climpl;
   14.48          list->climpl = NULL;
   14.49 @@ -188,8 +188,8 @@
   14.50  }
   14.51  
   14.52  void cxListStorePointers(CxList *list) {
   14.53 -    list->item_size = sizeof(void *);
   14.54 -    list->store_pointer = true;
   14.55 +    list->base.item_size = sizeof(void *);
   14.56 +    list->base.store_pointer = true;
   14.57      list->climpl = list->cl;
   14.58      list->cl = &cx_pointer_list_class;
   14.59  }
   14.60 @@ -221,7 +221,7 @@
   14.61          __attribute__((__unused__)) struct cx_list_s const *list,
   14.62          struct cx_list_s const *other
   14.63  ) {
   14.64 -    if (other->size == 0) return 0;
   14.65 +    if (other->base.size == 0) return 0;
   14.66      return -1;
   14.67  }
   14.68  
   14.69 @@ -237,7 +237,7 @@
   14.70      CxIterator iter = {0};
   14.71      iter.src_handle.c = list;
   14.72      iter.index = index;
   14.73 -    iter.valid = cx_emptyl_iter_valid;
   14.74 +    iter.base.valid = cx_emptyl_iter_valid;
   14.75      return iter;
   14.76  }
   14.77  
   14.78 @@ -258,14 +258,16 @@
   14.79  };
   14.80  
   14.81  CxList cx_empty_list = {
   14.82 -        NULL,
   14.83 -        NULL,
   14.84 -        0,
   14.85 -        0,
   14.86 -        NULL,
   14.87 -        NULL,
   14.88 -        NULL,
   14.89 -        false,
   14.90 +        {
   14.91 +                NULL,
   14.92 +                NULL,
   14.93 +                0,
   14.94 +                0,
   14.95 +                NULL,
   14.96 +                NULL,
   14.97 +                NULL,
   14.98 +                false
   14.99 +        },
  14.100          &cx_empty_list_class,
  14.101          NULL
  14.102  };
  14.103 @@ -284,7 +286,7 @@
  14.104  ) {
  14.105      if (
  14.106          // if one is storing pointers but the other is not
  14.107 -        (list->store_pointer ^ other->store_pointer) ||
  14.108 +        (list->base.store_pointer ^ other->base.store_pointer) ||
  14.109  
  14.110          // if one class is wrapped but the other is not
  14.111          ((list->climpl == NULL) ^ (other->climpl == NULL)) ||
  14.112 @@ -294,13 +296,13 @@
  14.113           (other->climpl != NULL ? other->climpl->compare : other->cl->compare))
  14.114      ) {
  14.115          // lists are definitely different - cannot use internal compare function
  14.116 -        if (list->size == other->size) {
  14.117 +        if (list->base.size == other->base.size) {
  14.118              CxIterator left = list->cl->iterator(list, 0, false);
  14.119              CxIterator right = other->cl->iterator(other, 0, false);
  14.120 -            for (size_t i = 0; i < list->size; i++) {
  14.121 +            for (size_t i = 0; i < list->base.size; i++) {
  14.122                  void *leftValue = cxIteratorCurrent(left);
  14.123                  void *rightValue = cxIteratorCurrent(right);
  14.124 -                int d = list->cmpfunc(leftValue, rightValue);
  14.125 +                int d = list->base.cmpfunc(leftValue, rightValue);
  14.126                  if (d != 0) {
  14.127                      return d;
  14.128                  }
  14.129 @@ -309,7 +311,7 @@
  14.130              }
  14.131              return 0;
  14.132          } else {
  14.133 -            return list->size < other->size ? -1 : 1;
  14.134 +            return list->base.size < other->base.size ? -1 : 1;
  14.135          }
  14.136      } else {
  14.137          // lists are compatible
  14.138 @@ -322,7 +324,7 @@
  14.139          size_t index
  14.140  ) {
  14.141      CxIterator it = list->cl->iterator(list, index, false);
  14.142 -    it.mutating = true;
  14.143 +    it.base.mutating = true;
  14.144      return it;
  14.145  }
  14.146  
  14.147 @@ -331,6 +333,6 @@
  14.148          size_t index
  14.149  ) {
  14.150      CxIterator it = list->cl->iterator(list, index, true);
  14.151 -    it.mutating = true;
  14.152 +    it.base.mutating = true;
  14.153      return it;
  14.154  }
    15.1 --- a/src/map.c	Thu May 23 19:29:14 2024 +0200
    15.2 +++ b/src/map.c	Thu May 23 20:29:28 2024 +0200
    15.3 @@ -52,7 +52,7 @@
    15.4  ) {
    15.5      CxIterator iter = {0};
    15.6      iter.src_handle.c = map;
    15.7 -    iter.valid = cx_empty_map_iter_valid;
    15.8 +    iter.base.valid = cx_empty_map_iter_valid;
    15.9      return iter;
   15.10  }
   15.11  
   15.12 @@ -66,14 +66,16 @@
   15.13  };
   15.14  
   15.15  CxMap cx_empty_map = {
   15.16 -        NULL,
   15.17 -        NULL,
   15.18 -        0,
   15.19 -        0,
   15.20 -        NULL,
   15.21 -        NULL,
   15.22 -        NULL,
   15.23 -        false,
   15.24 +        {
   15.25 +                NULL,
   15.26 +                NULL,
   15.27 +                0,
   15.28 +                0,
   15.29 +                NULL,
   15.30 +                NULL,
   15.31 +                NULL,
   15.32 +                false
   15.33 +        },
   15.34          &cx_empty_map_class
   15.35  };
   15.36  
   15.37 @@ -83,18 +85,18 @@
   15.38  
   15.39  CxIterator cxMapMutIteratorValues(CxMap *map) {
   15.40      CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
   15.41 -    it.mutating = true;
   15.42 +    it.base.mutating = true;
   15.43      return it;
   15.44  }
   15.45  
   15.46  CxIterator cxMapMutIteratorKeys(CxMap *map) {
   15.47      CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
   15.48 -    it.mutating = true;
   15.49 +    it.base.mutating = true;
   15.50      return it;
   15.51  }
   15.52  
   15.53  CxIterator cxMapMutIterator(CxMap *map) {
   15.54      CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
   15.55 -    it.mutating = true;
   15.56 +    it.base.mutating = true;
   15.57      return it;
   15.58  }
    16.1 --- a/src/tree.c	Thu May 23 19:29:14 2024 +0200
    16.2 +++ b/src/tree.c	Thu May 23 20:29:28 2024 +0200
    16.3 @@ -280,12 +280,12 @@
    16.4      iter.skip = false;
    16.5  
    16.6      // assign base iterator functions
    16.7 -    iter.mutating = false;
    16.8 -    iter.remove = false;
    16.9 -    iter.current_impl = NULL;
   16.10 -    iter.valid = cx_tree_iter_valid;
   16.11 -    iter.next = cx_tree_iter_next;
   16.12 -    iter.current = cx_tree_iter_current;
   16.13 +    iter.base.mutating = false;
   16.14 +    iter.base.remove = false;
   16.15 +    iter.base.current_impl = NULL;
   16.16 +    iter.base.valid = cx_tree_iter_valid;
   16.17 +    iter.base.next = cx_tree_iter_next;
   16.18 +    iter.base.current = cx_tree_iter_current;
   16.19  
   16.20      return iter;
   16.21  }
   16.22 @@ -389,12 +389,12 @@
   16.23      iter.queue_last = NULL;
   16.24  
   16.25      // assign base iterator functions
   16.26 -    iter.mutating = false;
   16.27 -    iter.remove = false;
   16.28 -    iter.current_impl = NULL;
   16.29 -    iter.valid = cx_tree_visitor_valid;
   16.30 -    iter.next = cx_tree_visitor_next;
   16.31 -    iter.current = cx_tree_visitor_current;
   16.32 +    iter.base.mutating = false;
   16.33 +    iter.base.remove = false;
   16.34 +    iter.base.current_impl = NULL;
   16.35 +    iter.base.valid = cx_tree_visitor_valid;
   16.36 +    iter.base.next = cx_tree_visitor_next;
   16.37 +    iter.base.current = cx_tree_visitor_current;
   16.38  
   16.39      return iter;
   16.40  }
    17.1 --- a/tests/Makefile	Thu May 23 19:29:14 2024 +0200
    17.2 +++ b/tests/Makefile	Thu May 23 20:29:28 2024 +0200
    17.3 @@ -73,8 +73,8 @@
    17.4  $(TEST_DIR)/test_hash_map$(OBJ_EXT): test_hash_map.c ../src/cx/test.h \
    17.5   util_allocator.h ../src/cx/allocator.h ../src/cx/common.h \
    17.6   ../src/cx/hash_map.h ../src/cx/map.h ../src/cx/collection.h \
    17.7 - ../src/cx/allocator.h ../src/cx/iterator.h ../src/cx/string.h \
    17.8 - ../src/cx/hash_key.h
    17.9 + ../src/cx/allocator.h ../src/cx/iterator.h ../src/cx/compare.h \
   17.10 + ../src/cx/string.h ../src/cx/hash_key.h
   17.11  	@echo "Compiling $<"
   17.12  	$(CC) -o $@ $(CFLAGS) -c $<
   17.13  
   17.14 @@ -87,7 +87,7 @@
   17.15   util_allocator.h ../src/cx/allocator.h ../src/cx/common.h \
   17.16   ../src/cx/compare.h ../src/cx/utils.h ../src/cx/array_list.h \
   17.17   ../src/cx/list.h ../src/cx/collection.h ../src/cx/allocator.h \
   17.18 - ../src/cx/iterator.h ../src/cx/linked_list.h
   17.19 + ../src/cx/iterator.h ../src/cx/compare.h ../src/cx/linked_list.h
   17.20  	@echo "Compiling $<"
   17.21  	$(CC) -o $@ $(CFLAGS) -c $<
   17.22  
    18.1 --- a/tests/test_hash_map.c	Thu May 23 19:29:14 2024 +0200
    18.2 +++ b/tests/test_hash_map.c	Thu May 23 20:29:28 2024 +0200
    18.3 @@ -42,19 +42,19 @@
    18.4          for(size_t i = 0 ; i < hmap->bucket_count ; i++) {
    18.5              CX_TEST_ASSERT(hmap->buckets[i] == NULL);
    18.6          }
    18.7 -        CX_TEST_ASSERT(map->item_size == 1);
    18.8 -        CX_TEST_ASSERT(map->size == 0);
    18.9 -        CX_TEST_ASSERT(map->allocator == allocator);
   18.10 -        CX_TEST_ASSERT(!map->store_pointer);
   18.11 -        CX_TEST_ASSERT(map->cmpfunc == NULL);
   18.12 -        CX_TEST_ASSERT(map->simple_destructor == NULL);
   18.13 -        CX_TEST_ASSERT(map->advanced_destructor == NULL);
   18.14 -        CX_TEST_ASSERT(map->destructor_data == NULL);
   18.15 +        CX_TEST_ASSERT(map->base.item_size == 1);
   18.16 +        CX_TEST_ASSERT(map->base.size == 0);
   18.17 +        CX_TEST_ASSERT(map->base.allocator == allocator);
   18.18 +        CX_TEST_ASSERT(!map->base.store_pointer);
   18.19 +        CX_TEST_ASSERT(map->base.cmpfunc == NULL);
   18.20 +        CX_TEST_ASSERT(map->base.simple_destructor == NULL);
   18.21 +        CX_TEST_ASSERT(map->base.advanced_destructor == NULL);
   18.22 +        CX_TEST_ASSERT(map->base.destructor_data == NULL);
   18.23          cxMapStorePointers(map);
   18.24 -        CX_TEST_ASSERT(map->store_pointer);
   18.25 -        CX_TEST_ASSERT(map->item_size == sizeof(void *));
   18.26 +        CX_TEST_ASSERT(map->base.store_pointer);
   18.27 +        CX_TEST_ASSERT(map->base.item_size == sizeof(void *));
   18.28          cxMapStoreObjects(map);
   18.29 -        CX_TEST_ASSERT(!map->store_pointer);
   18.30 +        CX_TEST_ASSERT(!map->base.store_pointer);
   18.31  
   18.32          cxMapDestroy(map);
   18.33          CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   18.34 @@ -73,10 +73,10 @@
   18.35          for (size_t i = 0; i < hmap->bucket_count; i++) {
   18.36              CX_TEST_ASSERT(hmap->buckets[i] == NULL);
   18.37          }
   18.38 -        CX_TEST_ASSERT(map->size == 0);
   18.39 -        CX_TEST_ASSERT(map->allocator == allocator);
   18.40 -        CX_TEST_ASSERT(map->store_pointer);
   18.41 -        CX_TEST_ASSERT(map->item_size == sizeof(void *));
   18.42 +        CX_TEST_ASSERT(map->base.size == 0);
   18.43 +        CX_TEST_ASSERT(map->base.allocator == allocator);
   18.44 +        CX_TEST_ASSERT(map->base.store_pointer);
   18.45 +        CX_TEST_ASSERT(map->base.item_size == sizeof(void *));
   18.46  
   18.47          cxMapDestroy(map);
   18.48          CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   18.49 @@ -106,7 +106,7 @@
   18.50          int result = cxMapRehash(map);
   18.51          CX_TEST_ASSERT(result == 0);
   18.52          CX_TEST_ASSERT(((struct cx_hash_map_s *)map)->bucket_count == 25);
   18.53 -        CX_TEST_ASSERT(map->size == 10);
   18.54 +        CX_TEST_ASSERT(map->base.size == 10);
   18.55  
   18.56          CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 1"), "val 1"));
   18.57          CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 2"), "val 2"));
   18.58 @@ -161,11 +161,11 @@
   18.59          cxMapPut(map, "key 2", (void *) "val 2");
   18.60          cxMapPut(map, "key 3", (void *) "val 3");
   18.61  
   18.62 -        CX_TEST_ASSERT(map->size == 3);
   18.63 +        CX_TEST_ASSERT(map->base.size == 3);
   18.64  
   18.65          cxMapClear(map);
   18.66  
   18.67 -        CX_TEST_ASSERT(map->size == 0);
   18.68 +        CX_TEST_ASSERT(map->base.size == 0);
   18.69          CX_TEST_ASSERT(cxMapGet(map, "key 1") == NULL);
   18.70          CX_TEST_ASSERT(cxMapGet(map, "key 2") == NULL);
   18.71          CX_TEST_ASSERT(cxMapGet(map, "key 3") == NULL);
   18.72 @@ -208,7 +208,7 @@
   18.73  
   18.74          // remove a string
   18.75          cxMapRemove(map, "s2");
   18.76 -        CX_TEST_ASSERT(map->size == 3);
   18.77 +        CX_TEST_ASSERT(map->base.size == 3);
   18.78  
   18.79          // iterate
   18.80          bool s3found = false, s4found = false, s5found = false;
   18.81 @@ -244,8 +244,8 @@
   18.82          cx_foreach(CxMapEntry*, entry, iter) {
   18.83              if (((char const *)entry->key->data)[4] % 2 == 1) cxIteratorFlagRemoval(iter);
   18.84          }
   18.85 -        CX_TEST_ASSERT(map->size == 3);
   18.86 -        CX_TEST_ASSERT(iter.index == map->size);
   18.87 +        CX_TEST_ASSERT(map->base.size == 3);
   18.88 +        CX_TEST_ASSERT(iter.index == map->base.size);
   18.89  
   18.90          CX_TEST_ASSERT(cxMapGet(map, "key 1") == NULL);
   18.91          CX_TEST_ASSERT(cxMapGet(map, "key 2") != NULL);
   18.92 @@ -348,7 +348,7 @@
   18.93      CxAllocator *allocator = &talloc.base;
   18.94      CX_TEST_DO {
   18.95          CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
   18.96 -        map->simple_destructor = test_simple_destructor;
   18.97 +        map->base.simple_destructor = test_simple_destructor;
   18.98          CX_TEST_CALL_SUBROUTINE(verify_any_destructor, map);
   18.99          CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
  18.100      }
  18.101 @@ -361,7 +361,7 @@
  18.102      CxAllocator *allocator = &talloc.base;
  18.103      CX_TEST_DO {
  18.104          CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
  18.105 -        map->advanced_destructor = test_advanced_destructor;
  18.106 +        map->base.advanced_destructor = test_advanced_destructor;
  18.107          CX_TEST_CALL_SUBROUTINE(verify_any_destructor, map);
  18.108          CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
  18.109      }
  18.110 @@ -370,7 +370,7 @@
  18.111  
  18.112  CX_TEST(test_empty_map_size) {
  18.113      CX_TEST_DO {
  18.114 -        CX_TEST_ASSERT(cxEmptyMap->size == 0);
  18.115 +        CX_TEST_ASSERT(cxEmptyMap->base.size == 0);
  18.116      }
  18.117  }
  18.118  
  18.119 @@ -430,7 +430,7 @@
  18.120          cxMapPut(map, cx_mutstr("foo"), "bar");
  18.121          cxMapPut(map, cx_str("hallo"), "welt");
  18.122  
  18.123 -        CX_TEST_ASSERT(map->size == 3);
  18.124 +        CX_TEST_ASSERT(map->base.size == 3);
  18.125          CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "test"), "test"));
  18.126          CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "foo"), "bar"));
  18.127          CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "hallo"), "welt"));
  18.128 @@ -441,16 +441,16 @@
  18.129          cxMapDetach(map, hallo);
  18.130          cxMapPut(map, cx_hash_key_str("key"), "value");
  18.131  
  18.132 -        CX_TEST_ASSERT(map->size == 2);
  18.133 +        CX_TEST_ASSERT(map->base.size == 2);
  18.134          CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key"), "value"));
  18.135          CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "foo"), "bar"));
  18.136  
  18.137          void *r;
  18.138          r = cxMapRemoveAndGet(map, "key");
  18.139          r = cxMapRemoveAndGet(map, cx_str("foo"));
  18.140 -        if (r != NULL) map->size = 47;
  18.141 +        if (r != NULL) map->base.size = 47;
  18.142  
  18.143 -        CX_TEST_ASSERT(map->size == 0);
  18.144 +        CX_TEST_ASSERT(map->base.size == 0);
  18.145  
  18.146          cxMapDestroy(map);
  18.147          CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
  18.148 @@ -539,21 +539,21 @@
  18.149  
  18.150  static CX_TEST_SUBROUTINE(verify_map_contents, CxMap *map) {
  18.151      // verify that the reference map has same size (i.e. no other keys are mapped)
  18.152 -    CX_TEST_ASSERT(map->size == test_map_reference_size());
  18.153 +    CX_TEST_ASSERT(map->base.size == test_map_reference_size());
  18.154  
  18.155      // verify key iterator
  18.156      {
  18.157          // collect the keys from the map iterator
  18.158          CxIterator keyiter = cxMapIteratorKeys(map);
  18.159          CX_TEST_ASSERT(keyiter.elem_size == sizeof(CxHashKey));
  18.160 -        CX_TEST_ASSERT(keyiter.elem_count == map->size);
  18.161 -        CxHashKey *keys = calloc(map->size, sizeof(CxHashKey));
  18.162 +        CX_TEST_ASSERT(keyiter.elem_count == map->base.size);
  18.163 +        CxHashKey *keys = calloc(map->base.size, sizeof(CxHashKey));
  18.164          cx_foreach(CxHashKey*, elem, keyiter) {
  18.165              keys[keyiter.index] = *elem;
  18.166          }
  18.167 -        CX_TEST_ASSERT(keyiter.index == map->size);
  18.168 +        CX_TEST_ASSERT(keyiter.index == map->base.size);
  18.169          // verify that all keys are mapped to values in reference map
  18.170 -        for (size_t i = 0 ; i < map->size ; i++) {
  18.171 +        for (size_t i = 0 ; i < map->base.size ; i++) {
  18.172              cxmutstr ksz = cx_strdup(cx_strn(keys[i].data, keys[i].len));
  18.173              CX_TEST_ASSERT(test_map_reference_get(ksz.ptr) != NULL);
  18.174              cx_strfree(&ksz);
  18.175 @@ -566,15 +566,15 @@
  18.176          // by using that the values in our test data are unique strings
  18.177          // we can re-use a similar approach as above
  18.178          CxIterator valiter = cxMapIteratorValues(map);
  18.179 -        CX_TEST_ASSERT(valiter.elem_size == map->item_size);
  18.180 -        CX_TEST_ASSERT(valiter.elem_count == map->size);
  18.181 -        char const** values = calloc(map->size, sizeof(char const*));
  18.182 +        CX_TEST_ASSERT(valiter.elem_size == map->base.item_size);
  18.183 +        CX_TEST_ASSERT(valiter.elem_count == map->base.size);
  18.184 +        char const** values = calloc(map->base.size, sizeof(char const*));
  18.185          cx_foreach(char const*, elem, valiter) {
  18.186              values[valiter.index] = elem;
  18.187          }
  18.188 -        CX_TEST_ASSERT(valiter.index == map->size);
  18.189 +        CX_TEST_ASSERT(valiter.index == map->base.size);
  18.190          // verify that all values are present in the reference map
  18.191 -        for (size_t i = 0 ; i < map->size ; i++) {
  18.192 +        for (size_t i = 0 ; i < map->base.size ; i++) {
  18.193              bool found = false;
  18.194              for (size_t j = 0; j < test_map_reference_len ; j++) {
  18.195                  if (test_map_reference[j].value == values[i]) {
  18.196 @@ -591,16 +591,16 @@
  18.197      {
  18.198          CxIterator pairiter = cxMapIterator(map);
  18.199          CX_TEST_ASSERT(pairiter.elem_size == sizeof(CxMapEntry));
  18.200 -        CX_TEST_ASSERT(pairiter.elem_count == map->size);
  18.201 -        struct test_map_kv *pairs = calloc(map->size, sizeof(struct test_map_kv));
  18.202 +        CX_TEST_ASSERT(pairiter.elem_count == map->base.size);
  18.203 +        struct test_map_kv *pairs = calloc(map->base.size, sizeof(struct test_map_kv));
  18.204          cx_foreach(CxMapEntry*, entry, pairiter) {
  18.205              CxHashKey const *key = entry->key;
  18.206              pairs[pairiter.index].key = cx_strdup(cx_strn(key->data, key->len)).ptr;
  18.207              pairs[pairiter.index].value = entry->value;
  18.208          }
  18.209 -        CX_TEST_ASSERT(pairiter.index == map->size);
  18.210 +        CX_TEST_ASSERT(pairiter.index == map->base.size);
  18.211          // verify that all pairs are present in the reference map
  18.212 -        for (size_t i = 0 ; i < map->size ; i++) {
  18.213 +        for (size_t i = 0 ; i < map->base.size ; i++) {
  18.214              CX_TEST_ASSERT(test_map_reference_get(pairs[i].key) == pairs[i].value);
  18.215              // this was strdup'ed
  18.216              free((void*)pairs[i].key);
    19.1 --- a/tests/test_list.c	Thu May 23 19:29:14 2024 +0200
    19.2 +++ b/tests/test_list.c	Thu May 23 20:29:28 2024 +0200
    19.3 @@ -622,7 +622,7 @@
    19.4  
    19.5  CX_TEST(test_empty_list_size) {
    19.6      CX_TEST_DO {
    19.7 -        CX_TEST_ASSERT(cxEmptyList->size == 0);
    19.8 +        CX_TEST_ASSERT(cxEmptyList->base.size == 0);
    19.9          CX_TEST_ASSERT(cxListSize(cxEmptyList) == 0);
   19.10      }
   19.11  }
   19.12 @@ -706,13 +706,13 @@
   19.13      CX_TEST_DO {
   19.14          CxList *list = cxLinkedListCreate(alloc, cx_cmp_int, sizeof(int));
   19.15          CX_TEST_ASSERT(list != NULL);
   19.16 -        CX_TEST_ASSERT(list->item_size == sizeof(int));
   19.17 -        CX_TEST_ASSERT(list->simple_destructor == NULL);
   19.18 -        CX_TEST_ASSERT(list->advanced_destructor == NULL);
   19.19 -        CX_TEST_ASSERT(list->destructor_data == NULL);
   19.20 +        CX_TEST_ASSERT(list->base.item_size == sizeof(int));
   19.21 +        CX_TEST_ASSERT(list->base.simple_destructor == NULL);
   19.22 +        CX_TEST_ASSERT(list->base.advanced_destructor == NULL);
   19.23 +        CX_TEST_ASSERT(list->base.destructor_data == NULL);
   19.24          CX_TEST_ASSERT(cxListSize(list) == 0);
   19.25 -        CX_TEST_ASSERT(list->allocator == alloc);
   19.26 -        CX_TEST_ASSERT(list->cmpfunc == cx_cmp_int);
   19.27 +        CX_TEST_ASSERT(list->base.allocator == alloc);
   19.28 +        CX_TEST_ASSERT(list->base.cmpfunc == cx_cmp_int);
   19.29          CX_TEST_ASSERT(!cxListIsStoringPointers(list));
   19.30          cxListDestroy(list);
   19.31          CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   19.32 @@ -724,13 +724,13 @@
   19.33      CxList *list = cxLinkedListCreateSimple(sizeof(int));
   19.34      CX_TEST_DO {
   19.35          CX_TEST_ASSERT(list != NULL);
   19.36 -        CX_TEST_ASSERT(list->item_size == sizeof(int));
   19.37 -        CX_TEST_ASSERT(list->simple_destructor == NULL);
   19.38 -        CX_TEST_ASSERT(list->advanced_destructor == NULL);
   19.39 -        CX_TEST_ASSERT(list->destructor_data == NULL);
   19.40 +        CX_TEST_ASSERT(list->base.item_size == sizeof(int));
   19.41 +        CX_TEST_ASSERT(list->base.simple_destructor == NULL);
   19.42 +        CX_TEST_ASSERT(list->base.advanced_destructor == NULL);
   19.43 +        CX_TEST_ASSERT(list->base.destructor_data == NULL);
   19.44          CX_TEST_ASSERT(cxListSize(list) == 0);
   19.45 -        CX_TEST_ASSERT(list->allocator == cxDefaultAllocator);
   19.46 -        CX_TEST_ASSERT(list->cmpfunc == NULL);
   19.47 +        CX_TEST_ASSERT(list->base.allocator == cxDefaultAllocator);
   19.48 +        CX_TEST_ASSERT(list->base.cmpfunc == NULL);
   19.49          CX_TEST_ASSERT(!cxListIsStoringPointers(list));
   19.50      }
   19.51      cxListDestroy(list);
   19.52 @@ -741,7 +741,7 @@
   19.53      CX_TEST_DO {
   19.54          CX_TEST_ASSERT(!cxListIsStoringPointers(list));
   19.55          cxListStorePointers(list);
   19.56 -        CX_TEST_ASSERT(list->item_size == sizeof(void *));
   19.57 +        CX_TEST_ASSERT(list->base.item_size == sizeof(void *));
   19.58          CX_TEST_ASSERT(list->cl != NULL);
   19.59          CX_TEST_ASSERT(list->climpl != NULL);
   19.60          CX_TEST_ASSERT(cxListIsStoringPointers(list));
   19.61 @@ -757,13 +757,13 @@
   19.62      CxList *list = cxLinkedListCreateSimple(CX_STORE_POINTERS);
   19.63      CX_TEST_DO {
   19.64          CX_TEST_ASSERT(list != NULL);
   19.65 -        CX_TEST_ASSERT(list->item_size == sizeof(void*));
   19.66 -        CX_TEST_ASSERT(list->simple_destructor == NULL);
   19.67 -        CX_TEST_ASSERT(list->advanced_destructor == NULL);
   19.68 -        CX_TEST_ASSERT(list->destructor_data == NULL);
   19.69 +        CX_TEST_ASSERT(list->base.item_size == sizeof(void*));
   19.70 +        CX_TEST_ASSERT(list->base.simple_destructor == NULL);
   19.71 +        CX_TEST_ASSERT(list->base.advanced_destructor == NULL);
   19.72 +        CX_TEST_ASSERT(list->base.destructor_data == NULL);
   19.73          CX_TEST_ASSERT(cxListSize(list) == 0);
   19.74 -        CX_TEST_ASSERT(list->allocator == cxDefaultAllocator);
   19.75 -        CX_TEST_ASSERT(list->cmpfunc == cx_cmp_ptr);
   19.76 +        CX_TEST_ASSERT(list->base.allocator == cxDefaultAllocator);
   19.77 +        CX_TEST_ASSERT(list->base.cmpfunc == cx_cmp_ptr);
   19.78          CX_TEST_ASSERT(cxListIsStoringPointers(list));
   19.79      }
   19.80      cxListDestroy(list);
   19.81 @@ -776,13 +776,13 @@
   19.82      CX_TEST_DO {
   19.83          CxList *list = cxArrayListCreate(alloc, cx_cmp_int, sizeof(int), 8);
   19.84          CX_TEST_ASSERT(list != NULL);
   19.85 -        CX_TEST_ASSERT(list->item_size == sizeof(int));
   19.86 -        CX_TEST_ASSERT(list->simple_destructor == NULL);
   19.87 -        CX_TEST_ASSERT(list->advanced_destructor == NULL);
   19.88 -        CX_TEST_ASSERT(list->destructor_data == NULL);
   19.89 +        CX_TEST_ASSERT(list->base.item_size == sizeof(int));
   19.90 +        CX_TEST_ASSERT(list->base.simple_destructor == NULL);
   19.91 +        CX_TEST_ASSERT(list->base.advanced_destructor == NULL);
   19.92 +        CX_TEST_ASSERT(list->base.destructor_data == NULL);
   19.93          CX_TEST_ASSERT(cxListSize(list) == 0);
   19.94 -        CX_TEST_ASSERT(list->allocator == alloc);
   19.95 -        CX_TEST_ASSERT(list->cmpfunc == cx_cmp_int);
   19.96 +        CX_TEST_ASSERT(list->base.allocator == alloc);
   19.97 +        CX_TEST_ASSERT(list->base.cmpfunc == cx_cmp_int);
   19.98          CX_TEST_ASSERT(!cxListIsStoringPointers(list));
   19.99          cxListDestroy(list);
  19.100          CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
  19.101 @@ -794,13 +794,13 @@
  19.102      CxList *list = cxArrayListCreateSimple(sizeof(int), 8);
  19.103      CX_TEST_DO {
  19.104          CX_TEST_ASSERT(list != NULL);
  19.105 -        CX_TEST_ASSERT(list->item_size == sizeof(int));
  19.106 -        CX_TEST_ASSERT(list->simple_destructor == NULL);
  19.107 -        CX_TEST_ASSERT(list->advanced_destructor == NULL);
  19.108 -        CX_TEST_ASSERT(list->destructor_data == NULL);
  19.109 +        CX_TEST_ASSERT(list->base.item_size == sizeof(int));
  19.110 +        CX_TEST_ASSERT(list->base.simple_destructor == NULL);
  19.111 +        CX_TEST_ASSERT(list->base.advanced_destructor == NULL);
  19.112 +        CX_TEST_ASSERT(list->base.destructor_data == NULL);
  19.113          CX_TEST_ASSERT(cxListSize(list) == 0);
  19.114 -        CX_TEST_ASSERT(list->allocator == cxDefaultAllocator);
  19.115 -        CX_TEST_ASSERT(list->cmpfunc == NULL);
  19.116 +        CX_TEST_ASSERT(list->base.allocator == cxDefaultAllocator);
  19.117 +        CX_TEST_ASSERT(list->base.cmpfunc == NULL);
  19.118          CX_TEST_ASSERT(!cxListIsStoringPointers(list));
  19.119      }
  19.120      cxListDestroy(list);
  19.121 @@ -810,13 +810,13 @@
  19.122      CxList *list = cxArrayListCreateSimple(CX_STORE_POINTERS, 8);
  19.123      CX_TEST_DO {
  19.124          CX_TEST_ASSERT(list != NULL);
  19.125 -        CX_TEST_ASSERT(list->item_size == sizeof(void*));
  19.126 -        CX_TEST_ASSERT(list->simple_destructor == NULL);
  19.127 -        CX_TEST_ASSERT(list->advanced_destructor == NULL);
  19.128 -        CX_TEST_ASSERT(list->destructor_data == NULL);
  19.129 +        CX_TEST_ASSERT(list->base.item_size == sizeof(void*));
  19.130 +        CX_TEST_ASSERT(list->base.simple_destructor == NULL);
  19.131 +        CX_TEST_ASSERT(list->base.advanced_destructor == NULL);
  19.132 +        CX_TEST_ASSERT(list->base.destructor_data == NULL);
  19.133          CX_TEST_ASSERT(cxListSize(list) == 0);
  19.134 -        CX_TEST_ASSERT(list->allocator == cxDefaultAllocator);
  19.135 -        CX_TEST_ASSERT(list->cmpfunc == cx_cmp_ptr);
  19.136 +        CX_TEST_ASSERT(list->base.allocator == cxDefaultAllocator);
  19.137 +        CX_TEST_ASSERT(list->base.cmpfunc == cx_cmp_ptr);
  19.138          CX_TEST_ASSERT(cxListIsStoringPointers(list));
  19.139      }
  19.140      cxListDestroy(list);
  19.141 @@ -848,7 +848,7 @@
  19.142      CX_TEST_DO {
  19.143          int item = 0;
  19.144          CxList *list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS);
  19.145 -        list->simple_destructor = test_fake_simple_int_destr;
  19.146 +        list->base.simple_destructor = test_fake_simple_int_destr;
  19.147          cxListAdd(list, &item);
  19.148          cxListDestroy(list);
  19.149          CX_TEST_ASSERT(item == 42);
  19.150 @@ -862,8 +862,8 @@
  19.151      CX_TEST_DO {
  19.152          void *item = cxMalloc(alloc, sizeof(int));
  19.153          CxList *list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS);
  19.154 -        list->destructor_data = alloc;
  19.155 -        list->advanced_destructor = (cx_destructor_func2) cxFree;
  19.156 +        list->base.destructor_data = alloc;
  19.157 +        list->base.advanced_destructor = (cx_destructor_func2) cxFree;
  19.158          cxListAdd(list, item);
  19.159          CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc));
  19.160          cxListDestroy(list);
  19.161 @@ -894,7 +894,7 @@
  19.162      CX_TEST_DO {
  19.163          int item = 0;
  19.164          CxList *list = cxArrayListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS, 4);
  19.165 -        list->simple_destructor = test_fake_simple_int_destr;
  19.166 +        list->base.simple_destructor = test_fake_simple_int_destr;
  19.167          cxListAdd(list, &item);
  19.168          cxListDestroy(list);
  19.169          CX_TEST_ASSERT(item == 42);
  19.170 @@ -908,8 +908,8 @@
  19.171      CX_TEST_DO {
  19.172          void *item = cxMalloc(alloc, sizeof(int));
  19.173          CxList *list = cxArrayListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS, 4);
  19.174 -        list->destructor_data = alloc;
  19.175 -        list->advanced_destructor = (cx_destructor_func2) cxFree;
  19.176 +        list->base.destructor_data = alloc;
  19.177 +        list->base.advanced_destructor = (cx_destructor_func2) cxFree;
  19.178          cxListAdd(list, item);
  19.179          CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc));
  19.180          cxListDestroy(list);
  19.181 @@ -1207,8 +1207,8 @@
  19.182      int *testdata = int_test_data_added_to_list(list, isptrlist, len);
  19.183  
  19.184      CxIterator iter = cxListIterator(list);
  19.185 -    CX_TEST_ASSERT(iter.elem_size == list->item_size);
  19.186 -    CX_TEST_ASSERT(iter.elem_count == list->size);
  19.187 +    CX_TEST_ASSERT(iter.elem_size == list->base.item_size);
  19.188 +    CX_TEST_ASSERT(iter.elem_count == list->base.size);
  19.189      size_t i = 0;
  19.190      cx_foreach(int*, x, iter) {
  19.191          CX_TEST_ASSERT(i == iter.index);
  19.192 @@ -1225,8 +1225,8 @@
  19.193      CX_TEST_ASSERT(i == 0);
  19.194      i = len / 2;
  19.195      CxIterator mut_iter = cxListMutIteratorAt(list, i);
  19.196 -    CX_TEST_ASSERT(mut_iter.elem_size == list->item_size);
  19.197 -    CX_TEST_ASSERT(mut_iter.elem_count == list->size);
  19.198 +    CX_TEST_ASSERT(mut_iter.elem_size == list->base.item_size);
  19.199 +    CX_TEST_ASSERT(mut_iter.elem_count == list->base.size);
  19.200      size_t j = 0;
  19.201      cx_foreach(int*, x, mut_iter) {
  19.202          CX_TEST_ASSERT(mut_iter.index == len / 2 + j / 2);
  19.203 @@ -1395,7 +1395,7 @@
  19.204  roll_out_test_combos(simple_destr, {
  19.205      const size_t len = 60;
  19.206      int *testdata = int_test_data_added_to_list(list, isptrlist, len);
  19.207 -    list->simple_destructor = simple_destr_test_fun;
  19.208 +    list->base.simple_destructor = simple_destr_test_fun;
  19.209      CX_TEST_CALL_SUBROUTINE(test_list_verify_destructor, list, testdata, len);
  19.210      free(testdata);
  19.211  })
  19.212 @@ -1403,7 +1403,7 @@
  19.213  roll_out_test_combos(advanced_destr, {
  19.214      const size_t len = 75;
  19.215      int *testdata = int_test_data_added_to_list(list, isptrlist, len);
  19.216 -    list->advanced_destructor = advanced_destr_test_fun;
  19.217 +    list->base.advanced_destructor = advanced_destr_test_fun;
  19.218      CX_TEST_CALL_SUBROUTINE(test_list_verify_destructor, list, testdata, len);
  19.219      free(testdata);
  19.220  })
    20.1 --- a/tests/test_tree.c	Thu May 23 19:29:14 2024 +0200
    20.2 +++ b/tests/test_tree.c	Thu May 23 20:29:28 2024 +0200
    20.3 @@ -260,8 +260,8 @@
    20.4          CX_TEST_ASSERT(!iter.exiting);
    20.5          CX_TEST_ASSERT(iter.counter == 1);
    20.6          CX_TEST_ASSERT(iter.node == &root);
    20.7 -        CX_TEST_ASSERT(!iter.mutating);
    20.8 -        CX_TEST_ASSERT(!iter.remove);
    20.9 +        CX_TEST_ASSERT(!iter.base.mutating);
   20.10 +        CX_TEST_ASSERT(!iter.base.remove);
   20.11          CX_TEST_ASSERT(iter.stack != NULL);
   20.12          CX_TEST_ASSERT(iter.stack_capacity > 0);
   20.13          CX_TEST_ASSERT(iter.stack_size == 1);
   20.14 @@ -517,8 +517,8 @@
   20.15          CxTreeVisitor iter = cx_tree_visitor(&root, tree_child_list);
   20.16          CX_TEST_ASSERT(iter.counter == 1);
   20.17          CX_TEST_ASSERT(iter.node == &root);
   20.18 -        CX_TEST_ASSERT(!iter.mutating);
   20.19 -        CX_TEST_ASSERT(!iter.remove);
   20.20 +        CX_TEST_ASSERT(!iter.base.mutating);
   20.21 +        CX_TEST_ASSERT(!iter.base.remove);
   20.22          CX_TEST_ASSERT(iter.queue_next != NULL);
   20.23          CX_TEST_ASSERT(iter.queue_last != NULL);
   20.24          CX_TEST_ASSERT(iter.depth == 1);

mercurial