| 40 |
36 |
| 41 > When you create a list which is storing pointers and do not specify a compare function, `cx_cmp_ptr` will be used by default. |
37 > When you create a list which is storing pointers and do not specify a compare function, `cx_cmp_ptr` will be used by default. |
| 42 |
38 |
| 43 ## Example |
39 ## Example |
| 44 |
40 |
| 45 <warning> |
41 In the following example we create a linked-list of regular expressions for filtering data. |
| 46 TODO: add example how to work with lists |
42 |
| 47 </warning> |
43 ```C |
| |
44 #include <cx/linked_list.h> |
| |
45 #include <regex.h> |
| |
46 |
| |
47 CxList *create_pattern_list(void) { |
| |
48 // create a linked list to store patterns |
| |
49 CxList *list = cxLinkedListCreateSimple(sizeof(regex_t)); |
| |
50 |
| |
51 // automatically free the pattern when list gets destroyed |
| |
52 cxDefineDestructor(list, regfree); |
| |
53 |
| |
54 return list; |
| |
55 } |
| |
56 |
| |
57 int add_pattern(CxList *list, const char *value) { |
| |
58 // compile pattern and add it to the list, if successful |
| |
59 regex_t regex; |
| |
60 if (regcomp(®ex, value, REG_EXTENDED|REG_NOSUB)) { |
| |
61 return 1; |
| |
62 } else { |
| |
63 // take address of local variable |
| |
64 // since we are not storing pointers in this list, |
| |
65 // we get a copy of the data directly in the list |
| |
66 cxListAdd(list, ®ex); |
| |
67 return 0; |
| |
68 } |
| |
69 } |
| |
70 |
| |
71 bool matches_any(CxList *list, const char *text) { |
| |
72 CxIterator iter = cxListIterator(list); |
| |
73 cx_foreach(regex_t*, pat, iter) { |
| |
74 if (regexec(pat, text, 0, NULL, 0) == 0) { |
| |
75 return true; |
| |
76 } |
| |
77 } |
| |
78 return false; |
| |
79 } |
| |
80 ``` |
| |
81 |
| |
82 If in the above example the list was created with `CX_STORE_POINTERS` instead of `sizeof(regex_t)`, |
| |
83 the `add_pattern()` function would need to be changed as follows: |
| |
84 |
| |
85 ```C |
| |
86 int add_pattern(CxList *list, const char *value) { |
| |
87 // allocate memory here, because now we are storing pointers |
| |
88 regex_t *regex = malloc(sizeof(regex_t)); |
| |
89 if (!regex || regcomp(regex, value, REG_EXTENDED|REG_NOSUB)) { |
| |
90 return 1; |
| |
91 } else { |
| |
92 cxListAdd(list, regex); |
| |
93 return 0; |
| |
94 } |
| |
95 } |
| |
96 ``` |
| |
97 |
| |
98 Also, just registering `regfree()` as destructor is not sufficient anymore, because the `regex_t` structure also needs to be freed. |
| |
99 Therefore, we would need to wrap the calls to `regfree()` and `free()` into an own destructor, which we then register with the list. |
| |
100 However, it should be clear by now that using `CX_STORE_POINTERS` is a bad choice for this use case to begin with. |
| |
101 |
| |
102 As a rule of thumb: if you allocate memory for an element that you immediately put into the list, consider storing the element directly. |
| |
103 And if you are getting pointers to already allocated memory from somewhere else, and you just want to organize those elements in a list, then consider using `CX_STORE_POINTERS`. |
| 48 |
104 |
| 49 > If you want to lazy-initialize lists, you can use the global `cxEmptyList` symbol as a placeholder instead of using a `NULL`-pointer. |
105 > If you want to lazy-initialize lists, you can use the global `cxEmptyList` symbol as a placeholder instead of using a `NULL`-pointer. |
| 50 > While you *must not* insert elements into that list, you can safely access this list or create iterators. |
106 > While you *must not* insert elements into that list, you can safely access this list or create iterators. |
| 51 > This allows you to write clean code without checking for `NULL`-pointer everywhere. |
107 > This allows you to write clean code without checking for `NULL`-pointer everywhere. |
| 52 > You still need to make sure that the placeholder is replaced with an actual list before inserting elements. |
108 > You still need to make sure that the placeholder is replaced with an actual list before inserting elements. |
| 77 |
133 |
| 78 The function `cxListAdd()` appends the element `elem` to the list and returns zero on success or non-zero when allocating the memory for the element fails. |
134 The function `cxListAdd()` appends the element `elem` to the list and returns zero on success or non-zero when allocating the memory for the element fails. |
| 79 Similarly, `cxListInsert()` adds the element at the specified `index`, |
135 Similarly, `cxListInsert()` adds the element at the specified `index`, |
| 80 and `cxListInsertSorted()` adds the element at the correct position so that the list remains sorted according to the list's compare function. |
136 and `cxListInsertSorted()` adds the element at the correct position so that the list remains sorted according to the list's compare function. |
| 81 |
137 |
| 82 When you are currently iterating through a list, you can insert items before or after the current position of the iterator |
138 When you are currently iterating through a list, you can insert elements before or after the current position of the iterator |
| 83 with `cxListInsertBefore()` or `cxListInsertAfter()`, respectively. |
139 with `cxListInsertBefore()` or `cxListInsertAfter()`, respectively. |
| 84 This _should_ be done with [mutating iterators](iterator.h.md#mutating-iterators) only, but is also defined for normal iterators. |
140 This _should_ be done with [mutating iterators](iterator.h.md#mutating-iterators) only, but is also defined for normal iterators. |
| 85 |
141 |
| 86 If the list is storing pointers (cf. `cxCollectionStoresPointers()`), the pointer `elem` is directly added to the list. |
142 If the list is storing pointers (cf. `cxCollectionStoresPointers()`), the pointer `elem` is directly added to the list. |
| 87 Otherwise, the contents at the location pointed to by `elem` are copied to the list's memory with the element size specified during creation of the list. |
143 Otherwise, the contents at the location pointed to by `elem` are copied to the list's memory with the element size specified during creation of the list. |
| 259 or any of the destructor functions deallocates the memory. |
315 or any of the destructor functions deallocates the memory. |
| 260 Otherwise, there is a risk of a memory leak. |
316 Otherwise, there is a risk of a memory leak. |
| 261 |
317 |
| 262 ## Implement own List Structures |
318 ## Implement own List Structures |
| 263 |
319 |
| 264 <warning> |
320 If you want to create your own list implementation, this is extremely easy. |
| 265 TODO: add documentation |
321 |
| 266 </warning> |
322 You just need to define a function for creating your list and assign a `cx_list_class` structure with the pointers to your implementation. |
| |
323 Then you call the `cx_list_init()` helper function to initialize the collection sture. |
| |
324 This also automatically adds support for `CX_STORE_POINTERS` to your list. |
| |
325 |
| |
326 ```C |
| |
327 // define the class with pointers to your functions |
| |
328 static cx_list_class my_list_class = { |
| |
329 my_list_destructor, |
| |
330 my_list_insert_element, |
| |
331 my_list_insert_array, |
| |
332 my_list_insert_sorted, |
| |
333 my_list_insert_iter, |
| |
334 my_list_remove, |
| |
335 my_list_clear, |
| |
336 my_list_swap, |
| |
337 my_list_at, |
| |
338 my_list_find_remove, |
| |
339 my_list_sort, |
| |
340 my_list_compare, |
| |
341 my_list_reverse, |
| |
342 my_list_iterator, |
| |
343 }; |
| |
344 |
| |
345 typedef struct { |
| |
346 struct cx_list_s base; |
| |
347 // your custom list data goes here |
| |
348 } my_list; |
| |
349 |
| |
350 CxList *my_list_create( |
| |
351 const CxAllocator *allocator, |
| |
352 cx_compare_func cmpfun, |
| |
353 size_t elem_size |
| |
354 ) { |
| |
355 if (allocator == NULL) { |
| |
356 allocator = cxDefaultAllocator; |
| |
357 } |
| |
358 |
| |
359 my_list *list = cxCalloc(allocator, 1, sizeof(my_list)); |
| |
360 if (list == NULL) return NULL; |
| |
361 cx_list_init((CxList*)list, &my_list_class, |
| |
362 allocator, cmpfun, elem_size); |
| |
363 |
| |
364 // initialize your custom list data here |
| |
365 |
| |
366 return (CxList *) list; |
| |
367 } |
| |
368 ``` |
| |
369 |
| |
370 The required behavior for the implementations is described in the following table. |
| |
371 You can always look at the source code of the UCX implementations to get inspiration. |
| |
372 |
| |
373 | Function | Description | |
| |
374 |------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| |
375 | `clear` | Invoke destructor functions on all elements and remove them from the list. | |
| |
376 | `destructor` | Invoke destructor functions on all elements and deallocate the entire list memory. | |
| |
377 | `insert_element` | Insert a single element at the specified index. Return zero on success and non-zero on failure. | |
| |
378 | `insert_array` | Insert an array of elements starting at the specified index. Return the number of elements inserted. | |
| |
379 | `insert_sorted` | Insert an array of sorted elements into a sorted list. Return the number of elements inserted. | |
| |
380 | `insert_iter` | Insert a single element depending on the iterator position. The third argument to this function is zero when the element shall be inserted after the iterator position and non-zero if it shall be inserted before the iterator position. The implementation is also responsible for adjusting the iterator, respectively. | |
| |
381 | `remove` | Removes a multiple elements starting at the specified index. If a target buffer is specified, copy the elements to that buffer. Otherwise, invoke the destructor functions for the elements. Return the number of elements actually removed. | |
| |
382 | `swap` | Swap two elements by index. Return zero on success or non-zero when any index was out-of-bounds. | |
| |
383 | `at` | Return a pointer to the element at the specified index or `NULL` when the index is out-of-bounds. | |
| |
384 | `find_remove` | Search for the specified element with the list's compare function and return the index if found. If the `remove` argument is true, invoke the destructor functions and remove the element. Return the list size if the element is not found. | |
| |
385 | `sort` | Sort all elements in the list with respect to the list's compare function. | |
| |
386 | `compare` | Only function pointer that may be `NULL`, in which case an unoptimized fallback is used. You can implement an optimized compare function that compares the list of another list of the same class. | |
| |
387 | `reverse` | Reorders all elements in the list so that they appear in exactly the opposite order. | |
| |
388 | `iterator` | Creates an iterator starting at the specified index. The Boolean argument indicates whether iteration is supposed to traverse backwards. | |
| |
389 |
| |
390 > If you initialize your list with `cx_list_init()` you do not have to worry about making a |
| |
391 > difference between storing pointers and storing elements, because your implementation will |
| |
392 > be automatically wrapped. |
| |
393 > This means, you only have to handle the one single case described above. |
| |
394 |
| |
395 ### Default Class Function Implementations |
| |
396 |
| |
397 If you are satisfied with some of the default implementations, you can use some pre-defined functions. |
| |
398 Note, however, that those are not optimized for any specific list structure |
| |
399 and just serve as a quick and convenient solution if performance does not matter for your use case. |
| |
400 |
| |
401 |
| |
402 | Default Function | Description | |
| |
403 |---------------------------------|-----------------------------------------------------------------------------------| |
| |
404 | `cx_list_default_insert_array` | Falls back to multiple calls of `insert_element`. | |
| |
405 | `cx_list_default_insert_sorted` | Uses linear search to find insertion points. | |
| |
406 | `cx_list_default_sort` | Copies all elements to an array, applies `qsort()`, and copies the elements back. | |
| |
407 | `cx_list_default_swap` | Uses a temporarily allocated buffer to perform a three-way-swap. | |
| |
408 |
| 267 |
409 |
| 268 <seealso> |
410 <seealso> |
| 269 <category ref="apidoc"> |
411 <category ref="apidoc"> |
| 270 <a href="https://ucx.sourceforge.io/api/list_8h.html">list.h</a> |
412 <a href="https://ucx.sourceforge.io/api/list_8h.html">list.h</a> |
| 271 </category> |
413 </category> |