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> |