overhaul all attributes

Sun, 28 Dec 2025 17:31:20 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 28 Dec 2025 17:31:20 +0100
changeset 1675
36c0fb2b60b2
parent 1674
8b0f162ac88e
child 1676
f889ffd07c86

overhaul all attributes

src/allocator.c file | annotate | diff | comparison | revisions
src/cx/allocator.h file | annotate | diff | comparison | revisions
src/cx/array_list.h file | annotate | diff | comparison | revisions
src/cx/buffer.h file | annotate | diff | comparison | revisions
src/cx/collection.h file | annotate | diff | comparison | revisions
src/cx/common.h file | annotate | diff | comparison | revisions
src/cx/compare.h file | annotate | diff | comparison | revisions
src/cx/hash_key.h file | annotate | diff | comparison | revisions
src/cx/hash_map.h file | annotate | diff | comparison | revisions
src/cx/iterator.h file | annotate | diff | comparison | revisions
src/cx/json.h file | annotate | diff | comparison | revisions
src/cx/kv_list.h file | annotate | diff | comparison | revisions
src/cx/linked_list.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/mempool.h file | annotate | diff | comparison | revisions
src/cx/printf.h file | annotate | diff | comparison | revisions
src/cx/properties.h file | annotate | diff | comparison | revisions
src/cx/streams.h file | annotate | diff | comparison | revisions
src/cx/string.h file | annotate | diff | comparison | revisions
src/cx/test.h file | annotate | diff | comparison | revisions
src/cx/tree.h file | annotate | diff | comparison | revisions
src/hash_key.c file | annotate | diff | comparison | revisions
src/json.c file | annotate | diff | comparison | revisions
src/kv_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/test_allocator.c file | annotate | diff | comparison | revisions
tests/test_hash_map.c file | annotate | diff | comparison | revisions
tests/test_json.c file | annotate | diff | comparison | revisions
tests/test_list.c file | annotate | diff | comparison | revisions
tests/test_mempool.c file | annotate | diff | comparison | revisions
tests/test_tree.c file | annotate | diff | comparison | revisions
--- a/src/allocator.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/allocator.c	Sun Dec 28 17:31:20 2025 +0100
@@ -61,14 +61,14 @@
 #endif
 
 static void *cx_malloc_stdlib(
-        cx_attr_unused void *d,
+        CX_UNUSED void *d,
         size_t n
 ) {
     return malloc(n);
 }
 
 static void *cx_realloc_stdlib(
-        cx_attr_unused void *d,
+        CX_UNUSED void *d,
         void *mem,
         size_t n
 ) {
@@ -76,7 +76,7 @@
 }
 
 static void *cx_calloc_stdlib(
-        cx_attr_unused void *d,
+        CX_UNUSED void *d,
         size_t nmemb,
         size_t size
 ) {
@@ -84,7 +84,7 @@
 }
 
 static void cx_free_stdlib(
-        cx_attr_unused void *d,
+        CX_UNUSED void *d,
         void *mem
 ) {
     free(mem);
--- a/src/cx/allocator.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/allocator.h	Sun Dec 28 17:31:20 2025 +0100
@@ -35,10 +35,6 @@
 
 #include "common.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * The class definition for an allocator.
  */
@@ -153,8 +149,8 @@
  *
  * @return the system's memory page size in bytes
  */
-cx_attr_nodiscard
-CX_EXPORT unsigned long cx_system_page_size(void);
+CX_EXTERN CX_NODISCARD
+unsigned long cx_system_page_size(void);
 
 /**
  * Reallocate a previously allocated block.
@@ -167,8 +163,8 @@
  * @retval non-zero failure
  * @see cx_reallocatearray()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_reallocate_(void **mem, size_t n);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_reallocate_(void **mem, size_t n);
 
 /**
  * Reallocate a previously allocated block.
@@ -182,8 +178,8 @@
  * @retval non-zero failure
  * @see cx_reallocate()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_reallocatearray_(void **mem, size_t nmemb, size_t size);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_reallocatearray_(void **mem, size_t nmemb, size_t size);
 
 /**
  * Reallocate a previously allocated block and changes the pointer in-place,
@@ -239,8 +235,8 @@
  * @param allocator the allocator
  * @param mem a pointer to the block to free
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxFree(const CxAllocator *allocator, void *mem);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxFree(const CxAllocator *allocator, void *mem);
 
 /**
  * Allocate @p n bytes of memory.
@@ -249,9 +245,9 @@
  * @param n the number of bytes
  * @return a pointer to the allocated memory
  */
-cx_attr_nodiscard cx_attr_nonnull
-cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2)
-CX_EXPORT void *cxMalloc(const CxAllocator *allocator, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CX_MALLOC CX_DEALLOC_UCX CX_ALLOCSIZE(2)
+void *cxMalloc(const CxAllocator *allocator, size_t n);
 
 /**
  * Reallocate the previously allocated block in @p mem, making the new block
@@ -268,9 +264,9 @@
  * @param n the new size in bytes
  * @return a pointer to the reallocated memory
  */
-cx_attr_nodiscard cx_attr_nonnull_arg(1)
-cx_attr_dealloc_ucx cx_attr_allocsize(3)
-CX_EXPORT void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL_ARG(1)
+CX_DEALLOC_UCX CX_ALLOCSIZE(3)
+void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n);
 
 /**
  * Reallocate the previously allocated block in @p mem.
@@ -292,9 +288,9 @@
  * @param size the size of each element
  * @return a pointer to the reallocated memory
  */
-cx_attr_nodiscard cx_attr_nonnull_arg(1)
-cx_attr_dealloc_ucx cx_attr_allocsize(3, 4)
-CX_EXPORT void *cxReallocArray(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL_ARG(1)
+CX_DEALLOC_UCX CX_ALLOCSIZE(3, 4)
+void *cxReallocArray(const CxAllocator *allocator,
         void *mem, size_t nmemb, size_t size);
 
 /**
@@ -308,8 +304,8 @@
  * @retval zero success
  * @retval non-zero failure
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n);
 
 /**
  * Reallocate a previously allocated block and changes the pointer in-place,
@@ -342,8 +338,8 @@
  * @retval zero success
  * @retval non-zero on failure
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cxReallocateArray_(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cxReallocateArray_(const CxAllocator *allocator,
         void **mem, size_t nmemb, size_t size);
 
 /**
@@ -376,9 +372,9 @@
  * @param size the size of each element in bytes
  * @return a pointer to the allocated memory
  */
-cx_attr_nonnull_arg(1) cx_attr_nodiscard
-cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2, 3)
-CX_EXPORT void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size);
+CX_EXTERN CX_NONNULL_ARG(1) CX_NODISCARD
+CX_MALLOC CX_DEALLOC_UCX CX_ALLOCSIZE(2, 3)
+void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size);
 
 /**
  * Allocate @p n bytes of memory and sets every byte to zero.
@@ -387,9 +383,9 @@
  * @param n the number of bytes
  * @return a pointer to the allocated memory
  */
-cx_attr_nodiscard cx_attr_nonnull
-cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2)
-CX_EXPORT void *cxZalloc(const CxAllocator *allocator, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CX_MALLOC CX_DEALLOC_UCX CX_ALLOCSIZE(2)
+void *cxZalloc(const CxAllocator *allocator, size_t n);
 
 /**
  * Allocate @p n bytes of memory.
@@ -510,10 +506,7 @@
  *
  * @param mem the memory to deallocate
  */
-CX_EXPORT void cxFreeDefault(void *mem);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN
+void cxFreeDefault(void *mem);
 
 #endif // UCX_ALLOCATOR_H
--- a/src/cx/array_list.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/array_list.h	Sun Dec 28 17:31:20 2025 +0100
@@ -39,10 +39,6 @@
 
 #include "list.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * The maximum item size in an array list that fits into
  * a stack buffer when swapped.
@@ -88,8 +84,8 @@
  * @retval zero allocation was successful
  * @retval non-zero allocation failed
  */
-cx_attr_nonnull
-CX_EXPORT int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
 
 /**
  * Initializes an array by allocating memory.
@@ -131,8 +127,8 @@
  * @param capacity the capacity of the fixed size array
  * @param size the number of initialized elements in the fixed size array
  */
-cx_attr_nonnull
-CX_EXPORT void cx_array_init_fixed_(CxArray *array, const void *data, size_t capacity, size_t size);
+CX_EXTERN CX_NONNULL
+void cx_array_init_fixed_(CxArray *array, const void *data, size_t capacity, size_t size);
 
 /**
  * Initializes an array with fixed size memory.
@@ -173,8 +169,8 @@
  * @retval zero allocation was successful
  * @retval non-zero allocation failed
  */
-cx_attr_nonnull
-CX_EXPORT int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
 
 /**
  * Changes the capacity of an array.
@@ -215,8 +211,8 @@
  * @retval zero allocation was successful
  * @retval non-zero allocation failed
  */
-cx_attr_nonnull
-CX_EXPORT int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
 
 /**
  * Copies the array to a new memory region.
@@ -269,8 +265,8 @@
  * @retval zero success
  * @retval non-zero a re-allocation was necessary but failed
  */
-cx_attr_nonnull_arg(1, 2)
-CX_EXPORT int cx_array_insert_(const CxAllocator *allocator, CxArray *array,
+CX_EXTERN CX_NONNULL_ARG(1, 2)
+int cx_array_insert_(const CxAllocator *allocator, CxArray *array,
         size_t elem_size, size_t index, const void *other, size_t n);
 
 /**
@@ -404,8 +400,8 @@
  * @retval zero success
  * @retval non-zero a re-allocation was necessary but failed
  */
-cx_attr_nonnull
-CX_EXPORT int cx_array_insert_sorted_(const CxAllocator *allocator, CxArray *array,
+CX_EXTERN CX_NONNULL
+int cx_array_insert_sorted_(const CxAllocator *allocator, CxArray *array,
         size_t elem_size, const void *sorted_data, size_t n,
         cx_compare_func cmp_func, bool allow_duplicates);
 
@@ -561,8 +557,8 @@
  * @retval zero success
  * @retval non-zero a re-allocation was necessary but failed
  */
-cx_attr_nonnull_arg(1, 2, 4, 6)
-CX_EXPORT int cx_array_insert_sorted_c_(const CxAllocator *allocator, CxArray *array,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 4, 6)
+int cx_array_insert_sorted_c_(const CxAllocator *allocator, CxArray *array,
         size_t elem_size, const void *sorted_data, size_t n,
         cx_compare_func2 cmp_func, void *context, bool allow_duplicates);
 
@@ -721,7 +717,8 @@
  * @param fn the compare function
  * @param context the context for the compare function
  */
-CX_EXPORT void cx_array_qsort_c(void *array, size_t nmemb, size_t size,
+CX_EXTERN CX_NONNULL
+void cx_array_qsort_c(void *array, size_t nmemb, size_t size,
         cx_compare_func2 fn, void *context);
 
 /**
@@ -733,7 +730,8 @@
  * @param elem_size the size of one element
  * @param fn the compare function
  */
-CX_EXPORT void cx_array_sort_(CxArray *array, size_t elem_size,
+CX_EXTERN CX_NONNULL
+void cx_array_sort_(CxArray *array, size_t elem_size,
         cx_compare_func fn);
 
 /**
@@ -746,7 +744,8 @@
  * @param fn the compare function
  * @param context the context for the compare function
  */
-CX_EXPORT void cx_array_sort_c_(CxArray *array, size_t elem_size,
+CX_EXTERN CX_NONNULL
+void cx_array_sort_c_(CxArray *array, size_t elem_size,
         cx_compare_func2 fn, void *context);
 
 /**
@@ -778,8 +777,8 @@
  * @param elem_size the size of one element
  * @return an iterator over the elements
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT CxIterator cx_array_iterator_(CxArray *array, size_t elem_size);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CxIterator cx_array_iterator_(CxArray *array, size_t elem_size);
 
 /**
  * Creates an iterator over the elements of an array.
@@ -804,8 +803,8 @@
  * @param array the name of the array
  * @return an iterator over the elements
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT CxIterator cx_array_iterator_ptr_(CxArray *array);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CxIterator cx_array_iterator_ptr_(CxArray *array);
 
 /**
  * Creates an iterator over the elements of an array containing pointers.
@@ -835,8 +834,8 @@
  * @param n the number of elements to remove
  * @param fast indicates whether tail elements should be copied into the gap
  */
-cx_attr_nonnull
-CX_EXPORT void cx_array_remove_(CxArray *array, size_t elem_size, size_t index, size_t n, bool fast);
+CX_EXTERN CX_NONNULL
+void cx_array_remove_(CxArray *array, size_t elem_size, size_t index, size_t n, bool fast);
 
 /**
  * Removes one element from the array.
@@ -912,8 +911,8 @@
  * @param allocator (@c CxAllocator*) the allocator which was used to allocate the array
  * @param array a pointer to the array structure
  */
-cx_attr_nonnull
-CX_EXPORT void cx_array_free_(const CxAllocator *allocator, CxArray *array);
+CX_EXTERN CX_NONNULL
+void cx_array_free_(const CxAllocator *allocator, CxArray *array);
 
 /**
  * Deallocates an array.
@@ -962,8 +961,8 @@
  * @see cx_array_binary_search_sup()
  * @see cx_array_binary_search()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search_inf(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_inf(const void *arr, size_t size,
         size_t elem_size, const void *elem, cx_compare_func cmp_func);
 
 /**
@@ -985,8 +984,8 @@
  * @see cx_array_binary_search_inf()
  * @see cx_array_binary_search_sup()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search(const void *arr, size_t size,
         size_t elem_size, const void *elem, cx_compare_func cmp_func);
 
 /**
@@ -1014,8 +1013,8 @@
  * @see cx_array_binary_search_inf()
  * @see cx_array_binary_search()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_sup(const void *arr, size_t size,
         size_t elem_size, const void *elem, cx_compare_func cmp_func);
 
 
@@ -1045,8 +1044,8 @@
  * @see cx_array_binary_search_sup()
  * @see cx_array_binary_search()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search_inf_c(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_inf_c(const void *arr, size_t size,
         size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context);
 
 /**
@@ -1069,8 +1068,8 @@
  * @see cx_array_binary_search_inf()
  * @see cx_array_binary_search_sup()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search_c(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_c(const void *arr, size_t size,
         size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context);
 
 /**
@@ -1099,8 +1098,8 @@
  * @see cx_array_binary_search_inf()
  * @see cx_array_binary_search()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search_sup_c(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_sup_c(const void *arr, size_t size,
         size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context);
 
 /**
@@ -1111,8 +1110,8 @@
  * @param idx1 index of the first element
  * @param idx2 index of the second element
  */
-cx_attr_nonnull
-CX_EXPORT void cx_array_swap(void *arr, size_t elem_size, size_t idx1, size_t idx2);
+CX_EXTERN CX_NONNULL
+void cx_array_swap(void *arr, size_t elem_size, size_t idx1, size_t idx2);
 
 /**
  * Allocates an array list for storing elements with @p elem_size bytes each.
@@ -1127,14 +1126,8 @@
  * @param initial_capacity the initial number of elements the array can store
  * @return the created list
  */
-cx_attr_nodiscard
-cx_attr_malloc
-cx_attr_dealloc(cxListFree, 1)
-CX_EXPORT CxList *cxArrayListCreate(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxListFree, 1)
+CxList *cxArrayListCreate(const CxAllocator *allocator,
         size_t elem_size, size_t initial_capacity);
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
 #endif // UCX_ARRAY_LIST_H
--- a/src/cx/buffer.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/buffer.h	Sun Dec 28 17:31:20 2025 +0100
@@ -50,10 +50,6 @@
 #include "allocator.h"
 #include "string.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * No buffer features enabled (all flags cleared).
  */
@@ -177,8 +173,8 @@
  * @param flags buffer features (see cx_buffer_s.flags)
  * @return zero on success, non-zero if a required allocation failed
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT int cxBufferInit(CxBuffer *buffer, const CxAllocator *allocator,
+CX_EXTERN CX_NONNULL_ARG(1)
+int cxBufferInit(CxBuffer *buffer, const CxAllocator *allocator,
         void *space, size_t capacity, int flags);
 
 /**
@@ -190,8 +186,8 @@
  * @param buffer the buffer which contents shall be destroyed
  * @see cxBufferInit()
  */
-cx_attr_nonnull
-CX_EXPORT void cxBufferDestroy(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+void cxBufferDestroy(CxBuffer *buffer);
 
 /**
  * Deallocates the buffer.
@@ -202,7 +198,8 @@
  * @param buffer the buffer to deallocate
  * @see cxBufferCreate()
  */
-CX_EXPORT void cxBufferFree(CxBuffer *buffer);
+CX_EXTERN
+void cxBufferFree(CxBuffer *buffer);
 
 /**
  * Allocates and initializes a fresh buffer.
@@ -228,8 +225,8 @@
  * @param flags buffer features (see cx_buffer_s.flags)
  * @return a pointer to the buffer on success, @c NULL if a required allocation failed
  */
-cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard
-CX_EXPORT CxBuffer *cxBufferCreate(const CxAllocator *allocator, void *space,
+CX_EXTERN CX_MALLOC CX_DEALLOC(cxBufferFree, 1) CX_NODISCARD
+CxBuffer *cxBufferCreate(const CxAllocator *allocator, void *space,
                                    size_t capacity, int flags);
 
 /**
@@ -268,8 +265,8 @@
  * @see cxBufferShiftLeft()
  * @see cxBufferShiftRight()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferShift(CxBuffer *buffer, off_t shift);
+CX_EXTERN CX_NONNULL
+int cxBufferShift(CxBuffer *buffer, off_t shift);
 
 /**
  * Shifts the buffer to the right.
@@ -281,8 +278,8 @@
  * @retval non-zero if a required auto-extension or copy-on-write fails
  * @see cxBufferShift()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferShiftRight(CxBuffer *buffer, size_t shift);
+CX_EXTERN CX_NONNULL
+int cxBufferShiftRight(CxBuffer *buffer, size_t shift);
 
 /**
  * Shifts the buffer to the left.
@@ -294,8 +291,8 @@
  * @retval non-zero if the buffer uses copy-on-write and the allocation fails
  * @see cxBufferShift()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferShiftLeft(CxBuffer *buffer, size_t shift);
+CX_EXTERN CX_NONNULL
+int cxBufferShiftLeft(CxBuffer *buffer, size_t shift);
 
 
 /**
@@ -318,8 +315,8 @@
  * @retval non-zero if the position is invalid
  *
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
+CX_EXTERN CX_NONNULL
+int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
 
 /**
  * Discards items from the end of the buffer.
@@ -332,8 +329,8 @@
  * @param nitems the number of items to discard
  * @return the actual number of discarded items
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems);
+CX_EXTERN CX_NONNULL
+size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems);
 
 /**
  * Clears the buffer by resetting the position and deleting the data.
@@ -347,8 +344,8 @@
  * @param buffer the buffer to be cleared
  * @see cxBufferReset()
  */
-cx_attr_nonnull
-CX_EXPORT void cxBufferClear(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+void cxBufferClear(CxBuffer *buffer);
 
 /**
  * Resets the buffer by resetting the position and size to zero.
@@ -359,8 +356,8 @@
  * @param buffer the buffer to be cleared
  * @see cxBufferClear()
  */
-cx_attr_nonnull
-CX_EXPORT void cxBufferReset(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+void cxBufferReset(CxBuffer *buffer);
 
 /**
  * Tests, if the buffer position has exceeded the buffer size.
@@ -370,8 +367,8 @@
  * byte of the buffer's contents
  * @retval false otherwise
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT bool cxBufferEof(const CxBuffer *buffer);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+bool cxBufferEof(const CxBuffer *buffer);
 
 /**
  * Ensures that the buffer has the required capacity.
@@ -391,8 +388,8 @@
  * @see cxBufferShrink()
  * @see cxBufferMinimumCapacity()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferReserve(CxBuffer *buffer, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cxBufferReserve(CxBuffer *buffer, size_t capacity);
 
 /**
  * Limits the buffer's capacity.
@@ -410,8 +407,8 @@
  * @see cxBufferReserve()
  * @see cxBufferMinimumCapacity()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity);
 
 /**
  * Ensures that the buffer has a minimum capacity.
@@ -430,8 +427,8 @@
  * @see cxBufferReserve()
  * @see cxBufferShrink()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity);
 
 /**
  * Shrinks the capacity of the buffer to fit its current size.
@@ -450,8 +447,8 @@
  * @see cxBufferReserve()
  * @see cxBufferMinimumCapacity()
  */
-cx_attr_nonnull
-CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve);
+CX_EXTERN CX_NONNULL
+void cxBufferShrink(CxBuffer *buffer, size_t reserve);
 
 /**
  * Writes data to a CxBuffer.
@@ -474,8 +471,8 @@
  * @see cxBufferAppend()
  * @see cxBufferRead()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferWrite(const void *ptr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cxBufferWrite(const void *ptr, size_t size,
         size_t nitems, CxBuffer *buffer);
 
 /**
@@ -497,8 +494,8 @@
  * @see cxBufferWrite()
  * @see cxBufferRead()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cxBufferAppend(const void *ptr, size_t size,
         size_t nitems, CxBuffer *buffer);
 
 /**
@@ -516,8 +513,8 @@
  * @see cxBufferWrite()
  * @see cxBufferAppend()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferRead(void *ptr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cxBufferRead(void *ptr, size_t size,
         size_t nitems, CxBuffer *buffer);
 
 /**
@@ -540,8 +537,8 @@
  * stream is reached, and automatic extension is not enabled or not possible
  * @see cxBufferTerminate()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c);
+CX_EXTERN CX_NONNULL
+int cxBufferPut(CxBuffer *buffer, int c);
 
 /**
  * Writes a terminating zero to a buffer at the current position.
@@ -555,8 +552,8 @@
  * @return zero, if the terminator could be written, non-zero otherwise
  * @see cxBufferShrink()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferTerminate(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+int cxBufferTerminate(CxBuffer *buffer);
 
 /**
  * Internal function - do not use.
@@ -566,8 +563,8 @@
  * @return the number of bytes written
  * @see cxBufferPutString()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str);
+CX_EXTERN CX_NONNULL
+size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str);
 
 /**
  * Writes a string to a buffer with cxBufferWrite().
@@ -588,8 +585,8 @@
  * @return the number of bytes written
  * @see cxBufferPutString()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str);
+CX_EXTERN CX_NONNULL
+size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str);
 
 /**
  * Appends a string to a buffer with cxBufferAppend().
@@ -610,8 +607,8 @@
  * @param buffer the buffer to read from
  * @return the character or @c EOF, if the end of the buffer is reached
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferGet(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+int cxBufferGet(CxBuffer *buffer);
 
 /**
  * Gets the data in a buffer as a @c cxstring.
@@ -619,7 +616,8 @@
  * @param buffer the buffer
  * @return the data in the buffer interpreted as a @c cxstring
  */
-CX_INLINE cxstring cx_bstr(CxBuffer *buffer) {
+CX_NONNULL CX_INLINE
+cxstring cx_bstr(CxBuffer *buffer) {
     return cx_strn(buffer->space, buffer->size);
 }
 
@@ -629,12 +627,9 @@
  * @param buffer the buffer
  * @return the data in the buffer interpreted as a @c cxmutstr
  */
-CX_INLINE cxmutstr cx_bstr_m(CxBuffer *buffer) {
+CX_NONNULL CX_INLINE
+cxmutstr cx_bstr_m(CxBuffer *buffer) {
     return cx_mutstrn(buffer->space, buffer->size);
 }
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif // UCX_BUFFER_H
--- a/src/cx/collection.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/collection.h	Sun Dec 28 17:31:20 2025 +0100
@@ -40,10 +40,6 @@
 #include "iterator.h"
 #include "compare.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Special constant used for creating collections that are storing pointers.
  */
@@ -316,8 +312,4 @@
     if ((c)->collection.simple_destructor) cx_invoke_simple_destructor(c,e); \
     if ((c)->collection.advanced_destructor) cx_invoke_advanced_destructor(c,e)
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
 #endif // UCX_COLLECTION_H
--- a/src/cx/common.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/common.h	Sun Dec 28 17:31:20 2025 +0100
@@ -133,27 +133,27 @@
 /**
  * Inform the compiler that falling through a switch case is intentional.
  */
-#define cx_attr_fallthrough __attribute__((__fallthrough__))
+#define CX_FALLTHROUGH __attribute__((__fallthrough__))
 
 /**
  * All pointer arguments must be non-NULL.
  */
-#define cx_attr_nonnull __attribute__((__nonnull__))
+#define CX_NONNULL __attribute__((__nonnull__))
 
 /**
  * The specified pointer arguments must be non-NULL.
  */
-#define cx_attr_nonnull_arg(...) __attribute__((__nonnull__(__VA_ARGS__)))
+#define CX_NONNULL_ARG(...) __attribute__((__nonnull__(__VA_ARGS__)))
 
 /**
  * The returned value is guaranteed to be non-NULL.
  */
-#define cx_attr_returns_nonnull __attribute__((__returns_nonnull__))
+#define CX_RETURNS_NONNULL __attribute__((__returns_nonnull__))
 
 /**
  * The attributed function always returns freshly allocated memory.
  */
-#define cx_attr_malloc __attribute__((__malloc__))
+#define CX_MALLOC __attribute__((__malloc__))
 
 #if !defined(__clang__) && __GNUC__ >= 11
 /**
@@ -163,59 +163,59 @@
  * @param freefunc the function that shall be used to free the memory
  * @param freefunc_arg the index of the pointer argument in @p freefunc
  */
-#define cx_attr_dealloc(freefunc, freefunc_arg) \
+#define CX_DEALLOC(freefunc, freefunc_arg) \
     __attribute__((__malloc__(freefunc, freefunc_arg)))
 #else
 /**
  * Not supported in clang.
  */
-#define cx_attr_dealloc(...)
+#define CX_DEALLOC(...)
 #endif // __clang__
 
 /**
  * Shortcut to specify #cxFree() as deallocator.
  */
-#define cx_attr_dealloc_ucx cx_attr_dealloc(cxFree, 2)
+#define CX_DEALLOC_UCX CX_DEALLOC(cxFree, 2)
 
 /**
  * Specifies the parameters from which the allocation size is calculated.
  */
-#define cx_attr_allocsize(...) __attribute__((__alloc_size__(__VA_ARGS__)))
+#define CX_ALLOCSIZE(...) __attribute__((__alloc_size__(__VA_ARGS__)))
 
 
 #ifdef __clang__
 /**
  * No support for @c null_terminated_string_arg in clang or GCC below 14.
  */
-#define cx_attr_cstr_arg(idx)
+#define CX_CSTR_ARG(idx)
 /**
  * No support for the access attribute in clang.
  */
-#define cx_attr_access(mode, ...)
+#define CX_ACCESS(mode, ...)
 #else
 #if __GNUC__ < 10
 /**
  * No support for access attribute in GCC < 10.
  */
-#define cx_attr_access(mode, ...)
+#define CX_ACCESS(mode, ...)
 #else
 /**
  * Helper macro to define access macros.
  */
-#define cx_attr_access(mode, ...) __attribute__((__access__(mode, __VA_ARGS__)))
+#define CX_ACCESS(mode, ...) __attribute__((__access__(mode, __VA_ARGS__)))
 #endif // __GNUC__ < 10
 #if __GNUC__ < 14
 /**
  * No support for @c null_terminated_string_arg in clang or GCC below 14.
  */
-#define cx_attr_cstr_arg(idx)
+#define CX_CSTR_ARG(idx)
 #else
 /**
  * The specified argument is expected to be a zero-terminated string.
  *
  * @param idx the index of the argument
  */
-#define cx_attr_cstr_arg(idx) \
+#define CX_CSTR_ARG(idx) \
     __attribute__((__null_terminated_string_arg__(idx)))
 #endif // __GNUC__ < 14
 #endif // __clang__
@@ -227,7 +227,7 @@
  * Takes one or two arguments: the index of the pointer and (optionally) the
  * index of another argument specifying the maximum number of accessed bytes.
  */
-#define cx_attr_access_r(...) cx_attr_access(__read_only__, __VA_ARGS__)
+#define CX_ACCESS_R(...) CX_ACCESS(__read_only__, __VA_ARGS__)
 
 /**
  * Specifies that the function will read and write through the given pointer.
@@ -235,7 +235,7 @@
  * Takes one or two arguments: the index of the pointer and (optionally) the
  * index of another argument specifying the maximum number of accessed bytes.
  */
-#define cx_attr_access_rw(...) cx_attr_access(__read_write__, __VA_ARGS__)
+#define CX_ACCESS_RW(...) CX_ACCESS(__read_write__, __VA_ARGS__)
 
 /**
  * Specifies that the function will only write through the given pointer.
@@ -243,17 +243,17 @@
  * Takes one or two arguments: the index of the pointer and (optionally) the
  * index of another argument specifying the maximum number of accessed bytes.
  */
-#define cx_attr_access_w(...) cx_attr_access(__write_only__, __VA_ARGS__)
+#define CX_ACCESS_W(...) CX_ACCESS(__write_only__, __VA_ARGS__)
 
 /**
  * Do not warn about unused variable.
  */
-#define cx_attr_unused __attribute__((__unused__))
+#define CX_UNUSED __attribute__((__unused__))
 
 /**
  * Warn about discarded return value.
  */
-#define cx_attr_nodiscard __attribute__((__warn_unused_result__))
+#define CX_NODISCARD __attribute__((__warn_unused_result__))
 
 
 // ---------------------------------------------------------------------------
@@ -287,6 +287,16 @@
 #define CX_EXPORT
 #endif // CX_WINDLL / CX_WINDLL_EXPORT
 
+#ifdef __cplusplus
+#define CX_EXTERN extern "C" CX_EXPORT
+#define CX_FPTR extern "C" typedef
+#else
+/** Declares a function with external linkage. */
+#define CX_EXTERN CX_EXPORT
+/** Defines a function pointer. */
+#define CX_FPTR typedef
+#endif
+
 #ifdef __GNUC__
 /**
  * Declares a function to be inlined.
@@ -364,10 +374,8 @@
  * @retval zero success
  * @retval non-zero the multiplication would overflow
  */
-#if __cplusplus
-extern "C"
-#endif
-CX_EXPORT int cx_szmul_impl(size_t a, size_t b, size_t *result);
+CX_EXTERN
+int cx_szmul_impl(size_t a, size_t b, size_t *result);
 #endif // cx_szmul
 
 #endif // UCX_COMMON_H
--- a/src/cx/compare.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/compare.h	Sun Dec 28 17:31:20 2025 +0100
@@ -38,10 +38,6 @@
 
 #include "common.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * A comparator function comparing two arbitrary values.
  *
@@ -54,14 +50,14 @@
  * can be used, but they are NOT compatible with this function
  * pointer.
  */
-typedef int (*cx_compare_func)(const void *left, const void *right);
+CX_FPTR int (*cx_compare_func)(const void *left, const void *right);
 
 /**
  * A comparator function comparing two arbitrary values.
  *
  * Functions with this signature allow specifying a pointer to custom data.
  */
-typedef int (*cx_compare_func2)(const void *left, const void *right, void *data);
+CX_FPTR int (*cx_compare_func2)(const void *left, const void *right, void *data);
 
 /**
  * Compares two integers of type int.
@@ -75,8 +71,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type int.
@@ -87,8 +83,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int(int i1, int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int(int i1, int i2);
 
 /**
  * Compares two integers of type long int.
@@ -102,8 +98,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_longint(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_longint(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type long int.
@@ -114,8 +110,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_longint(long int i1, long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_longint(long int i1, long int i2);
 
 /**
  * Compares two integers of type long long.
@@ -129,8 +125,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_longlong(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_longlong(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type long long.
@@ -141,8 +137,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_longlong(long long int i1, long long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_longlong(long long int i1, long long int i2);
 
 /**
  * Compares two integers of type int16_t.
@@ -156,8 +152,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int16(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int16(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type int16_t.
@@ -168,8 +164,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int16(int16_t i1, int16_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int16(int16_t i1, int16_t i2);
 
 /**
  * Compares two integers of type int32_t.
@@ -183,8 +179,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int32(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int32(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type int32_t.
@@ -195,8 +191,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int32(int32_t i1, int32_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int32(int32_t i1, int32_t i2);
 
 /**
  * Compares two integers of type int64_t.
@@ -210,8 +206,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int64(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int64(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type int64_t.
@@ -222,8 +218,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int64(int64_t i1, int64_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int64(int64_t i1, int64_t i2);
 
 /**
  * Compares two integers of type unsigned int.
@@ -237,8 +233,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type unsigned int.
@@ -249,8 +245,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint(unsigned int i1, unsigned int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint(unsigned int i1, unsigned int i2);
 
 /**
  * Compares two integers of type unsigned long int.
@@ -264,8 +260,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_ulongint(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_ulongint(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type unsigned long int.
@@ -276,8 +272,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
 
 /**
  * Compares two integers of type unsigned long long.
@@ -291,8 +287,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_ulonglong(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_ulonglong(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type unsigned long long.
@@ -303,8 +299,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
 
 /**
  * Compares two integers of type uint16_t.
@@ -318,8 +314,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint16(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint16(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type uint16_t.
@@ -330,8 +326,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
 
 /**
  * Compares two integers of type uint32_t.
@@ -345,8 +341,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint32(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint32(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type uint32_t.
@@ -357,8 +353,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
 
 /**
  * Compares two integers of type uint64_t.
@@ -372,8 +368,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint64(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint64(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type uint64_t.
@@ -384,8 +380,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
 
 /**
  * Compares two integers of type size_t.
@@ -399,8 +395,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_size(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_size(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type size_t.
@@ -411,8 +407,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_size(size_t i1, size_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_size(size_t i1, size_t i2);
 
 /**
  * Compares two real numbers of type float with precision 1e-6f.
@@ -426,8 +422,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_float(const void *f1, const void *f2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_float(const void *f1, const void *f2);
 
 /**
  * Compares two real numbers of type float with precision 1e-6f.
@@ -438,8 +434,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_float(float f1, float f2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_float(float f1, float f2);
 
 /**
  * Compares two real numbers of type double with precision 1e-14.
@@ -453,8 +449,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_double(const void *d1, const void *d2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_double(const void *d1, const void *d2);
 
 /**
  * Compares two real numbers of type double with precision 1e-14.
@@ -465,8 +461,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_double(double d1, double d2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_double(double d1, double d2);
 
 /**
  * Compares the integer representation of two pointers.
@@ -480,8 +476,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_intptr(const void *ptr1, const void *ptr2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_intptr(const void *ptr1, const void *ptr2);
 
 /**
  * Compares the integer representation of two pointers.
@@ -492,8 +488,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
 
 /**
  * Compares the unsigned integer representation of two pointers.
@@ -507,8 +503,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
 
 /**
  * Compares the unsigned integer representation of two pointers.
@@ -519,8 +515,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
 
 /**
  * Compares the pointers specified in the arguments without dereferencing.
@@ -531,8 +527,8 @@
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_ptr(const void *ptr1, const void *ptr2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_ptr(const void *ptr1, const void *ptr2);
 
 /** Wraps a compare function for cx_cmp_wrap. */
 typedef struct {
@@ -549,11 +545,7 @@
  * @return the result of the invoked compare function
  * @see cx_compare_func_wrapper
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_wrap(const void *ptr1, const void *ptr2, void* cmp_wrapper);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_wrap(const void *ptr1, const void *ptr2, void* cmp_wrapper);
 
 #endif //UCX_COMPARE_H
--- a/src/cx/hash_key.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/hash_key.h	Sun Dec 28 17:31:20 2025 +0100
@@ -40,10 +40,6 @@
 #include "common.h"
 #include "string.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /** Internal structure for a key within a hash map. */
 struct cx_hash_key_s {
     /**
@@ -78,8 +74,8 @@
  * @param key the key, the hash shall be computed for
  * @see cx_hash_key()
  */
-cx_attr_nonnull
-CX_EXPORT void cx_hash_murmur(CxHashKey *key);
+CX_EXTERN CX_NONNULL
+void cx_hash_murmur(CxHashKey *key);
 
 /**
  * Mixes up a 32-bit integer to be used as a hash.
@@ -89,7 +85,8 @@
  * @param x the integer
  * @return the hash
  */
-CX_INLINE uint32_t cx_hash_u32(uint32_t x) {
+CX_INLINE
+uint32_t cx_hash_u32(uint32_t x) {
     x = ((x >> 16) ^ x) * 0x45d9f3bu;
     x = ((x >> 16) ^ x) * 0x45d9f3bu;
     x = (x >> 16) ^ x;
@@ -104,7 +101,8 @@
  * @param x the integer
  * @return the hash
  */
-CX_INLINE uint64_t cx_hash_u64(uint64_t x){
+CX_INLINE
+uint64_t cx_hash_u64(uint64_t x){
     x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
     x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
     x = x ^ (x >> 31);
@@ -122,9 +120,8 @@
  * @param len the length of the object in memory
  * @return the hash key
  */
-cx_attr_nodiscard
-cx_attr_access_r(1, 2)
-CX_EXPORT CxHashKey cx_hash_key(const void *obj, size_t len);
+CX_EXTERN CX_NODISCARD CX_ACCESS_R(1, 2)
+CxHashKey cx_hash_key(const void *obj, size_t len);
 
 /**
  * Computes a hash key from a 32-bit integer.
@@ -132,8 +129,8 @@
  * @param x the integer
  * @return the hash key
  */
-cx_attr_nodiscard
-CX_INLINE CxHashKey cx_hash_key_u32(uint32_t x) {
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_u32(uint32_t x) {
     CxHashKey key;
     key.data = NULL;
     key.len = 0;
@@ -147,8 +144,8 @@
  * @param x the integer
  * @return the hash key
  */
-cx_attr_nodiscard
-CX_INLINE CxHashKey cx_hash_key_u64(uint64_t x) {
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_u64(uint64_t x) {
     CxHashKey key;
     key.data = NULL;
     key.len = 0;
@@ -164,8 +161,8 @@
  * @param str the string
  * @return the hash key
  */
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_INLINE CxHashKey cx_hash_key_str(const char *str) {
+CX_NODISCARD CX_CSTR_ARG(1) CX_INLINE
+CxHashKey cx_hash_key_str(const char *str) {
     return cx_hash_key((const void*)str, str == NULL ? 0 : strlen(str));
 }
 
@@ -180,8 +177,8 @@
  * @param str the string
  * @return the hash key
  */
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_INLINE CxHashKey cx_hash_key_ustr(const unsigned char *str) {
+CX_NODISCARD CX_CSTR_ARG(1) CX_INLINE
+CxHashKey cx_hash_key_ustr(const unsigned char *str) {
     return cx_hash_key((const void*)str, str == NULL ? 0 : strlen((const char*)str));
 }
 
@@ -192,8 +189,8 @@
  * @param len the length
  * @return the hash key
  */
-cx_attr_nodiscard cx_attr_access_r(1, 2)
-CX_INLINE CxHashKey cx_hash_key_bytes(const unsigned char *bytes, size_t len) {
+CX_NODISCARD CX_ACCESS_R(1, 2) CX_INLINE
+CxHashKey cx_hash_key_bytes(const unsigned char *bytes, size_t len) {
     return cx_hash_key((const void*)bytes, len);
 }
 
@@ -203,8 +200,8 @@
  * @param str the string
  * @return the hash key
  */
-cx_attr_nodiscard
-CX_INLINE CxHashKey cx_hash_key_cxstr(cxstring str) {
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_cxstr(cxstring str) {
     return cx_hash_key((void*)str.ptr, str.length);
 }
 
@@ -214,8 +211,8 @@
  * @param str the string
  * @return the hash key
  */
-cx_attr_nodiscard
-CX_INLINE CxHashKey cx_hash_key_mutstr(cxmutstr str) {
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_mutstr(cxmutstr str) {
     return cx_hash_key((void*)str.ptr, str.length);
 }
 
@@ -226,8 +223,8 @@
  * @param key the key
  * @return a copy of the key (not the data)
  */
-cx_attr_nodiscard
-CX_INLINE CxHashKey cx_hash_key_identity(CxHashKey key) {
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_identity(CxHashKey key) {
     return key;
 }
 
@@ -238,8 +235,8 @@
  * @param key a pointer to a key
  * @return a copy of the key (not the data)
  */
-cx_attr_nodiscard
-CX_INLINE CxHashKey cx_hash_key_deref(const CxHashKey *key) {
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_deref(const CxHashKey *key) {
     return *key;
 }
 
@@ -279,8 +276,8 @@
  * @param right (@c CxHashKey*) the second key
  * @return zero when the keys equal, non-zero when they differ
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cx_hash_key_cmp(const void *left, const void *right);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cx_hash_key_cmp(const void *left, const void *right);
 
 /**
  * Interprets the key data as a string and returns it.
@@ -288,11 +285,10 @@
  * @param key the key
  * @return the key data as a string
  */
-CX_EXPORT cxstring cx_hash_key_as_string(const CxHashKey *key);
+CX_EXTERN
+cxstring cx_hash_key_as_string(const CxHashKey *key);
 
 #ifdef __cplusplus
-} // extern "C"
-
 // ----------------------------------------------------------
 // Overloads of CX_HASH_KEY (the C++ version of a _Generic)
 // ----------------------------------------------------------
--- a/src/cx/hash_map.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/hash_map.h	Sun Dec 28 17:31:20 2025 +0100
@@ -38,10 +38,6 @@
 
 #include "map.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /** Internal structure for an element of a hash map. */
 struct cx_hash_map_element_s;
 
@@ -83,8 +79,8 @@
  * @param buckets the initial number of buckets in this hash map
  * @return a pointer to the new hash map
  */
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1)
-CX_EXPORT CxMap *cxHashMapCreate(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxMapFree, 1)
+CxMap *cxHashMapCreate(const CxAllocator *allocator,
         size_t itemsize, size_t buckets);
 
 /**
@@ -106,12 +102,7 @@
  * @retval zero success
  * @retval non-zero if a memory allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapRehash(CxMap *map);
-
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL
+int cxMapRehash(CxMap *map);
 
 #endif // UCX_HASH_MAP_H
--- a/src/cx/iterator.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/iterator.h	Sun Dec 28 17:31:20 2025 +0100
@@ -38,10 +38,6 @@
 
 #include "common.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Common data for all iterators.
  */
@@ -220,8 +216,8 @@
  * @return an iterator for the specified array
  * @see cxIteratorPtr()
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxIterator(const void *array,
+CX_EXTERN CX_NODISCARD
+CxIterator cxIterator(const void *array,
         size_t elem_size, size_t elem_count);
 
 /**
@@ -237,11 +233,7 @@
  * @return an iterator for the specified array
  * @see cxIterator()
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NODISCARD
+CxIterator cxIteratorPtr(const void *array, size_t elem_count);
 
 #endif // UCX_ITERATOR_H
--- a/src/cx/json.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/json.h	Sun Dec 28 17:31:20 2025 +0100
@@ -43,11 +43,6 @@
 #include "array_list.h"
 #include "map.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
 /**
  * The type of the parsed token.
  */
@@ -115,6 +110,10 @@
      */
     CX_JSON_NOTHING, // this allows us to always return non-NULL values
     /**
+     * No meaningful data.
+     */
+    CX_JSON_UNINITIALIZED,
+    /**
      * A JSON object.
      */
     CX_JSON_OBJECT,
@@ -427,8 +426,8 @@
  *
  * @return new JSON writer settings
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonWriter cxJsonWriterCompact(void);
+CX_EXTERN CX_NODISCARD
+CxJsonWriter cxJsonWriterCompact(void);
 
 /**
  * Creates a default writer configuration for pretty output.
@@ -436,8 +435,8 @@
  * @param use_spaces false if you want tabs, true if you want four spaces instead
  * @return new JSON writer settings
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonWriter cxJsonWriterPretty(bool use_spaces);
+CX_EXTERN CX_NODISCARD
+CxJsonWriter cxJsonWriterPretty(bool use_spaces);
 
 /**
  * Writes a JSON value to a buffer or stream.
@@ -457,8 +456,8 @@
  * @retval zero success
  * @retval non-zero when no or not all data could be written
  */
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cxJsonWrite(void* target, const CxJsonValue* value,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxJsonWrite(void* target, const CxJsonValue* value,
         cx_write_func wfunc, const CxJsonWriter* settings);
 
 
@@ -472,8 +471,8 @@
  * @see cxJsonWriterCompact()
  * @see cxJsonToPrettyString()
  */
-cx_attr_nonnull_arg(2)
-CX_EXPORT cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value);
+CX_EXTERN CX_NONNULL_ARG(2) CX_NODISCARD
+cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value);
 
 /**
  * Produces a pretty string representation of the specified JSON value.
@@ -485,8 +484,8 @@
  * @see cxJsonWriterPretty()
  * @see cxJsonToString()
  */
-cx_attr_nonnull_arg(2)
-CX_EXPORT cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value);
+CX_EXTERN CX_NONNULL_ARG(2) CX_NODISCARD
+cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value);
 
 /**
  * Initializes the JSON interface.
@@ -495,8 +494,8 @@
  * @param allocator the allocator that shall be used for the produced values
  * @see cxJsonDestroy()
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxJsonInit(CxJson *json, const CxAllocator *allocator);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxJsonInit(CxJson *json, const CxAllocator *allocator);
 
 /**
  * Destroys the JSON interface.
@@ -504,8 +503,8 @@
  * @param json the JSON interface
  * @see cxJsonInit()
  */
-cx_attr_nonnull
-CX_EXPORT void cxJsonDestroy(CxJson *json);
+CX_EXTERN CX_NONNULL
+void cxJsonDestroy(CxJson *json);
 
 /**
  * Destroys and re-initializes the JSON interface.
@@ -515,8 +514,8 @@
  *
  * @param json the JSON interface
  */
-cx_attr_nonnull
-CX_EXPORT void cxJsonReset(CxJson *json);
+CX_EXTERN CX_NONNULL
+void cxJsonReset(CxJson *json);
 
 /**
  * Fills the input buffer.
@@ -536,8 +535,8 @@
  * @retval non-zero internal allocation error
  * @see cxJsonFill()
  */
-cx_attr_nonnull_arg(1) cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len);
+CX_EXTERN CX_NONNULL_ARG(1) CX_ACCESS_R(2, 3)
+int cxJsonFilln(CxJson *json, const char *buf, size_t len);
 
 
 /**
@@ -548,8 +547,8 @@
  * @retval zero success
  * @retval non-zero internal allocation error
  */
-cx_attr_nonnull
-CX_INLINE int cx_json_fill(CxJson *json, cxstring str) {
+CX_NONNULL CX_INLINE
+int cx_json_fill(CxJson *json, cxstring str) {
     return cxJsonFilln(json, str.ptr, str.length);
 }
 
@@ -581,8 +580,8 @@
  * @param value a pointer where the JSON value shall be stored to
  * @return status code
  */
-cx_attr_nonnull_arg(3)
-CX_EXPORT CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
+CX_EXTERN CX_NONNULL_ARG(3) CX_ACCESS_W(3)
+CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
             cxstring str, CxJsonValue **value);
 
 /**
@@ -603,6 +602,20 @@
         cx_json_from_string(allocator, cx_strcast(str), value)
 
 /**
+ * Recursively deallocates the memory of a JSON value.
+ *
+ * @remark The type of each deallocated value will be changed
+ * to #CX_JSON_NOTHING, and values of such a type will be skipped
+ * by the deallocation. That means this function protects
+ * you from double-frees when you are accidentally freeing
+ * a nested value and then the parent value (or vice versa).
+ *
+ * @param value the value
+ */
+CX_EXTERN
+void cxJsonValueFree(CxJsonValue *value);
+
+/**
  * Creates a new (empty) JSON object.
  *
  * @param allocator the allocator to use
@@ -610,8 +623,8 @@
  * @see cxJsonObjPutObj()
  * @see cxJsonArrAddValues()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
 
 /**
  * Creates a new (empty) JSON array.
@@ -624,8 +637,8 @@
  * @see cxJsonObjPutArr()
  * @see cxJsonArrAddValues()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity);
 
 /**
  * Creates a new JSON number value.
@@ -636,8 +649,8 @@
  * @see cxJsonObjPutNumber()
  * @see cxJsonArrAddNumbers()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
 
 /**
  * Creates a new JSON number value based on an integer.
@@ -648,8 +661,8 @@
  * @see cxJsonObjPutInteger()
  * @see cxJsonArrAddIntegers()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
 
 /**
  * Creates a new JSON string.
@@ -662,8 +675,8 @@
  * @see cxJsonObjPutString()
  * @see cxJsonArrAddCxStrings()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str);
 
 /**
  * Creates a new JSON string.
@@ -685,8 +698,8 @@
  * @see cxJsonObjPutLiteral()
  * @see cxJsonArrAddLiterals()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
 
 /**
  * Adds number values to a JSON array.
@@ -697,8 +710,8 @@
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
 
 /**
  * Adds number values, of which all are integers, to a JSON array.
@@ -709,8 +722,8 @@
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
 
 /**
  * Adds strings to a JSON array.
@@ -724,8 +737,8 @@
  * @retval non-zero allocation failure
  * @see cxJsonArrAddCxStrings()
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
 
 /**
  * Adds strings to a JSON array.
@@ -739,8 +752,8 @@
  * @retval non-zero allocation failure
  * @see cxJsonArrAddStrings()
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
 
 /**
  * Adds literals to a JSON array.
@@ -751,8 +764,8 @@
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
 
 /**
  * Add arbitrary values to a JSON array.
@@ -766,8 +779,8 @@
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
 
 /**
  * Adds or replaces a value within a JSON object.
@@ -780,8 +793,8 @@
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull
-CX_EXPORT int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child);
+CX_EXTERN CX_NONNULL
+int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child);
 
 /**
  * Adds or replaces a value within a JSON object.
@@ -810,8 +823,8 @@
  * @see cxJsonObjPut()
  * @see cxJsonCreateObj()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name);
 
 /**
  * Creates a new JSON object and adds it to an existing object.
@@ -836,8 +849,8 @@
  * @see cxJsonObjPut()
  * @see cxJsonCreateArr()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity);
 
 /**
  * Creates a new JSON array and adds it to an object.
@@ -863,8 +876,8 @@
  * @see cxJsonObjPut()
  * @see cxJsonCreateNumber()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num);
 
 /**
  * Creates a new JSON number and adds it to an object.
@@ -890,8 +903,8 @@
  * @see cxJsonObjPut()
  * @see cxJsonCreateInteger()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num);
 
 /**
  * Creates a new JSON number, based on an integer, and adds it to an object.
@@ -917,8 +930,8 @@
  * @see cxJsonObjPut()
  * @see cxJsonCreateString()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str);
 
 /**
  * Creates a new JSON string and adds it to an object.
@@ -946,8 +959,8 @@
  * @see cxJsonObjPut()
  * @see cxJsonCreateLiteral()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
 
 /**
  * Creates a new JSON literal and adds it to an object.
@@ -962,19 +975,6 @@
 #define cxJsonObjPutLiteral(obj, name, lit) cx_json_obj_put_literal(obj, cx_strcast(name), lit)
 
 /**
- * Recursively deallocates the memory of a JSON value.
- *
- * @remark The type of each deallocated value will be changed
- * to #CX_JSON_NOTHING, and values of such a type will be skipped
- * by the deallocation. That means this function protects
- * you from double-frees when you are accidentally freeing
- * a nested value and then the parent value (or vice versa).
- *
- * @param value the value
- */
-CX_EXPORT void cxJsonValueFree(CxJsonValue *value);
-
-/**
  * Tries to obtain the next JSON value.
  *
  * Before this function can be called, the input buffer needs
@@ -996,8 +996,8 @@
  * @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number
  * @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error
  */
-cx_attr_nonnull cx_attr_access_w(2)
-CX_EXPORT CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(2)
+CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
 
 /**
  * Checks if the specified value is a JSON object.
@@ -1006,8 +1006,8 @@
  * @retval true the value is a JSON object
  * @retval false otherwise
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsObject(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsObject(const CxJsonValue *value) {
     return value->type == CX_JSON_OBJECT;
 }
 
@@ -1018,8 +1018,8 @@
  * @retval true the value is a JSON array
  * @retval false otherwise
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsArray(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsArray(const CxJsonValue *value) {
     return value->type == CX_JSON_ARRAY;
 }
 
@@ -1030,8 +1030,8 @@
  * @retval true the value is a string
  * @retval false otherwise
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsString(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsString(const CxJsonValue *value) {
     return value->type == CX_JSON_STRING;
 }
 
@@ -1046,8 +1046,8 @@
  * @retval false otherwise
  * @see cxJsonIsInteger()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsNumber(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsNumber(const CxJsonValue *value) {
     return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER;
 }
 
@@ -1059,8 +1059,8 @@
  * @retval false otherwise
  * @see cxJsonIsNumber()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsInteger(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsInteger(const CxJsonValue *value) {
     return value->type == CX_JSON_INTEGER;
 }
 
@@ -1076,8 +1076,8 @@
  * @see cxJsonIsFalse()
  * @see cxJsonIsNull()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsLiteral(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsLiteral(const CxJsonValue *value) {
     return value->type == CX_JSON_LITERAL;
 }
 
@@ -1090,8 +1090,8 @@
  * @see cxJsonIsTrue()
  * @see cxJsonIsFalse()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsBool(const CxJsonValue *value) {
     return cxJsonIsLiteral(value) && value->literal != CX_JSON_NULL;
 }
 
@@ -1107,8 +1107,8 @@
  * @see cxJsonIsBool()
  * @see cxJsonIsFalse()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsTrue(const CxJsonValue *value) {
     return cxJsonIsLiteral(value) && value->literal == CX_JSON_TRUE;
 }
 
@@ -1124,8 +1124,8 @@
  * @see cxJsonIsBool()
  * @see cxJsonIsTrue()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsFalse(const CxJsonValue *value) {
     return cxJsonIsLiteral(value) && value->literal == CX_JSON_FALSE;
 }
 
@@ -1137,8 +1137,8 @@
  * @retval false otherwise
  * @see cxJsonIsLiteral()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsNull(const CxJsonValue *value) {
     return cxJsonIsLiteral(value) && value->literal == CX_JSON_NULL;
 }
 
@@ -1151,8 +1151,8 @@
  * @return the value represented as C string
  * @see cxJsonIsString()
  */
-cx_attr_nonnull  cx_attr_returns_nonnull
-CX_EXPORT char *cxJsonAsString(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL CX_NODISCARD
+char *cxJsonAsString(const CxJsonValue *value);
 
 /**
  * Obtains a UCX string from the given JSON value.
@@ -1163,8 +1163,8 @@
  * @return the value represented as UCX string
  * @see cxJsonIsString()
  */
-cx_attr_nonnull
-CX_EXPORT cxstring cxJsonAsCxString(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+cxstring cxJsonAsCxString(const CxJsonValue *value);
 
 /**
  * Obtains a mutable UCX string from the given JSON value.
@@ -1175,8 +1175,8 @@
  * @return the value represented as mutable UCX string
  * @see cxJsonIsString()
  */
-cx_attr_nonnull
-CX_EXPORT cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value);
 
 /**
  * Obtains a double-precision floating-point value from the given JSON value.
@@ -1187,8 +1187,8 @@
  * @return the value represented as double
  * @see cxJsonIsNumber()
  */
-cx_attr_nonnull
-CX_EXPORT double cxJsonAsDouble(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+double cxJsonAsDouble(const CxJsonValue *value);
 
 /**
  * Obtains a 64-bit signed integer from the given JSON value.
@@ -1202,8 +1202,8 @@
  * @see cxJsonIsNumber()
  * @see cxJsonIsInteger()
  */
-cx_attr_nonnull
-CX_EXPORT int64_t cxJsonAsInteger(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int64_t cxJsonAsInteger(const CxJsonValue *value);
 
 /**
  * Obtains a Boolean value from the given JSON value.
@@ -1215,8 +1215,8 @@
  * @return the value represented as double
  * @see cxJsonIsLiteral()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonAsBool(const CxJsonValue *value) {
     return value->literal == CX_JSON_TRUE;
 }
 
@@ -1229,8 +1229,8 @@
  * @return the size of the array
  * @see cxJsonIsArray()
  */
-cx_attr_nonnull
-CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+size_t cxJsonArrSize(const CxJsonValue *value) {
     return value->array.size;
 }
 
@@ -1248,8 +1248,8 @@
  * @return the value at the specified index
  * @see cxJsonIsArray()
  */
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL CX_NODISCARD
+CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
 
 /**
  * Removes an element from a JSON array.
@@ -1264,8 +1264,8 @@
  * @return the removed value from the specified index or @c NULL when the index was out of bounds
  * @see cxJsonIsArray()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
+CX_EXTERN CX_NONNULL
+CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
 
 /**
  * Returns an iterator over the JSON array elements.
@@ -1278,8 +1278,8 @@
  * @return an iterator over the array elements
  * @see cxJsonIsArray()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxIterator cxJsonArrIter(const CxJsonValue *value);
 
 /**
  * Returns the size of a JSON object.
@@ -1290,8 +1290,8 @@
  * @return the size of the object, i.e., the number of key/value pairs
  * @see cxJsonIsObject()
  */
-cx_attr_nonnull
-CX_INLINE size_t cxJsonObjSize(const CxJsonValue *value) {
+CX_NONNULL CX_INLINE
+size_t cxJsonObjSize(const CxJsonValue *value) {
     return cxCollectionSize(value->object);
 }
 
@@ -1307,8 +1307,8 @@
  * @return an iterator over the object members
  * @see cxJsonIsObject()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxJsonObjIter(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxMapIterator cxJsonObjIter(const CxJsonValue *value);
 
 /**
  * Internal function, do not use.
@@ -1316,8 +1316,8 @@
  * @param name the key to look up
  * @return the value corresponding to the key
  */
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL CX_NODISCARD
+CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name);
 
 /**
  * Returns a value corresponding to a key in a JSON object.
@@ -1341,8 +1341,8 @@
  * @param name the key to look up
  * @return the value corresponding to the key or @c NULL when the key is not part of the object
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name);
+CX_EXTERN CX_NONNULL
+CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name);
 
 /**
  * Removes and returns a value corresponding to a key in a JSON object.
@@ -1369,7 +1369,8 @@
  * @retval zero the values are equal (except for ordering of object members)
  * @retval non-zero the values differ
  */
-CX_EXPORT int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other);
+CX_EXTERN CX_NODISCARD
+int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other);
 
 
 /**
@@ -1385,11 +1386,10 @@
  * @return the new value or @c NULL if any allocation was unsuccessful
  * @see cxJsonCloneFunc()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonClone(const CxJsonValue* value,
+CX_EXTERN CX_NODISCARD
+CxJsonValue* cxJsonClone(const CxJsonValue* value,
         const CxAllocator* allocator);
 
-
 /**
  * A @c cx_clone_func compatible version of cxJsonClone().
  *
@@ -1402,8 +1402,8 @@
  * @return the new value or @c NULL if any allocation was unsuccessful
  * @see cxJsonClone()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cx_json_clone_func(
+CX_EXTERN CX_NODISCARD
+CxJsonValue* cx_json_clone_func(
         CxJsonValue* target, const CxJsonValue* source,
         const CxAllocator* allocator, void *data);
 
@@ -1419,9 +1419,5 @@
  */
 #define cxJsonCloneFunc  ((cx_clone_func) cx_json_clone_func)
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* UCX_JSON_H */
 
--- a/src/cx/kv_list.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/kv_list.h	Sun Dec 28 17:31:20 2025 +0100
@@ -40,10 +40,6 @@
 #include "list.h"
 #include "map.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
  *
@@ -63,8 +59,8 @@
  * @see cxKvListAsMap()
  * @see cxKvListAsList()
  */
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1)
-CX_EXPORT CxList *cxKvListCreate(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxListFree, 1)
+CxList *cxKvListCreate(const CxAllocator *allocator,
         size_t elem_size);
 
 /**
@@ -85,8 +81,8 @@
  * @see cxKvListAsMap()
  * @see cxKvListAsList()
  */
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1)
-CX_EXPORT CxMap *cxKvListCreateAsMap(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxMapFree, 1)
+CxMap *cxKvListCreateAsMap(const CxAllocator *allocator,
         size_t elem_size);
 
 /**
@@ -95,8 +91,8 @@
  * @param map a map pointer that was returned by a call to cxKvListAsMap()
  * @return the original list pointer
  */
-cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxList *cxKvListAsList(CxMap *map);
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_RETURNS_NONNULL
+CxList *cxKvListAsList(CxMap *map);
 
 /**
  * Converts a map pointer belonging to a key-value-List back to the original list pointer.
@@ -104,8 +100,8 @@
  * @param list a list created by cxKvListCreate()
  * @return a map pointer that lets you use the list as if it was a map
  */
-cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxMap *cxKvListAsMap(CxList *list);
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_RETURNS_NONNULL
+CxMap *cxKvListAsMap(CxList *list);
 
 /**
  * Sets or updates the key of a list item.
@@ -120,8 +116,8 @@
  * @retval non-zero memory allocation failure or the index is out of bounds
  * @see cxKvListSetKey()
  */
-cx_attr_nonnull
-CX_EXPORT int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key);
+CX_EXTERN CX_NONNULL
+int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key);
 
 /**
  * Inserts an item into the list at the specified index and associates it with the specified key.
@@ -134,8 +130,8 @@
  * @retval non-zero memory allocation failure or the index is out of bounds
  * @see cxKvListInsert()
  */
-cx_attr_nonnull
-CX_EXPORT int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value);
+CX_EXTERN CX_NONNULL
+int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value);
 
 /**
  * Sets or updates the key of a list item.
@@ -165,7 +161,6 @@
  */
 #define cxKvListInsert(list, index, key, value) cx_kv_list_insert(list, index, CX_HASH_KEY(key), value)
 
-
 /**
  * Removes the key of a list item.
  *
@@ -178,8 +173,8 @@
  * @retval zero success
  * @retval non-zero the index is out of bounds
  */
-cx_attr_nonnull
-CX_EXPORT int cxKvListRemoveKey(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL
+int cxKvListRemoveKey(CxList *list, size_t index);
 
 /**
  * Returns the key of a list item.
@@ -188,8 +183,8 @@
  * @param index the index of the element in the list
  * @return a pointer to the key or @c NULL when the index is out of bounds or the item does not have a key
  */
-cx_attr_nonnull
-CX_EXPORT const CxHashKey *cxKvListGetKey(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+const CxHashKey *cxKvListGetKey(CxList *list, size_t index);
 
 /**
  * Adds an item into the list and associates it with the specified key.
@@ -202,8 +197,4 @@
  */
 #define cxKvListAdd(list, key, value) cxKvListInsert(list, (list)->collection.size, key, value)
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
 #endif // UCX_KV_LIST_H
--- a/src/cx/linked_list.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/linked_list.h	Sun Dec 28 17:31:20 2025 +0100
@@ -39,10 +39,6 @@
 #include "common.h"
 #include "list.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Metadata for a linked list.
  */
@@ -94,8 +90,8 @@
  * @param elem_size the size of each element in bytes
  * @return the created list
  */
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1)
-CX_EXPORT CxList *cxLinkedListCreate(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxListFree, 1)
+CxList *cxLinkedListCreate(const CxAllocator *allocator,
         size_t elem_size);
 
 /**
@@ -112,8 +108,8 @@
  * @param list the list (must be a linked list)
  * @param len the length of the extra data
  */
-cx_attr_nonnull
-CX_EXPORT void cx_linked_list_extra_data(cx_linked_list *list, size_t len);
+CX_EXTERN CX_NONNULL
+void cx_linked_list_extra_data(cx_linked_list *list, size_t len);
 
 /**
  * Finds the node at a certain index.
@@ -132,8 +128,8 @@
  * @param index the search index
  * @return the node found at the specified index
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cx_linked_list_at(const void *start,size_t start_index,
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cx_linked_list_at(const void *start,size_t start_index,
         ptrdiff_t loc_advance, size_t index);
 
 /**
@@ -148,8 +144,8 @@
  * @param cmp_func a compare function to compare @p elem against the node data
  * @return a pointer to the found node or @c NULL if no matching node was found
  */
-cx_attr_nonnull_arg(1, 4, 6)
-CX_EXPORT void *cx_linked_list_find(const void *start, ptrdiff_t loc_advance,
+CX_EXTERN CX_NONNULL_ARG(1, 4, 6)
+void *cx_linked_list_find(const void *start, ptrdiff_t loc_advance,
         ptrdiff_t loc_data, const void *elem, size_t *found_index,
         cx_compare_func cmp_func);
 
@@ -166,8 +162,8 @@
  * @param context additional context for the compare function
  * @return a pointer to the found node or @c NULL if no matching node was found
  */
-cx_attr_nonnull_arg(1, 4, 6)
-CX_EXPORT void *cx_linked_list_find_c(const void *start, ptrdiff_t loc_advance,
+CX_EXTERN CX_NONNULL_ARG(1, 4, 6)
+void *cx_linked_list_find_c(const void *start, ptrdiff_t loc_advance,
         ptrdiff_t loc_data, const void *elem, size_t *found_index,
         cx_compare_func2 cmp_func, void *context);
 
@@ -182,8 +178,8 @@
  * @param loc_prev the location of the @c prev pointer
  * @return a pointer to the first node
  */
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL
+void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev);
 
 /**
  * Finds the last node in a linked list.
@@ -196,8 +192,8 @@
  * @param loc_next the location of the @c next pointer
  * @return a pointer to the last node
  */
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT void *cx_linked_list_last(const void *node, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL
+void *cx_linked_list_last(const void *node, ptrdiff_t loc_next);
 
 /**
  * Finds the predecessor of a node in case it is not linked.
@@ -209,8 +205,8 @@
  * @param node the successor of the node to find
  * @return the node or @c NULL if @p node has no predecessor
  */
-cx_attr_nonnull
-CX_EXPORT void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const void *node);
+CX_EXTERN CX_NONNULL
+void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const void *node);
 
 /**
  * Adds a new node to a linked list.
@@ -224,8 +220,8 @@
  * @param loc_next the location of a @c next pointer within your node struct (required)
  * @param new_node a pointer to the node that shall be appended
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
+CX_EXTERN CX_NONNULL_ARG(5)
+void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
 
 /**
  * Prepends a new node to a linked list.
@@ -239,8 +235,8 @@
  * @param loc_next the location of a @c next pointer within your node struct (required)
  * @param new_node a pointer to the node that shall be prepended
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
+CX_EXTERN CX_NONNULL_ARG(5)
+void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
 
 /**
  * Links two nodes.
@@ -250,8 +246,8 @@
  * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
  * @param loc_next the location of a @c next pointer within your node struct (required)
  */
-cx_attr_nonnull
-CX_EXPORT void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL
+void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
 /**
  * Unlinks two nodes.
@@ -263,8 +259,8 @@
  * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
  * @param loc_next the location of a @c next pointer within your node struct (required)
  */
-cx_attr_nonnull
-CX_EXPORT void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL
+void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
 /**
  * Inserts a new node after a given node of a linked list.
@@ -280,8 +276,8 @@
  * @param node the node after which to insert (@c NULL if you want to prepend the node to the list)
  * @param new_node a pointer to the node that shall be inserted
  */
-cx_attr_nonnull_arg(6)
-CX_EXPORT void cx_linked_list_insert(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(6)
+void cx_linked_list_insert(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *new_node);
 
 /**
@@ -304,8 +300,8 @@
  * @param insert_begin a pointer to the first node of the chain that shall be inserted
  * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined)
  */
-cx_attr_nonnull_arg(6)
-CX_EXPORT void cx_linked_list_insert_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(6)
+void cx_linked_list_insert_chain(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *insert_begin, void *insert_end);
 
 /**
@@ -322,8 +318,8 @@
  * @param new_node a pointer to the node that shall be inserted
  * @param cmp_func a compare function that will receive the node pointers
  */
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT void cx_linked_list_insert_sorted(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+void cx_linked_list_insert_sorted(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func);
 
 /**
@@ -345,8 +341,8 @@
  * @param insert_begin a pointer to the first node of the chain that shall be inserted
  * @param cmp_func a compare function that will receive the node pointers
  */
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT void cx_linked_list_insert_sorted_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+void cx_linked_list_insert_sorted_chain(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func);
 
 /**
@@ -365,8 +361,8 @@
  * @retval zero when the node was inserted
  * @retval non-zero when a node with the same value already exists
  */
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT int cx_linked_list_insert_unique(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+int cx_linked_list_insert_unique(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func);
 
 /**
@@ -387,8 +383,8 @@
  * @param cmp_func a compare function that will receive the node pointers
  * @return a pointer to a new chain with all duplicates that were not inserted (or @c NULL when there were no duplicates)
  */
-cx_attr_nonnull_arg(1, 5, 6) cx_attr_nodiscard
-CX_EXPORT void *cx_linked_list_insert_unique_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6) CX_NODISCARD
+void *cx_linked_list_insert_unique_chain(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func);
 
 /**
@@ -406,8 +402,8 @@
  * @param cmp_func a compare function that will receive the node pointers
  * @param context context for the compare function
  */
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT void cx_linked_list_insert_sorted_c(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+void cx_linked_list_insert_sorted_c(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func2 cmp_func, void *context);
 
 /**
@@ -430,8 +426,8 @@
  * @param cmp_func a compare function that will receive the node pointers
  * @param context context for the compare function
  */
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT void cx_linked_list_insert_sorted_chain_c(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+void cx_linked_list_insert_sorted_chain_c(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func2 cmp_func, void *context);
 
 /**
@@ -450,8 +446,8 @@
  * @retval zero when the node was inserted
  * @retval non-zero when a node with the same value already exists
  */
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT int cx_linked_list_insert_unique_c(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+int cx_linked_list_insert_unique_c(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func2 cmp_func, void *context);
 
 /**
@@ -473,8 +469,8 @@
  * @param context context for the compare function
  * @return a pointer to a new chain with all duplicates that were not inserted (or @c NULL when there were no duplicates)
  */
-cx_attr_nonnull_arg(1, 5, 6) cx_attr_nodiscard
-CX_EXPORT void *cx_linked_list_insert_unique_chain_c(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6) CX_NODISCARD
+void *cx_linked_list_insert_unique_chain_c(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func2 cmp_func, void *context);
 
 /**
@@ -498,8 +494,8 @@
  * @param num the number of nodes to remove
  * @return the actual number of nodes that were removed (can be less when the list did not have enough nodes)
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT size_t cx_linked_list_remove_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(5)
+size_t cx_linked_list_remove_chain(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, size_t num);
 
 /**
@@ -521,8 +517,8 @@
  * @param loc_next the location of a @c next pointer within your node struct (required)
  * @param node the node to remove
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT void cx_linked_list_remove(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(5)
+void cx_linked_list_remove(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node);
 
 /**
@@ -532,8 +528,8 @@
  * @param loc_next the location of the @c next pointer within the node struct
  * @return the size of the list or zero if @p node is @c NULL
  */
-cx_attr_nodiscard
-CX_EXPORT size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next);
+CX_EXTERN CX_NODISCARD
+size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next);
 
 /**
  * Sorts a linked list based on a comparison function.
@@ -547,8 +543,8 @@
  * @param loc_data the location of the @c data pointer within your node struct
  * @param cmp_func the compare function defining the sort order
  */
-cx_attr_nonnull_arg(1, 6)
-CX_EXPORT void cx_linked_list_sort(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 6)
+void cx_linked_list_sort(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func cmp_func);
 
 /**
@@ -564,11 +560,10 @@
  * @param cmp_func the compare function defining the sort order
  * @param context additional context for the compare function
  */
-cx_attr_nonnull_arg(1, 6)
-CX_EXPORT void cx_linked_list_sort_c(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 6)
+void cx_linked_list_sort_c(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func2 cmp_func, void *context);
 
-
 /**
  * Compares two lists element wise.
  *
@@ -582,8 +577,8 @@
  * @return the first non-zero result of invoking @p cmp_func or: negative if the left list is smaller than the
  * right list, positive if the left list is larger than the right list, zero if both lists are equal.
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT int cx_linked_list_compare(const void *begin_left, const void *begin_right,
+CX_EXTERN CX_NONNULL_ARG(5)
+int cx_linked_list_compare(const void *begin_left, const void *begin_right,
         ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func);
 
 /**
@@ -599,8 +594,8 @@
  * @return the first non-zero result of invoking @p cmp_func or: negative if the left list is smaller than the
  * right list, positive if the left list is larger than the right list, zero if both lists are equal.
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT int cx_linked_list_compare_c(const void *begin_left, const void *begin_right,
+CX_EXTERN CX_NONNULL_ARG(5)
+int cx_linked_list_compare_c(const void *begin_left, const void *begin_right,
         ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func2 cmp_func, void *context);
 
 /**
@@ -611,11 +606,7 @@
  * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
  * @param loc_next the location of a @c next pointer within your node struct (required)
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL_ARG(1)
+void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
 #endif // UCX_LINKED_LIST_H
--- a/src/cx/list.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/list.h	Sun Dec 28 17:31:20 2025 +0100
@@ -39,10 +39,6 @@
 #include "common.h"
 #include "collection.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * List class type.
  */
@@ -206,8 +202,8 @@
  * @param n the number of elements to insert
  * @return the number of elements actually inserted
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT size_t cx_list_default_insert_array(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL_ARG(1)
+size_t cx_list_default_insert_array(struct cx_list_s *list,
         size_t index, const void *data, size_t n);
 
 /**
@@ -226,8 +222,8 @@
  * @param n the number of elements to insert
  * @return the number of elements actually inserted
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_list_default_insert_sorted(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL
+size_t cx_list_default_insert_sorted(struct cx_list_s *list,
         const void *sorted_data, size_t n);
 
 /**
@@ -246,8 +242,8 @@
  * @param n the number of elements to insert
  * @return the number of elements from the @p sorted_data that are definitely present in the list after this call
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_list_default_insert_unique(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL
+size_t cx_list_default_insert_unique(struct cx_list_s *list,
         const void *sorted_data, size_t n);
 
 /**
@@ -261,8 +257,8 @@
  *
  * @param list the list that shall be sorted
  */
-cx_attr_nonnull
-CX_EXPORT void cx_list_default_sort(struct cx_list_s *list);
+CX_EXTERN CX_NONNULL
+void cx_list_default_sort(struct cx_list_s *list);
 
 /**
  * Default unoptimized swap implementation.
@@ -277,8 +273,8 @@
  * @retval non-zero when indices are out of bounds or memory
  * allocation for the temporary buffer fails
  */
-cx_attr_nonnull
-CX_EXPORT int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
+CX_EXTERN CX_NONNULL
+int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
 
 /**
  * Initializes a list struct.
@@ -319,8 +315,8 @@
  * @param allocator the allocator for the elements
  * @param elem_size the size of one element
  */
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT void cx_list_init(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+void cx_list_init(struct cx_list_s *list,
     struct cx_list_class_s *cl, const struct cx_allocator_s *allocator,
     size_t elem_size);
 
@@ -332,8 +328,8 @@
  * @param list the list which is comparing the elements
  * @return the comparison result
  */
-cx_attr_nonnull
-CX_EXPORT int cx_list_compare_wrapper(
+CX_EXTERN CX_NONNULL
+int cx_list_compare_wrapper(
     const void *left, const void *right, void *list);
 
 /**
@@ -342,8 +338,8 @@
  * @param list the list
  * @return the number of currently stored elements
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListSize(const CxList *list);
+CX_EXTERN CX_NONNULL
+size_t cxListSize(const CxList *list);
 
 /**
  * Adds an item to the end of the list.
@@ -355,8 +351,8 @@
  * @see cxListAddArray()
  * @see cxListEmplace()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListAdd(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListAdd(CxList *list, const void *elem);
 
 /**
  * Adds multiple items to the end of the list.
@@ -375,8 +371,8 @@
  * @return the number of added elements
  * @see cxListEmplaceArray()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListAddArray(CxList *list, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListAddArray(CxList *list, const void *array, size_t n);
 
 /**
  * Inserts an item at the specified index.
@@ -392,8 +388,8 @@
  * @see cxListInsertBefore()
  * @see cxListEmplaceAt()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsert(CxList *list, size_t index, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsert(CxList *list, size_t index, const void *elem);
 
 /**
  * Allocates memory for an element at the specified index and returns a pointer to that memory.
@@ -407,8 +403,8 @@
  * @see cxListEmplaceArrayAt()
  * @see cxListInsert()
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListEmplaceAt(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxListEmplaceAt(CxList *list, size_t index);
 
 /**
  * Allocates memory for an element at the end of the list and returns a pointer to that memory.
@@ -420,8 +416,8 @@
  * @see cxListEmplaceAt()
  * @see cxListAdd()
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListEmplace(CxList *list);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxListEmplace(CxList *list);
 
 /**
  * Allocates memory for multiple elements and returns an iterator.
@@ -440,8 +436,8 @@
  * @see cxListEmplaceAt()
  * @see cxListInsertArray()
  */
-cx_attr_nonnull
-CX_EXPORT CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n);
 
 /**
  * Allocates memory for multiple elements and returns an iterator.
@@ -459,8 +455,8 @@
  * @see cxListEmplace()
  * @see cxListAddArray()
  */
-cx_attr_nonnull
-CX_EXPORT CxIterator cxListEmplaceArray(CxList *list, size_t n);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxIterator cxListEmplaceArray(CxList *list, size_t n);
 
 /**
  * Inserts an item into a sorted list.
@@ -472,8 +468,8 @@
  * @retval zero success
  * @retval non-zero memory allocation failure
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsertSorted(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertSorted(CxList *list, const void *elem);
 
 /**
  * Inserts an item into a list if it does not exist.
@@ -488,8 +484,8 @@
  * @retval zero success (also when the element was already in the list)
  * @retval non-zero memory allocation failure
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsertUnique(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertUnique(CxList *list, const void *elem);
 
 /**
  * Inserts multiple items to the list at the specified index.
@@ -511,8 +507,8 @@
  * @return the number of added elements
  * @see cxListEmplaceArrayAt()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n);
 
 /**
  * Inserts a sorted array into a sorted list.
@@ -533,8 +529,8 @@
  * @param n the number of elements to add
  * @return the number of added elements
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n);
 
 /**
  * Inserts an array into a list, skipping duplicates.
@@ -568,8 +564,8 @@
  *
  * @return the number of elements from the @p sorted_data that are definitely present in the list after this call
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n);
 
 /**
  * Inserts an element after the current location of the specified iterator.
@@ -587,8 +583,8 @@
  * @see cxListInsert()
  * @see cxListInsertBefore()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsertAfter(CxIterator *iter, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertAfter(CxIterator *iter, const void *elem);
 
 /**
  * Inserts an element before the current location of the specified iterator.
@@ -606,8 +602,8 @@
  * @see cxListInsert()
  * @see cxListInsertAfter()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsertBefore(CxIterator *iter, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertBefore(CxIterator *iter, const void *elem);
 
 /**
  * Removes the element at the specified index.
@@ -620,8 +616,8 @@
  * @retval zero success
  * @retval non-zero index out of bounds
  */
-cx_attr_nonnull
-CX_EXPORT int cxListRemove(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL
+int cxListRemove(CxList *list, size_t index);
 
 /**
  * Removes and returns the element at the specified index.
@@ -636,8 +632,8 @@
  * @retval zero success
  * @retval non-zero index out of bounds
  */
-cx_attr_nonnull cx_attr_access_w(3)
-CX_EXPORT int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(3)
+int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf);
 
 /**
  * Removes and returns the first element of the list.
@@ -653,8 +649,8 @@
  * @see cxListPopFront()
  * @see cxListRemoveAndGetLast()
  */
-cx_attr_nonnull cx_attr_access_w(2)
-CX_EXPORT int cxListRemoveAndGetFirst(CxList *list, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(2)
+int cxListRemoveAndGetFirst(CxList *list, void *targetbuf);
 
 /**
  * Removes and returns the first element of the list.
@@ -687,8 +683,8 @@
  * @retval zero success
  * @retval non-zero the list is empty
  */
-cx_attr_nonnull cx_attr_access_w(2)
-CX_EXPORT int cxListRemoveAndGetLast(CxList *list, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(2)
+int cxListRemoveAndGetLast(CxList *list, void *targetbuf);
 
 /**
  * Removes and returns the last element of the list.
@@ -723,8 +719,8 @@
  * @param num the number of elements to remove
  * @return the actual number of removed elements
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListRemoveArray(CxList *list, size_t index, size_t num);
+CX_EXTERN CX_NONNULL
+size_t cxListRemoveArray(CxList *list, size_t index, size_t num);
 
 /**
  * Removes and returns multiple elements starting at the specified index.
@@ -739,8 +735,8 @@
  * @param targetbuf a buffer where to copy the elements
  * @return the actual number of removed elements
  */
-cx_attr_nonnull cx_attr_access_w(4)
-CX_EXPORT size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(4)
+size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf);
 
 /**
  * Removes all elements from this list.
@@ -750,8 +746,8 @@
  *
  * @param list the list
  */
-cx_attr_nonnull
-CX_EXPORT void cxListClear(CxList *list);
+CX_EXTERN CX_NONNULL
+void cxListClear(CxList *list);
 
 /**
  * Swaps two items in the list.
@@ -766,8 +762,8 @@
  * @retval non-zero one of the indices is out of bounds,
  * or the swap needed extra memory, but allocation failed
  */
-cx_attr_nonnull
-CX_EXPORT int cxListSwap(CxList *list, size_t i, size_t j);
+CX_EXTERN CX_NONNULL
+int cxListSwap(CxList *list, size_t i, size_t j);
 
 /**
  * Returns a pointer to the element at the specified index.
@@ -778,8 +774,8 @@
  * @param index the index of the element
  * @return a pointer to the element or @c NULL if the index is out of bounds
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListAt(const CxList *list, size_t index);
+CX_EXTERN CX_NONNULL
+void *cxListAt(const CxList *list, size_t index);
 
 /**
  * Returns a pointer to the first element.
@@ -789,8 +785,8 @@
  * @param list the list
  * @return a pointer to the first element or @c NULL if the list is empty
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListFirst(const CxList *list);
+CX_EXTERN CX_NONNULL
+void *cxListFirst(const CxList *list);
 
 /**
  * Returns a pointer to the last element.
@@ -800,8 +796,8 @@
  * @param list the list
  * @return a pointer to the last element or @c NULL if the list is empty
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListLast(const CxList *list);
+CX_EXTERN CX_NONNULL
+void *cxListLast(const CxList *list);
 
 /**
  * Sets the element at the specified index in the list.
@@ -815,8 +811,8 @@
  * @retval zero on success
  * @retval non-zero when index is out of bounds
  */
-cx_attr_nonnull
-CX_EXPORT int cxListSet(CxList *list, size_t index, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListSet(CxList *list, size_t index, const void *elem);
 
 /**
  * Returns an iterator pointing to the item at the specified index.
@@ -829,8 +825,8 @@
  * @param index the index where the iterator shall point at
  * @return a new iterator
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListIteratorAt(const CxList *list, size_t index);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListIteratorAt(const CxList *list, size_t index);
 
 /**
  * Returns a backwards iterator pointing to the item at the specified index.
@@ -843,8 +839,8 @@
  * @param index the index where the iterator shall point at
  * @return a new iterator
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index);
 
 /**
  * Returns an iterator pointing to the first item of the list.
@@ -856,8 +852,8 @@
  * @param list the list
  * @return a new iterator
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListIterator(const CxList *list);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListIterator(const CxList *list);
 
 /**
  * Returns a backwards iterator pointing to the last item of the list.
@@ -869,8 +865,8 @@
  * @param list the list
  * @return a new iterator
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListBackwardsIterator(const CxList *list);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListBackwardsIterator(const CxList *list);
 
 /**
  * Returns the index of the first element that equals @p elem.
@@ -883,8 +879,8 @@
  * @see cxListIndexValid()
  * @see cxListContains()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxListFind(const CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxListFind(const CxList *list, const void *elem);
 
 /**
  * Checks if the list contains the specified element.
@@ -897,8 +893,8 @@
  * @retval false if the element is not contained
  * @see cxListFind()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT bool cxListContains(const CxList* list, const void* elem);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+bool cxListContains(const CxList* list, const void* elem);
 
 /**
  * Checks if the specified index is within bounds.
@@ -908,8 +904,8 @@
  * @retval true if the index is within bounds
  * @retval false if the index is out of bounds
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT bool cxListIndexValid(const CxList *list, size_t index);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+bool cxListIndexValid(const CxList *list, size_t index);
 
 /**
  * Removes and returns the index of the first element that equals @p elem.
@@ -922,8 +918,8 @@
  * when the element is not found or could not be removed
  * @see cxListIndexValid()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListFindRemove(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+size_t cxListFindRemove(CxList *list, const void *elem);
 
 /**
  * Sorts the list.
@@ -932,16 +928,16 @@
  *
  * @param list the list
  */
-cx_attr_nonnull
-CX_EXPORT void cxListSort(CxList *list);
+CX_EXTERN CX_NONNULL
+void cxListSort(CxList *list);
 
 /**
  * Reverses the order of the items.
  *
  * @param list the list
  */
-cx_attr_nonnull
-CX_EXPORT void cxListReverse(CxList *list);
+CX_EXTERN CX_NONNULL
+void cxListReverse(CxList *list);
 
 /**
  * Compares a list to another list of the same type.
@@ -957,8 +953,8 @@
  * @retval positive the first list is larger
  * or the first non-equal element in the first list is larger
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cxListCompare(const CxList *list, const CxList *other);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cxListCompare(const CxList *list, const CxList *other);
 
 /**
  * Deallocates the memory of the specified list structure.
@@ -967,7 +963,8 @@
  *
  * @param list the list that shall be freed
  */
-CX_EXPORT void cxListFree(CxList *list);
+CX_EXTERN
+void cxListFree(CxList *list);
 
 
 /**
@@ -989,8 +986,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxListCloneShallow()
  */
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cxListClone(CxList *dst, const CxList *src,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxListClone(CxList *dst, const CxList *src,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -1012,8 +1009,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxListDifferenceShallow()
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxListDifference(CxList *dst,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxListDifference(CxList *dst,
         const CxList *minuend, const CxList *subtrahend,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
@@ -1036,8 +1033,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxListIntersectionShallow()
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxListIntersection(CxList *dst, const CxList *src, const CxList *other,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxListIntersection(CxList *dst, const CxList *src, const CxList *other,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -1061,8 +1058,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxListUnionShallow()
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxListUnion(CxList *dst, const CxList *src, const CxList *other,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxListUnion(CxList *dst, const CxList *src, const CxList *other,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -1084,8 +1081,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxListClone()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListCloneShallow(CxList *dst, const CxList *src);
+CX_EXTERN CX_NONNULL
+int cxListCloneShallow(CxList *dst, const CxList *src);
 
 /**
  * Clones elements from a list only if they are not present in another list.
@@ -1106,8 +1103,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxListDifference()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListDifferenceShallow(CxList *dst,
+CX_EXTERN CX_NONNULL
+int cxListDifferenceShallow(CxList *dst,
         const CxList *minuend, const CxList *subtrahend);
 
 /**
@@ -1129,8 +1126,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxListIntersection()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListIntersectionShallow(CxList *dst, const CxList *src, const CxList *other);
+CX_EXTERN CX_NONNULL
+int cxListIntersectionShallow(CxList *dst, const CxList *src, const CxList *other);
 
 /**
  * Performs a deep clone of one list into another, skipping duplicates.
@@ -1153,8 +1150,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxListUnion()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListUnionShallow(CxList *dst, const CxList *src, const CxList *other);
+CX_EXTERN CX_NONNULL
+int cxListUnionShallow(CxList *dst, const CxList *src, const CxList *other);
 
 /**
  * Asks the list to reserve enough memory for a given total number of elements.
@@ -1173,8 +1170,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxListShrink()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListReserve(CxList *list, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cxListReserve(CxList *list, size_t capacity);
 
 /**
  * Advises the list to free any overallocated memory.
@@ -1187,11 +1184,7 @@
  * @param list the list
  * @return usually zero
  */
-cx_attr_nonnull
-CX_EXPORT int cxListShrink(CxList *list);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL
+int cxListShrink(CxList *list);
 
 #endif // UCX_LIST_H
--- a/src/cx/map.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/map.h	Sun Dec 28 17:31:20 2025 +0100
@@ -46,10 +46,6 @@
 typedef struct cx_list_s CxList;
 #endif
 
-#ifdef    __cplusplus
-extern "C" {
-#endif
-
 /** Type for the UCX map. */
 typedef struct cx_map_s CxMap;
 
@@ -227,8 +223,8 @@
  *
  * @param map the map to be freed
  */
-CX_EXPORT void cxMapFree(CxMap *map);
-
+CX_EXTERN
+void cxMapFree(CxMap *map);
 
 /**
  * Clears a map by removing all elements.
@@ -237,8 +233,8 @@
  *
  * @param map the map to be cleared
  */
-cx_attr_nonnull
-CX_EXPORT void cxMapClear(CxMap *map);
+CX_EXTERN CX_NONNULL
+void cxMapClear(CxMap *map);
 
 /**
  * Returns the number of elements in this map.
@@ -246,8 +242,8 @@
  * @param map the map
  * @return the number of stored elements
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxMapSize(const CxMap *map);
+CX_EXTERN CX_NONNULL
+size_t cxMapSize(const CxMap *map);
 
 /**
  * Creates a value iterator for a map.
@@ -262,8 +258,8 @@
  * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored values
  */
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapIteratorValues(const CxMap *map);
+CX_EXTERN CX_NODISCARD
+CxMapIterator cxMapIteratorValues(const CxMap *map);
 
 /**
  * Creates a key iterator for a map.
@@ -277,8 +273,8 @@
  * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored keys
  */
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapIteratorKeys(const CxMap *map);
+CX_EXTERN CX_NODISCARD
+CxMapIterator cxMapIteratorKeys(const CxMap *map);
 
 /**
  * Creates an iterator for a map.
@@ -294,8 +290,8 @@
  * @see cxMapIteratorKeys()
  * @see cxMapIteratorValues()
  */
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapIterator(const CxMap *map);
+CX_EXTERN CX_NODISCARD
+CxMapIterator cxMapIterator(const CxMap *map);
 
 /**
  * Puts a key/value-pair into the map.
@@ -317,8 +313,8 @@
  * @retval non-zero value on memory allocation failure
  * @see cxMapPut()
  */
-cx_attr_nonnull
-CX_EXPORT int cx_map_put(CxMap *map, CxHashKey key, void *value);
+CX_EXTERN CX_NONNULL
+int cx_map_put(CxMap *map, CxHashKey key, void *value);
 
 /**
  * Puts a key/value-pair into the map.
@@ -359,8 +355,8 @@
  * @return the pointer to the allocated memory or @c NULL if allocation fails
  * @see cxMapEmplace()
  */
-cx_attr_nonnull
-CX_EXPORT void *cx_map_emplace(CxMap *map, CxHashKey key);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cx_map_emplace(CxMap *map, CxHashKey key);
 
 /**
  * Allocates memory for a value in the map associated with the specified key.
@@ -393,8 +389,8 @@
  * @return the value
  * @see cxMapGet()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cx_map_get(const CxMap *map, CxHashKey key);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cx_map_get(const CxMap *map, CxHashKey key);
 
 /**
  * Retrieves a value by using a key.
@@ -436,8 +432,8 @@
  * @see cxMapRemove()
  * @see cxMapRemoveAndGet()
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
+CX_EXTERN CX_NONNULL_ARG(1)
+int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
 
 /**
  * Removes a key/value-pair from the map by using the key.
@@ -476,7 +472,6 @@
  */
 #define cxMapRemoveAndGet(map, key, targetbuf) cx_map_remove(map, CX_HASH_KEY(key), targetbuf)
 
-
 /**
  * Performs a deep clone of one map into another.
  *
@@ -499,11 +494,10 @@
  * @retval zero when all elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cxMapClone(CxMap *dst, const CxMap *src,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxMapClone(CxMap *dst, const CxMap *src,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
-
 /**
  * Clones entries of a map if their key is not present in another map.
  *
@@ -516,8 +510,8 @@
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -537,8 +531,8 @@
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 
@@ -554,8 +548,8 @@
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -575,8 +569,8 @@
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -595,8 +589,8 @@
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cxMapUnion(CxMap *dst, const CxMap *src,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxMapUnion(CxMap *dst, const CxMap *src,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -622,8 +616,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxMapClone()
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapCloneShallow(CxMap *dst, const CxMap *src);
+CX_EXTERN CX_NONNULL
+int cxMapCloneShallow(CxMap *dst, const CxMap *src);
 
 /**
  * Clones entries of a map if their key is not present in another map.
@@ -637,8 +631,8 @@
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend);
+CX_EXTERN CX_NONNULL
+int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend);
 
 /**
  * Clones entries of a map if their key is not present in a list.
@@ -658,9 +652,8 @@
  * @retval non-zero when an allocation error occurred
  * @see cxMapListDifference()
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys);
-
+CX_EXTERN CX_NONNULL
+int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys);
 
 /**
  * Clones entries of a map only if their key is present in another map.
@@ -674,8 +667,8 @@
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other);
+CX_EXTERN CX_NONNULL
+int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other);
 
 /**
  * Clones entries of a map only if their key is present in a list.
@@ -694,8 +687,8 @@
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys);
+CX_EXTERN CX_NONNULL
+int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys);
 
 /**
  * Clones entries into a map if their key does not exist yet.
@@ -713,9 +706,8 @@
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapUnionShallow(CxMap *dst, const CxMap *src);
-
+CX_EXTERN CX_NONNULL
+int cxMapUnionShallow(CxMap *dst, const CxMap *src);
 
 /**
  * Compares the entries of two maps.
@@ -729,11 +721,7 @@
  * @retval non-zero (unspecified whether positive or negative) when the size
  * of both maps is equal but a key or a value is different
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapCompare(const CxMap *map, const CxMap *other);
-
-#ifdef    __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL
+int cxMapCompare(const CxMap *map, const CxMap *other);
 
 #endif // UCX_MAP_H
--- a/src/cx/mempool.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/mempool.h	Sun Dec 28 17:31:20 2025 +0100
@@ -39,10 +39,6 @@
 #include "common.h"
 #include "allocator.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /** A memory block in a simple memory pool. */
 struct cx_mempool_memory_s {
     /** The destructor. */
@@ -156,7 +152,8 @@
  *
  * @param pool the memory pool to free
  */
-CX_EXPORT void cxMempoolFree(CxMempool *pool);
+CX_EXTERN
+void cxMempoolFree(CxMempool *pool);
 
 /**
  * Creates an array-based memory pool.
@@ -168,8 +165,8 @@
  * @param type the type of memory pool
  * @return the created memory pool or @c NULL if allocation failed
  */
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMempoolFree, 1)
-CX_EXPORT CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxMempoolFree, 1)
+CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
 
 /**
  * Creates a basic array-based memory pool.
@@ -207,8 +204,8 @@
  * @param pool the memory pool
  * @param fnc the destructor that shall be applied to all memory blocks
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
 
 /**
  * Sets the global destructor for all memory blocks within the specified pool.
@@ -217,8 +214,8 @@
  * @param fnc the destructor that shall be applied to all memory blocks
  * @param data additional data for the destructor function
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
 
 /**
  * Sets the destructor function for a specific allocated memory object.
@@ -230,8 +227,8 @@
  * @param memory the object allocated in the pool
  * @param fnc the destructor function
  */
-cx_attr_nonnull
-CX_EXPORT void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc);
+CX_EXTERN CX_NONNULL
+void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc);
 
 /**
  * Sets the destructor function for a specific allocated memory object.
@@ -244,8 +241,8 @@
  * @param fnc the destructor function
  * @param data additional data for the destructor function
  */
-cx_attr_nonnull
-CX_EXPORT void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, void *data);
+CX_EXTERN CX_NONNULL
+void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, void *data);
 
 /**
  * Removes the destructor function for a specific allocated memory object.
@@ -255,8 +252,8 @@
  *
  * @param memory the object allocated in the pool
  */
-cx_attr_nonnull
-CX_EXPORT void cxMempoolRemoveDestructor(void *memory);
+CX_EXTERN CX_NONNULL
+void cxMempoolRemoveDestructor(void *memory);
 
 /**
  * Removes the destructor function for a specific allocated memory object.
@@ -266,8 +263,8 @@
  *
  * @param memory the object allocated in the pool
  */
-cx_attr_nonnull
-CX_EXPORT void cxMempoolRemoveDestructor2(void *memory);
+CX_EXTERN CX_NONNULL
+void cxMempoolRemoveDestructor2(void *memory);
 
 /**
  * Registers foreign memory with this pool.
@@ -284,9 +281,8 @@
  * @retval zero success
  * @retval non-zero failure
  */
-cx_attr_nonnull
-CX_EXPORT int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func destr);
-
+CX_EXTERN CX_NONNULL
+int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func destr);
 
 /**
  * Registers foreign memory with this pool.
@@ -307,8 +303,8 @@
  * @retval zero success
  * @retval non-zero failure
  */
-cx_attr_nonnull
-CX_EXPORT int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_func2 destr, void *data);
+CX_EXTERN CX_NONNULL
+int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_func2 destr, void *data);
 
 /**
  * Transfers all the memory managed by one pool to another.
@@ -325,8 +321,8 @@
  * @retval zero success
  * @retval non-zero allocation failure or incompatible pools
  */
-cx_attr_nonnull
-CX_EXPORT int cxMempoolTransfer(CxMempool *source, CxMempool *dest);
+CX_EXTERN CX_NONNULL
+int cxMempoolTransfer(CxMempool *source, CxMempool *dest);
 
 /**
  * Transfers an object from one pool to another.
@@ -342,11 +338,7 @@
  * @retval zero success
  * @retval non-zero failure, or the object was not found in the source pool, or the pools are incompatible
  */
-cx_attr_nonnull
-CX_EXPORT int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, const void *obj);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL
+int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, const void *obj);
 
 #endif // UCX_MEMPOOL_H
--- a/src/cx/printf.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/printf.h	Sun Dec 28 17:31:20 2025 +0100
@@ -45,14 +45,9 @@
  * @param fmt_idx index of the format string parameter
  * @param arg_idx index of the first formatting argument
  */
-#define cx_attr_printf(fmt_idx, arg_idx) \
+#define CX_PRINTF_ARGS(fmt_idx, arg_idx) \
     __attribute__((__format__(printf, fmt_idx, arg_idx)))
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
 /**
  * The maximum string length that fits into stack memory.
  */
@@ -68,8 +63,8 @@
  * @param ... additional arguments
  * @return the total number of bytes written or an error code from stdlib printf implementation
  */
-cx_attr_nonnull_arg(1, 2, 3) cx_attr_printf(3, 4) cx_attr_cstr_arg(3)
-CX_EXPORT int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3) CX_PRINTF_ARGS(3, 4) CX_CSTR_ARG(3)
+int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...);
 
 /**
  * A @c vfprintf like function which writes the output to a stream by
@@ -82,8 +77,8 @@
  * @return the total number of bytes written or an error code from stdlib printf implementation
  * @see cx_fprintf()
  */
-cx_attr_nonnull cx_attr_cstr_arg(3)
-CX_EXPORT int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_list ap);
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(3)
+int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_list ap);
 
 /**
  * An @c asprintf like function which allocates space for a string
@@ -99,8 +94,8 @@
  * @return the formatted string
  * @see cx_strfree_a()
  */
-cx_attr_nonnull_arg(1, 2) cx_attr_printf(2, 3) cx_attr_cstr_arg(2)
-CX_EXPORT cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2) CX_PRINTF_ARGS(2, 3) CX_CSTR_ARG(2)
+cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt, ...);
 
 /**
  * An @c asprintf like function which allocates space for a string
@@ -131,8 +126,8 @@
  * @return the formatted string
  * @see cx_asprintf_a()
  */
-cx_attr_nonnull cx_attr_cstr_arg(2)
-CX_EXPORT cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt, va_list ap);
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(2)
+cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt, va_list ap);
 
 /**
 * A @c vasprintf like function which allocates space for a string
@@ -193,8 +188,8 @@
  * @param ... additional arguments
  * @return the length of the produced string or an error code from stdlib printf implementation
  */
-cx_attr_nonnull_arg(1, 2, 3, 4) cx_attr_printf(4, 5) cx_attr_cstr_arg(4)
-CX_EXPORT int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4) CX_PRINTF_ARGS(4, 5) CX_CSTR_ARG(4)
+int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, ...);
 
 
 /**
@@ -228,8 +223,8 @@
  * @param ap argument list
  * @return the length of the produced string or an error code from stdlib printf implementation
  */
-cx_attr_nonnull  cx_attr_cstr_arg(4) cx_attr_access_rw(2) cx_attr_access_rw(3)
-CX_EXPORT int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap);
+CX_EXTERN CX_NONNULL  CX_CSTR_ARG(4) CX_ACCESS_RW(2) CX_ACCESS_RW(3)
+int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap);
 
 
 /**
@@ -275,9 +270,9 @@
  * @param ... additional arguments
  * @return the length of the produced string or an error code from stdlib printf implementation
  */
-cx_attr_nonnull_arg(1, 2, 4, 5) cx_attr_printf(5, 6) cx_attr_cstr_arg(5)
-cx_attr_access_rw(2) cx_attr_access_rw(3) cx_attr_access_rw(4)
-CX_EXPORT int cx_sprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2, 4, 5) CX_PRINTF_ARGS(5, 6) CX_CSTR_ARG(5)
+CX_ACCESS_RW(2) CX_ACCESS_RW(3) CX_ACCESS_RW(4)
+int cx_sprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ...);
 
 /**
  * An @c sprintf like function which allocates a new string when the buffer is not large enough.
@@ -322,12 +317,7 @@
  * @param ap argument list
  * @return the length of the produced string or an error code from stdlib printf implementation
  */
-cx_attr_nonnull cx_attr_cstr_arg(5)
-CX_EXPORT int cx_vsprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap);
-
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(5)
+int cx_vsprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap);
 
 #endif //UCX_PRINTF_H
--- a/src/cx/properties.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/properties.h	Sun Dec 28 17:31:20 2025 +0100
@@ -41,10 +41,6 @@
 #include "map.h"
 #include "buffer.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Configures the expected characters for the properties parser.
  */
@@ -184,8 +180,8 @@
  * @param config the properties configuration
  * @see cxPropertiesInitDefault()
  */
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
+CX_EXTERN CX_NONNULL
+void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
 
 /**
  * Destroys the properties interface.
@@ -198,8 +194,8 @@
  *
  * @param prop the properties interface
  */
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesDestroy(CxProperties *prop);
+CX_EXTERN CX_NONNULL
+void cxPropertiesDestroy(CxProperties *prop);
 
 /**
  * Destroys and re-initializes the properties interface.
@@ -209,8 +205,8 @@
  *
  * @param prop the properties interface
  */
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesReset(CxProperties *prop);
+CX_EXTERN CX_NONNULL
+void cxPropertiesReset(CxProperties *prop);
 
 /**
  * Initialize a properties parser with the default configuration.
@@ -242,8 +238,8 @@
  * @retval non-zero a memory allocation was necessary but failed
  * @see cxPropertiesFill()
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len);
 
 /**
  * Internal function, do not use.
@@ -253,8 +249,8 @@
  * @retval zero success
  * @retval non-zero a memory allocation was necessary but failed
  */
-cx_attr_nonnull
-CX_INLINE int cx_properties_fill(CxProperties *prop, cxstring str) {
+CX_NONNULL CX_INLINE
+int cx_properties_fill(CxProperties *prop, cxstring str) {
     return cxPropertiesFilln(prop, str.ptr, str.length);
 }
 
@@ -287,8 +283,8 @@
  * @param buf a pointer to stack memory
  * @param capacity the capacity of the stack memory
  */
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capacity);
+CX_EXTERN CX_NONNULL
+void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capacity);
 
 /**
  * Retrieves the next key/value-pair.
@@ -320,8 +316,8 @@
  * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
  * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value);
 
 /**
  * The size of the stack memory that `cxPropertiesLoad()` will reserve with `cxPropertiesUseStack()`.
@@ -342,8 +338,8 @@
  * @param config the parser config
  * @return status code
  */
-cx_attr_nonnull_arg(3)
-CX_EXPORT CxPropertiesStatus cx_properties_load(const CxAllocator *allocator,
+CX_EXTERN CX_NONNULL_ARG(3)
+CxPropertiesStatus cx_properties_load(const CxAllocator *allocator,
         cxstring filename, CxMap *target, CxPropertiesConfig config);
 
 /**
@@ -401,9 +397,4 @@
 #define cxPropertiesLoadDefault(allocator, filename, target) \
     cx_properties_load(allocator, cx_strcast(filename), target, cx_properties_config_default)
 
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
 #endif // UCX_PROPERTIES_H
--- a/src/cx/streams.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/streams.h	Sun Dec 28 17:31:20 2025 +0100
@@ -41,10 +41,6 @@
 
 #include "common.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Reads data from a stream and writes it to another stream.
  *
@@ -61,9 +57,9 @@
  * iterations.
  * @return the total number of bytes copied
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-cx_attr_access_r(1) cx_attr_access_w(2) cx_attr_access_w(5)
-CX_EXPORT size_t cx_stream_bncopy(void *src, void *dest,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+CX_ACCESS_R(1) CX_ACCESS_W(2) CX_ACCESS_W(5)
+size_t cx_stream_bncopy(void *src, void *dest,
         cx_read_func rfnc, cx_write_func wfnc,
         char *buf, size_t bufsize, size_t n);
 
@@ -95,8 +91,8 @@
  * @param n the maximum number of bytes that shall be copied.
  * @return total number of bytes copied
  */
-cx_attr_nonnull cx_attr_access_r(1) cx_attr_access_w(2)
-CX_EXPORT size_t cx_stream_ncopy(void *src, void *dest,
+CX_EXTERN CX_NONNULL CX_ACCESS_R(1) CX_ACCESS_W(2)
+size_t cx_stream_ncopy(void *src, void *dest,
         cx_read_func rfnc, cx_write_func wfnc, size_t n);
 
 /**
@@ -113,8 +109,4 @@
 #define cx_stream_copy(src, dest, rfnc, wfnc) \
     cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX)
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif // UCX_STREAMS_H
--- a/src/cx/string.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/string.h	Sun Dec 28 17:31:20 2025 +0100
@@ -159,7 +159,7 @@
  *
  * @see cx_mutstrn()
  */
-cx_attr_nodiscard cx_attr_cstr_arg(1)
+CX_NODISCARD CX_CSTR_ARG(1)
 CX_INLINE cxmutstr cx_mutstr(char *cstring) {
     cxmutstr str;
     str.ptr = cstring;
@@ -183,7 +183,7 @@
  *
  * @see cx_mutstr()
  */
-cx_attr_nodiscard cx_attr_access_rw(1, 2)
+CX_NODISCARD CX_ACCESS_RW(1, 2)
 CX_INLINE cxmutstr cx_mutstrn(char *cstring, size_t length) {
     cxmutstr str;
     str.ptr = cstring;
@@ -208,7 +208,7 @@
  *
  * @see cx_strn()
  */
-cx_attr_nodiscard cx_attr_cstr_arg(1)
+CX_NODISCARD CX_CSTR_ARG(1)
 CX_INLINE cxstring cx_str(const char *cstring) {
     cxstring str;
     str.ptr = cstring;
@@ -233,7 +233,7 @@
  *
  * @see cx_str()
  */
-cx_attr_nodiscard cx_attr_access_r(1, 2)
+CX_NODISCARD CX_ACCESS_R(1, 2)
 CX_INLINE cxstring cx_strn(const char *cstring, size_t length) {
     cxstring str;
     str.ptr = cstring;
@@ -242,40 +242,39 @@
 }
 
 #ifdef __cplusplus
-cx_attr_nodiscard
+CX_NODISCARD
 CX_CPPDECL cxmutstr cx_strcast_m(cxmutstr str) {
     return str;
 }
-cx_attr_nodiscard
+CX_NODISCARD
 CX_CPPDECL cxstring cx_strcast_m(cxstring str) {
     return str;
 }
-cx_attr_nodiscard
+CX_NODISCARD
 CX_CPPDECL cxmutstr cx_strcast_m(char *str) {
     return cx_mutstr(str);
 }
-cx_attr_nodiscard
+CX_NODISCARD
 CX_CPPDECL cxmutstr cx_strcast_m(unsigned char *str) {
     return cx_mutstr(reinterpret_cast<char*>(str));
 }
-cx_attr_nodiscard
+CX_NODISCARD
 CX_CPPDECL cxstring cx_strcast_m(const char *str) {
     return cx_str(str);
 }
-cx_attr_nodiscard
+CX_NODISCARD
 CX_CPPDECL cxstring cx_strcast_m(const unsigned char *str) {
     return cx_str(reinterpret_cast<const char*>(str));
 }
-cx_attr_nodiscard
+CX_NODISCARD
 CX_CPPDECL cxstring cx_strcast_(cxmutstr str) {
     return cx_strn(str.ptr, str.length);
 }
-cx_attr_nodiscard
+CX_NODISCARD
 CX_CPPDECL cxstring cx_strcast_(cxstring str) {
     return str;
 }
 #define cx_strcast(s) cx_strcast_(cx_strcast_m(s))
-extern "C" {
 #else
 /**
  * Internal function, do not use.
@@ -284,7 +283,7 @@
  * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
+CX_NODISCARD
 CX_INLINE cxmutstr cx_strcast_cxms(cxmutstr str) {
     return str;
 }
@@ -295,7 +294,7 @@
  * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
+CX_NODISCARD
 CX_INLINE cxstring cx_strcast_cxs(cxstring str) {
     return str;
 }
@@ -307,7 +306,7 @@
  * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
+CX_NODISCARD
 CX_INLINE cxmutstr cx_strcast_uc(unsigned char *str) {
     return cx_mutstr((char*)str);
 }
@@ -319,7 +318,7 @@
  * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
+CX_NODISCARD
 CX_INLINE cxmutstr cx_strcast_c(char *str) {
     return cx_mutstr(str);
 }
@@ -331,7 +330,7 @@
  * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
+CX_NODISCARD
 CX_INLINE cxstring cx_strcast_ucc(const unsigned char *str) {
     return cx_str((const char*)str);
 }
@@ -343,7 +342,7 @@
  * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
+CX_NODISCARD
 CX_INLINE cxstring cx_strcast_cc(const char *str) {
     return cx_str(str);
 }
@@ -419,7 +418,8 @@
  *
  * @param str the string to free
  */
-CX_EXPORT void cx_strfree(cxmutstr *str);
+CX_EXTERN
+void cx_strfree(cxmutstr *str);
 
 /**
  * Passes the pointer in this string to the allocator's free function.
@@ -434,8 +434,8 @@
  * @param alloc the allocator
  * @param str the string to free
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str);
 
 /**
  * Copies a string.
@@ -450,8 +450,8 @@
  * @retval non-zero if re-allocation failed
  * @see cx_strcpy_a()
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT int cx_strcpy_a_(const CxAllocator *alloc, cxmutstr *dest, cxstring src);
+CX_EXTERN CX_NONNULL_ARG(1)
+int cx_strcpy_a_(const CxAllocator *alloc, cxmutstr *dest, cxstring src);
 
 /**
  * Copies a string.
@@ -496,8 +496,8 @@
  * @param ...      all strings
  * @return the accumulated length of all strings
  */
-cx_attr_nodiscard
-CX_EXPORT size_t cx_strlen(size_t count, ...);
+CX_EXTERN CX_NODISCARD
+size_t cx_strlen(size_t count, ...);
 
 /**
  * Concatenates strings.
@@ -519,8 +519,8 @@
  * @param ...   all other UCX strings
  * @return the concatenated string
  */
-cx_attr_nonnull
-CX_EXPORT cxmutstr cx_strcat_a(const CxAllocator *alloc,
+CX_EXTERN CX_NONNULL
+cxmutstr cx_strcat_a(const CxAllocator *alloc,
         cxmutstr str, size_t count, ...);
 
 /**
@@ -553,8 +553,8 @@
  * @return a substring of @p string starting at @p start
  * @see cx_strsubsl()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strsubsl_(cxstring string, size_t start, size_t length);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strsubsl_(cxstring string, size_t start, size_t length);
 
 /**
  * Returns a substring.
@@ -566,19 +566,20 @@
  * @return a substring of @p string starting at @p start
  * @see cx_strsubs()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strsubs_(cxstring string, size_t start);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strsubs_(cxstring string, size_t start);
 
-CX_INLINE cxmutstr cx_strsubs_m_(cxmutstr string, size_t start) {
+CX_INLINE
+cxmutstr cx_strsubs_m_(cxmutstr string, size_t start) {
     return cx_mutstrcast(cx_strsubs_(cx_strcast(string), start));
 }
 
-CX_INLINE cxmutstr cx_strsubsl_m_(cxmutstr string, size_t start, size_t length) {
+CX_INLINE
+cxmutstr cx_strsubsl_m_(cxmutstr string, size_t start, size_t length) {
     return cx_mutstrcast(cx_strsubsl_(cx_strcast(string), start, length));
 }
 
 #ifdef __cplusplus
-} // extern "C"
 CX_CPPDECL cxstring cx_strsubs_cpp_(cxstring string, size_t start) {
     return cx_strsubs_(string, start);
 }
@@ -593,7 +594,6 @@
 }
 #define cx_strsubs(string, start) cx_strsubs_cpp_(cx_strcast_m(string), start)
 #define cx_strsubsl(string, start, length) cx_strsubsl_cpp_(cx_strcast_m(string), start, length)
-extern "C" {
 #else
 /**
  * Returns a substring starting at the specified location.
@@ -644,7 +644,8 @@
  * @return the character at the index
  * @see cx_strat()
  */
-CX_INLINE char cx_strat_(cxstring str, off_t index) {
+CX_INLINE
+char cx_strat_(cxstring str, off_t index) {
     size_t i;
     if (index >= 0) {
         i = index;
@@ -684,8 +685,8 @@
  *
  * @see cx_strchr_m()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strchr(cxstring string, int chr);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strchr(cxstring string, int chr);
 
 /**
  * Returns a substring starting at the location of the first occurrence of the
@@ -699,8 +700,8 @@
  *
  * @see cx_strchr()
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strchr_m(cxmutstr string, int chr);
+CX_EXTERN CX_NODISCARD
+cxmutstr cx_strchr_m(cxmutstr string, int chr);
 
 /**
  * Returns a substring starting at the location of the last occurrence of the
@@ -714,8 +715,8 @@
  *
  * @see cx_strrchr_m()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strrchr(cxstring string, int chr);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strrchr(cxstring string, int chr);
 
 /**
  * Returns a substring starting at the location of the last occurrence of the
@@ -729,8 +730,8 @@
  *
  * @see cx_strrchr()
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strrchr_m(cxmutstr string, int chr);
+CX_EXTERN CX_NODISCARD
+cxmutstr cx_strrchr_m(cxmutstr string, int chr);
 
 /**
  * Searches for a specific substring.
@@ -743,8 +744,8 @@
  * or an empty string, if the sequence is not contained
  * @see cx_strstr()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strstr_(cxstring haystack, cxstring needle);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strstr_(cxstring haystack, cxstring needle);
 
 /**
  * Returns a substring starting at the location of the first occurrence of the
@@ -774,8 +775,8 @@
  * or an empty string, if the sequence is not contained
  * @see cx_strstr_m()
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strstr_m_(cxmutstr haystack, cxstring needle);
+CX_EXTERN CX_NODISCARD
+cxmutstr cx_strstr_m_(cxmutstr haystack, cxstring needle);
 
 /**
  * Returns a substring starting at the location of the first occurrence of the
@@ -806,8 +807,8 @@
  * @return the actual number of split items
  * @see cx_strsplit()
  */
-cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3)
-CX_EXPORT size_t cx_strsplit_(cxstring string, cxstring delim,
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(4, 3)
+size_t cx_strsplit_(cxstring string, cxstring delim,
         size_t limit, cxstring *output);
 
 /**
@@ -823,8 +824,8 @@
  * @return the actual number of split items
  * @see cx_strsplit_a()
  */
-cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT size_t cx_strsplit_a_(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(5)
+size_t cx_strsplit_a_(const CxAllocator *allocator,
         cxstring string, cxstring delim,
         size_t limit, cxstring **output);
 
@@ -841,8 +842,8 @@
  * @return the actual number of split items
  * @see cx_strsplit_m()
  */
-cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3)
-CX_EXPORT size_t cx_strsplit_m_(cxmutstr string, cxstring delim,
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(4, 3)
+size_t cx_strsplit_m_(cxmutstr string, cxstring delim,
         size_t limit, cxmutstr *output);
 
 /**
@@ -858,8 +859,8 @@
  * @return the actual number of split items
  * @see cx_strsplit_ma()
  */
-cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT size_t cx_strsplit_ma_(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(5)
+size_t cx_strsplit_ma_(const CxAllocator *allocator,
         cxmutstr string, cxstring delim, size_t limit,
         cxmutstr **output);
 
@@ -946,8 +947,8 @@
  * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
  * than @p s2, zero if both strings equal
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_strcmp_(cxstring s1, cxstring s2);
+CX_EXTERN CX_NODISCARD
+int cx_strcmp_(cxstring s1, cxstring s2);
 
 /**
  * Compares two strings.
@@ -967,8 +968,8 @@
  * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
  * than @p s2, zero if both strings equal ignoring case
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_strcasecmp_(cxstring s1, cxstring s2);
+CX_EXTERN CX_NODISCARD
+int cx_strcasecmp_(cxstring s1, cxstring s2);
 
 /**
  * Compares two strings ignoring case.
@@ -993,8 +994,8 @@
  * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
  * than @p s2, zero if both strings equal
  */
-cx_attr_nodiscard  cx_attr_nonnull
-CX_EXPORT int cx_strcmp_p(const void *s1, const void *s2);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cx_strcmp_p(const void *s1, const void *s2);
 
 /**
  * Compares two strings ignoring case.
@@ -1006,9 +1007,8 @@
  * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
  * than @p s2, zero if both strings equal ignoring case
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cx_strcasecmp_p(const void *s1, const void *s2);
-
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cx_strcasecmp_p(const void *s1, const void *s2);
 
 /**
  * Creates a duplicate of the specified string.
@@ -1022,8 +1022,8 @@
  * @return a duplicate of the string
  * @see cx_strdup()
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string);
 
 /**
  * Creates a duplicate of the specified string.
@@ -1064,8 +1064,8 @@
  * @param string the string that shall be trimmed
  * @return the trimmed string
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strtrim(cxstring string);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strtrim(cxstring string);
 
 /**
  * Omits leading and trailing spaces.
@@ -1076,8 +1076,8 @@
  * @param string the string that shall be trimmed
  * @return the trimmed string
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strtrim_m(cxmutstr string);
+CX_EXTERN CX_NODISCARD
+cxmutstr cx_strtrim_m(cxmutstr string);
 
 /**
  * Checks if a string has a specific prefix.
@@ -1087,8 +1087,8 @@
  * @return @c true, if and only if the string has the specified prefix,
  * @c false otherwise
  */
-cx_attr_nodiscard
-CX_EXPORT bool cx_strprefix_(cxstring string, cxstring prefix);
+CX_EXTERN CX_NODISCARD
+bool cx_strprefix_(cxstring string, cxstring prefix);
 
 /**
  * Checks if a string has a specific prefix.
@@ -1108,8 +1108,8 @@
  * @return @c true, if and only if the string has the specified suffix,
  * @c false otherwise
  */
-cx_attr_nodiscard
-CX_EXPORT bool cx_strsuffix_(cxstring string, cxstring suffix);
+CX_EXTERN CX_NODISCARD
+bool cx_strsuffix_(cxstring string, cxstring suffix);
 
 /**
  * Checks if a string has a specific suffix.
@@ -1129,8 +1129,8 @@
  * @return @c true, if and only if the string has the specified prefix,
  * @c false otherwise
  */
-cx_attr_nodiscard
-CX_EXPORT bool cx_strcaseprefix_(cxstring string, cxstring prefix);
+CX_EXTERN CX_NODISCARD
+bool cx_strcaseprefix_(cxstring string, cxstring prefix);
 
 /**
  * Checks if a string has a specific prefix, ignoring the case.
@@ -1150,8 +1150,8 @@
  * @return @c true, if and only if the string has the specified suffix,
  * @c false otherwise
  */
-cx_attr_nodiscard
-CX_EXPORT bool cx_strcasesuffix_(cxstring string, cxstring suffix);
+CX_EXTERN CX_NODISCARD
+bool cx_strcasesuffix_(cxstring string, cxstring suffix);
 
 /**
  * Checks, if a string has a specific suffix, ignoring the case.
@@ -1163,7 +1163,6 @@
  */
 #define cx_strcasesuffix(string, suffix) cx_strcasesuffix_(cx_strcast(string), cx_strcast(suffix))
 
-
 /**
  * Replaces a string with another string.
  *
@@ -1180,8 +1179,8 @@
  * @see cx_strreplacen_a()
  * @see cx_strreplacen()
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT cxmutstr cx_strreplace_(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL
+cxmutstr cx_strreplace_(const CxAllocator *allocator,
         cxstring str, cxstring search, cxstring replacement, size_t replmax);
 
 /**
@@ -1268,8 +1267,8 @@
  * @param limit the maximum number of tokens that shall be returned
  * @return a new string tokenization context
  */
-cx_attr_nodiscard
-CX_EXPORT CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit);
+CX_EXTERN CX_NODISCARD
+CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit);
 
 /**
  * Creates a string tokenization context.
@@ -1292,11 +1291,10 @@
  * @return true if successful, false if the limit or the end of the string
  * has been reached
  */
-cx_attr_nonnull  cx_attr_nodiscard  cx_attr_access_w(2)
-CX_EXPORT bool cx_strtok_next_(CxStrtokCtx *ctx, cxstring *token);
+CX_EXTERN CX_NONNULL CX_NODISCARD CX_ACCESS_W(2)
+bool cx_strtok_next_(CxStrtokCtx *ctx, cxstring *token);
 
 #ifdef __cplusplus
-} // extern "C"
 CX_CPPDECL cx_strtok_next(CxStrtokCtx *ctx, cxstring *token) {
     return cx_strtok_next_(ctx, token);
 }
@@ -1305,7 +1303,6 @@
     //       but it works on all supported platforms
     return cx_strtok_next_(ctx, reinterpret_cast<cxstring*>(token));
 }
-extern "C" {
 #else // ! __cplusplus
 /**
  * Returns the next token.
@@ -1330,8 +1327,8 @@
  * @param delim array of more delimiters
  * @param count number of elements in the array
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t count);
 
 /* ------------------------------------------------------------------------- *
  *                string to number conversion functions                      *
@@ -1351,8 +1348,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1368,8 +1365,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1385,8 +1382,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1402,8 +1399,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1419,8 +1416,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1436,8 +1433,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1453,8 +1450,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1470,8 +1467,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1487,8 +1484,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1504,8 +1501,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1521,8 +1518,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1538,8 +1535,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1555,8 +1552,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1572,8 +1569,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1589,8 +1586,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1606,8 +1603,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1623,8 +1620,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a single precision floating-point number.
@@ -1640,8 +1637,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
 
 /**
  * Converts a string to a double precision floating-point number.
@@ -1657,8 +1654,8 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -2304,8 +2301,4 @@
  */
 #define cx_strtod(str, output) cx_strtod_lc_(cx_strcast(str), output, '.', ",")
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
 #endif //UCX_STRING_H
--- a/src/cx/test.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/test.h	Sun Dec 28 17:31:20 2025 +0100
@@ -73,10 +73,6 @@
 #include <string.h>
 #include <setjmp.h>
 
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
 #ifndef __FUNCTION__
 /**
  * Alias for the <code>__func__</code> preprocessor macro.
@@ -136,7 +132,7 @@
  * @param name optional name of the suite
  * @return a new test suite
  */
-cx_attr_nonnull cx_attr_nodiscard  cx_attr_cstr_arg(1) cx_attr_malloc
+CX_NONNULL CX_NODISCARD  CX_CSTR_ARG(1) CX_MALLOC
 static inline CxTestSuite* cx_test_suite_new(const char *name) {
     CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite));
     if (suite != NULL) {
@@ -173,7 +169,7 @@
  * @retval zero success
  * @retval non-zero failure
  */
-cx_attr_nonnull
+CX_NONNULL
 CX_INLINE int cx_test_register(CxTestSuite* suite, CxTest test) {
     CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet));
     if (t) {
@@ -200,7 +196,7 @@
  * @param out_target the target buffer or file to write the output to
  * @param out_writer the write function writing to @p out_target
  */
-cx_attr_nonnull
+CX_NONNULL
 CX_INLINE void cx_test_run(CxTestSuite *suite, void *out_target, cx_write_func out_writer) {
     if (suite->name == NULL) {
         out_writer("*** Test Suite ***\n", 1, 19, out_target);
@@ -326,9 +322,5 @@
 #define CX_TEST_CALL_SUBROUTINE(name,...) \
         name(_suite_,_output_,_writefnc_,_env_,__VA_ARGS__)
 
-#ifdef	__cplusplus
-}
-#endif
-
 #endif	/* UCX_TEST_H */
 
--- a/src/cx/tree.h	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/cx/tree.h	Sun Dec 28 17:31:20 2025 +0100
@@ -40,10 +40,6 @@
 
 #include "collection.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * A depth-first tree iterator.
  *
@@ -213,15 +209,15 @@
  * Releases internal memory of the given tree iterator.
  * @param iter the iterator
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeIteratorDispose(CxTreeIterator *iter);
+CX_EXTERN CX_NONNULL
+void cxTreeIteratorDispose(CxTreeIterator *iter);
 
 /**
  * Releases internal memory of the given tree visitor.
  * @param visitor the visitor
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeVisitorDispose(CxTreeVisitor *visitor);
+CX_EXTERN CX_NONNULL
+void cxTreeVisitorDispose(CxTreeVisitor *visitor);
 
 /**
  * Advises the iterator to skip the subtree below the current node and
@@ -256,8 +252,8 @@
  * @param loc_next offset in the node struct for the next pointer
  * @see cx_tree_unlink()
  */
-cx_attr_nonnull
-CX_EXPORT void cx_tree_link(void *parent, void *node,
+CX_EXTERN CX_NONNULL
+void cx_tree_link(void *parent, void *node,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
         ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
@@ -275,8 +271,8 @@
  * @param loc_next offset in the node struct for the next pointer
  * @see cx_tree_link()
  */
-cx_attr_nonnull
-CX_EXPORT void cx_tree_unlink(void *node,
+CX_EXTERN CX_NONNULL
+void cx_tree_unlink(void *node,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
         ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
@@ -366,8 +362,8 @@
  * could contain the node (but doesn't right now), negative if the tree does not
  * contain any node that might be related to the searched data
  */
-cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT int cx_tree_search_data(const void *root, size_t depth,
+CX_EXTERN CX_NONNULL CX_ACCESS_W(5)
+int cx_tree_search_data(const void *root, size_t depth,
         const void *data, cx_tree_search_data_func sfunc,
         void **result, ptrdiff_t loc_children, ptrdiff_t loc_next);
 
@@ -385,7 +381,7 @@
  * node matching the criteria is returned.
  *
  * @param root the root node
-* @param depth the maximum depth (zero=indefinite, one=just root)
+ * @param depth the maximum depth (zero=indefinite, one=just root)
  * @param node the node to search for
  * @param sfunc the search function
  * @param result where the result shall be stored
@@ -395,8 +391,8 @@
  * could contain the node (but doesn't right now), negative if the tree does not
  * contain any node that might be related to the searched data
  */
-cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT int cx_tree_search(const void *root, size_t depth,
+CX_EXTERN CX_NONNULL CX_ACCESS_W(5)
+int cx_tree_search(const void *root, size_t depth,
         const void *node, cx_tree_search_func sfunc,
         void **result, ptrdiff_t loc_children, ptrdiff_t loc_next);
 
@@ -420,8 +416,8 @@
  * @return the new tree iterator
  * @see cxTreeIteratorDispose()
  */
-cx_attr_nodiscard
-CX_EXPORT CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit,
+CX_EXTERN CX_NODISCARD
+CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit,
         ptrdiff_t loc_children, ptrdiff_t loc_next);
 
 /**
@@ -442,8 +438,8 @@
  * @return the new tree visitor
  * @see cxTreeVisitorDispose()
  */
-cx_attr_nodiscard
-CX_EXPORT CxTreeVisitor cx_tree_visitor(void *root,
+CX_EXTERN CX_NODISCARD
+CxTreeVisitor cx_tree_visitor(void *root,
         ptrdiff_t loc_children, ptrdiff_t loc_next);
 
 /**
@@ -504,8 +500,8 @@
  * @return the number of nodes created and added
  * @see cx_tree_add()
  */
-cx_attr_nonnull_arg(1, 3, 4, 6, 7) cx_attr_access_w(6)
-CX_EXPORT size_t cx_tree_add_iter(struct cx_iterator_base_s *iter, size_t num,
+CX_EXTERN CX_NONNULL_ARG(1, 3, 4, 6, 7) CX_ACCESS_W(6)
+size_t cx_tree_add_iter(struct cx_iterator_base_s *iter, size_t num,
         cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
         void *cdata, void **failed, void *root,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
@@ -548,8 +544,8 @@
  * @return the number of array elements successfully processed
  * @see cx_tree_add()
  */
-cx_attr_nonnull_arg(1, 4, 5, 7, 8) cx_attr_access_w(7)
-CX_EXPORT size_t cx_tree_add_array(const void *src, size_t num, size_t elem_size,
+CX_EXTERN CX_NONNULL_ARG(1, 4, 5, 7, 8) CX_ACCESS_W(7)
+size_t cx_tree_add_array(const void *src, size_t num, size_t elem_size,
         cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
         void *cdata, void **failed, void *root,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
@@ -600,8 +596,8 @@
  * @return zero when a new node was created and added to the tree,
  * non-zero otherwise
  */
-cx_attr_nonnull_arg(1, 2, 3, 5, 6) cx_attr_access_w(5)
-CX_EXPORT int cx_tree_add(const void *src,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 5, 6) CX_ACCESS_W(5)
+int cx_tree_add(const void *src,
         cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
         void *cdata, void **cnode, void *root,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
@@ -786,8 +782,8 @@
  * @param node the node to remove
  * @see cxTreeFree()
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeDestroySubtree(CxTree *tree, void *node);
+CX_EXTERN CX_NONNULL
+void cxTreeDestroySubtree(CxTree *tree, void *node);
 
 
 /**
@@ -825,7 +821,8 @@
  *
  * @param tree the tree to free
  */
-CX_EXPORT void cxTreeFree(CxTree *tree);
+CX_EXTERN
+void cxTreeFree(CxTree *tree);
 
 /**
  * Creates a new tree structure based on the specified layout.
@@ -851,8 +848,8 @@
  * @see cxTreeCreateSimple()
  * @see cxTreeCreateWrapped()
  */
-cx_attr_nonnull_arg(2, 3, 4) cx_attr_nodiscard  cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1)
-CX_EXPORT CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func,
+CX_EXTERN CX_NONNULL_ARG(2, 3, 4) CX_NODISCARD  CX_MALLOC CX_DEALLOC(cxTreeFree, 1)
+CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func,
         cx_tree_search_func search_func, cx_tree_search_data_func search_data_func,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
         ptrdiff_t loc_prev, ptrdiff_t loc_next);
@@ -899,8 +896,8 @@
  * @return the new tree
  * @see cxTreeCreate()
  */
-cx_attr_nonnull_arg(2) cx_attr_nodiscard  cx_attr_malloc  cx_attr_dealloc(cxTreeFree, 1)
-CX_EXPORT CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
+CX_EXTERN CX_NONNULL_ARG(2) CX_NODISCARD  CX_MALLOC  CX_DEALLOC(cxTreeFree, 1)
+CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
         ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
@@ -916,8 +913,8 @@
  * @retval zero success
  * @retval non-zero failure
  */
-cx_attr_nonnull
-CX_EXPORT int cxTreeInsert(CxTree *tree, const void *data);
+CX_EXTERN CX_NONNULL
+int cxTreeInsert(CxTree *tree, const void *data);
 
 /**
  * Inserts elements provided by an iterator efficiently into the tree.
@@ -931,8 +928,8 @@
  * @param n the maximum number of elements to insert
  * @return the number of elements that could be successfully inserted
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n);
 
 /**
  * Inserts an array of data efficiently into the tree.
@@ -947,8 +944,8 @@
  * @param n the number of elements in the array
  * @return the number of elements that could be successfully inserted
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n);
 
 /**
  * Searches the data in the specified tree.
@@ -961,8 +958,8 @@
  * @param data the data to search for
  * @return the first matching node, or @c NULL when the data cannot be found
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cxTreeFind(CxTree *tree, const void *data);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxTreeFind(CxTree *tree, const void *data);
 
 /**
  * Searches the data in the specified subtree.
@@ -983,8 +980,8 @@
  * @param max_depth the maximum search depth
  * @return the first matching node, or @c NULL when the data cannot be found
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth);
 
 /**
  * Determines the size of the specified subtree.
@@ -993,8 +990,8 @@
  * @param subtree_root the root node of the subtree
  * @return the number of nodes in the specified subtree
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
 
 /**
  * Determines the depth of the specified subtree.
@@ -1003,8 +1000,8 @@
  * @param subtree_root the root node of the subtree
  * @return the tree depth including the @p subtree_root
  */
-cx_attr_nonnull  cx_attr_nodiscard
-CX_EXPORT size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
 
 /**
  * Determines the size of the entire tree.
@@ -1012,8 +1009,8 @@
  * @param tree the tree
  * @return the tree size, counting the root as one
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeSize(CxTree *tree);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeSize(CxTree *tree);
 
 /**
  * Determines the depth of the entire tree.
@@ -1021,8 +1018,8 @@
  * @param tree the tree
  * @return the tree depth, counting the root as one
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeDepth(CxTree *tree);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeDepth(CxTree *tree);
 
 /**
  * Creates a depth-first iterator for the specified tree starting in @p node.
@@ -1036,8 +1033,8 @@
  * @return a tree iterator (depth-first)
  * @see cxTreeVisit()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit);
 
 /**
  * Creates a breadth-first iterator for the specified tree starting in @p node.
@@ -1049,8 +1046,8 @@
  * @return a tree visitor (a.k.a. breadth-first iterator)
  * @see cxTreeIterate()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node);
 
 /**
  * Creates a depth-first iterator for the specified tree.
@@ -1061,8 +1058,8 @@
  * @return a tree iterator (depth-first)
  * @see cxTreeVisit()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit);
 
 /**
  * Creates a breadth-first iterator for the specified tree.
@@ -1071,8 +1068,8 @@
  * @return a tree visitor (a.k.a. breadth-first iterator)
  * @see cxTreeIterate()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeVisitor cxTreeVisit(CxTree *tree);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeVisitor cxTreeVisit(CxTree *tree);
 
 /**
  * Sets the (new) parent of the specified child.
@@ -1085,8 +1082,8 @@
  * @param child the node to add
  * @see cxTreeAddChildNode()
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeSetParent(CxTree *tree, void *parent, void *child);
+CX_EXTERN CX_NONNULL
+void cxTreeSetParent(CxTree *tree, void *parent, void *child);
 
 /**
  * Adds a new node to the tree.
@@ -1103,8 +1100,8 @@
  * @param child the node to add
  * @see cxTreeSetParent()
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeAddChildNode(CxTree *tree, void *parent, void *child);
+CX_EXTERN CX_NONNULL
+void cxTreeAddChildNode(CxTree *tree, void *parent, void *child);
 
 /**
  * Creates a new node and adds it to the tree.
@@ -1123,8 +1120,8 @@
  * @return zero when the new node was created, non-zero on allocation failure
  * @see cxTreeInsert()
  */
-cx_attr_nonnull
-CX_EXPORT int cxTreeAddChild(CxTree *tree, void *parent, const void *data);
+CX_EXTERN CX_NONNULL
+int cxTreeAddChild(CxTree *tree, void *parent, const void *data);
 
 /**
  * A function that is invoked when a node needs to be re-linked to a new parent.
@@ -1158,8 +1155,8 @@
  * node
  * @return zero on success, non-zero if @p node is the root node of the tree
  */
-cx_attr_nonnull_arg(1, 2)
-CX_EXPORT int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
+CX_EXTERN CX_NONNULL_ARG(1, 2)
+int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
 
 /**
  * Removes a node and its subtree from the tree.
@@ -1172,8 +1169,8 @@
  * @param tree the tree
  * @param node the node to remove
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeRemoveSubtree(CxTree *tree, void *node);
+CX_EXTERN CX_NONNULL
+void cxTreeRemoveSubtree(CxTree *tree, void *node);
 
 /**
  * Destroys a node and re-links its children to its former parent.
@@ -1193,11 +1190,7 @@
  * node
  * @return zero on success, non-zero if @p node is the root node of the tree
  */
-cx_attr_nonnull_arg(1, 2)
-CX_EXPORT int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL_ARG(1, 2)
+int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
 
 #endif //UCX_TREE_H
--- a/src/hash_key.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/hash_key.c	Sun Dec 28 17:31:20 2025 +0100
@@ -63,14 +63,14 @@
     switch (len) {
         case 3:
             h ^= (data[i + 2] & 0xFF) << 16;
-            cx_attr_fallthrough;
+            CX_FALLTHROUGH;
         case 2:
             h ^= (data[i + 1] & 0xFF) << 8;
-            cx_attr_fallthrough;
+            CX_FALLTHROUGH;
         case 1:
             h ^= (data[i + 0] & 0xFF);
             h *= m;
-            cx_attr_fallthrough;
+            CX_FALLTHROUGH;
         default: // do nothing
             ;
     }
--- a/src/json.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/json.c	Sun Dec 28 17:31:20 2025 +0100
@@ -1499,7 +1499,7 @@
 }
 
 CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source,
-        const CxAllocator* allocator, cx_attr_unused void *data) {
+        const CxAllocator* allocator, CX_UNUSED void *data) {
     if (source == NULL || source->type == CX_JSON_NOTHING) {
         return &cx_json_value_nothing;
     }
@@ -1511,7 +1511,8 @@
             return ret; \
         } else { \
             *target = *ret; \
-            cxFree(allocator, ret); \
+            ret->type = CX_JSON_UNINITIALIZED; \
+            cxJsonValueFree(ret); \
             return target; \
         } \
     }
--- a/src/kv_list.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/kv_list.c	Sun Dec 28 17:31:20 2025 +0100
@@ -515,7 +515,7 @@
 }
 
 static int cx_kvl_change_capacity(struct cx_list_s *list,
-        cx_attr_unused size_t cap) {
+        CX_UNUSED size_t cap) {
     // since our backing list is a linked list, we don't need to do much here,
     // but rehashing the map is quite useful
     cx_kv_list *kv_list = (cx_kv_list*)list;
--- a/src/list.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/list.c	Sun Dec 28 17:31:20 2025 +0100
@@ -63,33 +63,33 @@
 
 // <editor-fold desc="empty list implementation">
 
-static void cx_emptyl_noop(cx_attr_unused CxList *list) {
+static void cx_emptyl_noop(CX_UNUSED CxList *list) {
     // this is a noop, but MUST be implemented
 }
 
 static void *cx_emptyl_at(
-        cx_attr_unused const struct cx_list_s *list,
-        cx_attr_unused size_t index
+        CX_UNUSED const struct cx_list_s *list,
+        CX_UNUSED size_t index
 ) {
     return NULL;
 }
 
 static size_t cx_emptyl_find_remove(
-        cx_attr_unused struct cx_list_s *list,
-        cx_attr_unused const void *elem,
-        cx_attr_unused bool remove
+        CX_UNUSED struct cx_list_s *list,
+        CX_UNUSED const void *elem,
+        CX_UNUSED bool remove
 ) {
     return 0;
 }
 
-static bool cx_emptyl_iter_valid(cx_attr_unused const void *iter) {
+static bool cx_emptyl_iter_valid(CX_UNUSED const void *iter) {
     return false;
 }
 
 static CxIterator cx_emptyl_iterator(
         const struct cx_list_s *list,
         size_t index,
-        cx_attr_unused bool backwards
+        CX_UNUSED bool backwards
 ) {
     CxIterator iter = {0};
     iter.src_handle = (void*) list;
--- a/src/map.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/map.c	Sun Dec 28 17:31:20 2025 +0100
@@ -33,24 +33,24 @@
 
 // <editor-fold desc="empty map implementation">
 
-static void cx_empty_map_noop(cx_attr_unused CxMap *map) {
+static void cx_empty_map_noop(CX_UNUSED CxMap *map) {
     // this is a noop, but MUST be implemented
 }
 
 static void *cx_empty_map_get(
-        cx_attr_unused const CxMap *map,
-        cx_attr_unused CxHashKey key
+        CX_UNUSED const CxMap *map,
+        CX_UNUSED CxHashKey key
 ) {
     return NULL;
 }
 
-static bool cx_empty_map_iter_valid(cx_attr_unused const void *iter) {
+static bool cx_empty_map_iter_valid(CX_UNUSED const void *iter) {
     return false;
 }
 
 static CxMapIterator cx_empty_map_iterator(
         const struct cx_map_s *map,
-        cx_attr_unused enum cx_map_iterator_type type
+        CX_UNUSED enum cx_map_iterator_type type
 ) {
     CxMapIterator iter = {0};
     iter.map = (CxMap*) map;
--- a/src/tree.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/src/tree.c	Sun Dec 28 17:31:20 2025 +0100
@@ -417,7 +417,7 @@
     return iter->node;
 }
 
-cx_attr_nonnull
+CX_NONNULL
 static void cx_tree_visitor_enqueue_siblings(
         struct cx_tree_visitor_s *iter, void *node, ptrdiff_t loc_next) {
     node = tree_next(node);
--- a/tests/test_allocator.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/tests/test_allocator.c	Sun Dec 28 17:31:20 2025 +0100
@@ -297,9 +297,9 @@
 }
 
 static void *test_allocator_mock_failing_realloc(
-        cx_attr_unused void *p,
-        cx_attr_unused void *d,
-        cx_attr_unused size_t n
+        CX_UNUSED void *p,
+        CX_UNUSED void *d,
+        CX_UNUSED size_t n
 ) {
     return NULL;
 }
--- a/tests/test_hash_map.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/tests/test_hash_map.c	Sun Dec 28 17:31:20 2025 +0100
@@ -359,7 +359,7 @@
 }
 
 static void test_advanced_destructor(
-        cx_attr_unused void *unused,
+        CX_UNUSED void *unused,
         void *d
 ) {
     test_simple_destructor(d);
--- a/tests/test_json.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/tests/test_json.c	Sun Dec 28 17:31:20 2025 +0100
@@ -1041,7 +1041,8 @@
         CX_TEST_ASSERT(result == CX_JSON_NO_ERROR);
         CX_TEST_ASSERT(cxJsonIsObject(v));
         CxJsonValue *value = cxJsonObjGet(v, "value");
-        CX_TEST_ASSERT(cxJsonAsString(value));
+        CX_TEST_ASSERT(cxJsonIsString(value));
+        CX_TEST_ASSERT(!cx_strcmp(cxJsonAsCxString(value), "test"));
         cxJsonValueFree(v);
         // read array
         cxJsonFill(&json, "[ 0, 1, 2, 3, 4, 5 ]\n");
--- a/tests/test_list.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/tests/test_list.c	Sun Dec 28 17:31:20 2025 +0100
@@ -1916,7 +1916,7 @@
 }
 #define roll_out_test_combos(name, body) \
 static CX_TEST_SUBROUTINE(test_list_verify_##name, CxList *list, \
-    cx_attr_unused bool isptrlist) body \
+    CX_UNUSED bool isptrlist) body \
 roll_out_test_invokers(name)
 
 static void set_default_class_funcs(CxList *list, cx_list_class *defaulted_cl) {
@@ -1935,7 +1935,7 @@
     set_default_class_funcs(list, &defaulted_cl)
 #define roll_out_test_combos_with_defaulted_funcs(name, body) \
 static CX_TEST_SUBROUTINE(test_list_verify_##name, CxList *list, \
-    cx_attr_unused bool isptrlist) body \
+    CX_UNUSED bool isptrlist) body \
 roll_out_test_invokers(name) \
 CX_TEST(test_list_llm_##name) { \
     set_up_combo \
@@ -2432,7 +2432,7 @@
 })
 
 static unsigned test_remove_array_destr_ctr;
-static void test_remove_array_destr(cx_attr_unused void *d) {
+static void test_remove_array_destr(CX_UNUSED void *d) {
     test_remove_array_destr_ctr++;
 }
 
@@ -2979,7 +2979,7 @@
     destr_test_ctr++;
 }
 
-static void advanced_destr_test_fun(cx_attr_unused void *u, void *data) {
+static void advanced_destr_test_fun(CX_UNUSED void *u, void *data) {
     simple_destr_test_fun(data);
 }
 
--- a/tests/test_mempool.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/tests/test_mempool.c	Sun Dec 28 17:31:20 2025 +0100
@@ -34,11 +34,11 @@
 
 static unsigned test_mempool_destructor_called;
 
-static void test_mempool_destructor(cx_attr_unused void *mem) {
+static void test_mempool_destructor(CX_UNUSED void *mem) {
     test_mempool_destructor_called++;
 }
 
-static void test_mempool_destructor2(void *data, cx_attr_unused void *mem) {
+static void test_mempool_destructor2(void *data, CX_UNUSED void *mem) {
     int *ctr = data;
     *ctr = *ctr + 1;
 }
--- a/tests/test_tree.c	Sun Dec 28 15:45:39 2025 +0100
+++ b/tests/test_tree.c	Sun Dec 28 17:31:20 2025 +0100
@@ -2093,8 +2093,8 @@
 
 static void test_tree_remove_node_relink_mock(
         void *node,
-        cx_attr_unused const void *oldp,
-        cx_attr_unused const void *newp
+        CX_UNUSED const void *oldp,
+        CX_UNUSED const void *newp
 ) {
     tree_node_file * n = node;
     // this function fakes the relink logic in below test

mercurial