| 27 > If you want to lazy-initialize maps, you can use the global `cxEmptyMap` symbol as a placeholder instead of using a `NULL`-pointer. |
27 > If you want to lazy-initialize maps, you can use the global `cxEmptyMap` symbol as a placeholder instead of using a `NULL`-pointer. |
| 28 > While you *must not* insert elements into that map, you can safely access this map or create iterators. |
28 > While you *must not* insert elements into that map, you can safely access this map or create iterators. |
| 29 > This allows you to write clean code without checking for `NULL`-pointer everywhere. |
29 > This allows you to write clean code without checking for `NULL`-pointer everywhere. |
| 30 > You still need to make sure that the placeholder is replaced with an actual map before inserting elements. |
30 > You still need to make sure that the placeholder is replaced with an actual map before inserting elements. |
| 31 |
31 |
| 32 ## Overview |
32 ## Examples |
| 33 |
33 |
| 34 <warning> |
34 ```C |
| 35 TODO: example |
35 #include <stdio.h> |
| 36 </warning> |
36 #include <cx/hash_map.h> |
| |
37 |
| |
38 int main() { |
| |
39 CxMap *contacts = cxHashMapCreateSimple(CX_STORE_POINTERS); |
| |
40 |
| |
41 // Build a small phone book |
| |
42 cxMapPut(contacts, "John", "123-0815"); |
| |
43 cxMapPut(contacts, "Jane", "987-4711"); |
| |
44 cxMapPut(contacts, "Michelle", "555-3141"); |
| |
45 cxMapPut(contacts, "Oliver", "000-9999"); |
| |
46 |
| |
47 // Retrieve a phone number |
| |
48 const char *janes_phone = cxMapGet(contacts, "Jane"); |
| |
49 printf("The number of Jane is: %s\n", janes_phone); |
| |
50 |
| |
51 // Update number |
| |
52 cxMapPut(contacts, "Jane", "987-1337"); |
| |
53 |
| |
54 // Remove and retrieve number |
| |
55 cxMapRemoveAndGet(contacts, "Jane", &janes_phone); |
| |
56 printf("The number of Jane was: %s\n", janes_phone); |
| |
57 janes_phone = cxMapGet(contacts, "Jane"); |
| |
58 if (janes_phone == NULL) printf("Jane's number was deleted.\n"); |
| |
59 |
| |
60 // Iterate through the contact list |
| |
61 CxMapIterator iter = cxMapIterator(contacts); |
| |
62 cx_foreach(CxMapEntry *, entry, iter) { |
| |
63 cxstring name = cx_strn(entry->key->data, entry->key->len); |
| |
64 const char *phone = entry->value; |
| |
65 printf("%.*s: %s\n", (int) name.length, name.ptr, phone); |
| |
66 } |
| |
67 |
| |
68 cxMapFree(contacts); |
| |
69 |
| |
70 return 0; |
| |
71 } |
| |
72 ``` |
| |
73 |
| |
74 The above example illustrates basic operations with a map. |
| |
75 |
| |
76 In the first part we add several entries to the map. |
| |
77 Then the example shows retrieval, updating, and removal of information. |
| |
78 The last part shows how to iterate over the pairs of the map and how to recover the string from the key. |
| |
79 |
| |
80 In real world situations, however, it is quite unlikely that you will use a map to store string literals. |
| |
81 The next example shows a more realistic program, where it is necessary to store strings based on user input. |
| |
82 |
| |
83 ```C |
| |
84 #include <stdio.h> |
| |
85 #include <string.h> |
| |
86 #include <cx/hash_map.h> |
| |
87 |
| |
88 int main() { |
| |
89 // store strings in the map... |
| |
90 CxMap *contacts = cxHashMapCreateSimple(sizeof(cxmutstr)); |
| |
91 // ...which are automatically freed when removed |
| |
92 cxDefineDestructor(contacts, cx_strfree); |
| |
93 |
| |
94 // build a small interactive program |
| |
95 const unsigned buffer_size = 256; |
| |
96 char input[buffer_size]; |
| |
97 bool running = true; |
| |
98 |
| |
99 while (running) { |
| |
100 puts("\n*** Contacts - Menu ***"); |
| |
101 puts("1) Add entry"); |
| |
102 puts("2) Look up entry"); |
| |
103 puts("3) Remove entry"); |
| |
104 puts("4) Show all entries"); |
| |
105 puts("0) Exit"); |
| |
106 fputs("> ", stdout); |
| |
107 |
| |
108 if (fgets(input, buffer_size, stdin)) { |
| |
109 if (input[1] != '\n') { |
| |
110 puts("Please select a menu item.\n"); |
| |
111 } else if (input[0] == '1') { |
| |
112 fputs("Name > ", stdout); |
| |
113 if (fgets(input, buffer_size, stdin)) { |
| |
114 // Copy the name (alternative: use 2nd input buf) |
| |
115 cxmutstr name = cx_strdup( |
| |
116 cx_strn(input, strcspn(input, "\n"))); |
| |
117 fputs("Phone > ", stdout); |
| |
118 if (fgets(input, buffer_size, stdin)) { |
| |
119 // add the entry, note that only the contents |
| |
120 // of the cxmutstr are copied, not the string |
| |
121 // data - therefore we have to call cx_strdup |
| |
122 cxmutstr phone = cx_strdup( |
| |
123 cx_strn(input, strcspn(input, "\n"))); |
| |
124 // note, that we can simply use the cxmutstr |
| |
125 // for the name as key, because cxMapPut uses |
| |
126 // generic selection |
| |
127 cxMapPut(contacts, name, &phone); |
| |
128 } |
| |
129 cx_strfree(&name); |
| |
130 } |
| |
131 } else if (input[0] == '2') { |
| |
132 fputs("Name > ", stdout); |
| |
133 if (fgets(input, buffer_size, stdin)) { |
| |
134 // Look up the name |
| |
135 input[strcspn(input, "\n")] = '\0'; |
| |
136 cxmutstr *phone = cxMapGet(contacts, input); |
| |
137 if (phone == NULL) { |
| |
138 puts("No record."); |
| |
139 } else { |
| |
140 printf("Result: %.*s\n", |
| |
141 (int) phone->length, phone->ptr); |
| |
142 } |
| |
143 } |
| |
144 } else if (input[0] == '3') { |
| |
145 fputs("Name > ", stdout); |
| |
146 if (fgets(input, buffer_size, stdin)) { |
| |
147 // Remove the entry |
| |
148 // Since we registered cx_strfree as destructor, |
| |
149 // the memory is automatically freed |
| |
150 input[strcspn(input, "\n")] = '\0'; |
| |
151 if (cxMapRemove(contacts, input)) { |
| |
152 puts("No such record."); |
| |
153 } else { |
| |
154 puts("Removed."); |
| |
155 } |
| |
156 } |
| |
157 } else if (input[0] == '4') { |
| |
158 // Almost the same iteration loop as above ... |
| |
159 CxMapIterator iter = cxMapIterator(contacts); |
| |
160 cx_foreach(CxMapEntry *, entry, iter) { |
| |
161 cxstring name = cx_strn(entry->key->data, |
| |
162 entry->key->len); |
| |
163 // ... except that here we get cxmutstr* |
| |
164 cxmutstr *phone = entry->value; |
| |
165 printf("%.*s: %.*s\n", |
| |
166 (int) name.length, name.ptr, |
| |
167 (int) phone->length, phone->ptr); |
| |
168 } |
| |
169 } else if (input[0] == '0') { |
| |
170 running = false; |
| |
171 } else { |
| |
172 puts("Please select a menu item.\n"); |
| |
173 } |
| |
174 } else { |
| |
175 running = false; |
| |
176 } |
| |
177 } |
| |
178 |
| |
179 // this will free all remaining strings that are in the map |
| |
180 cxMapFree(contacts); |
| |
181 |
| |
182 return 0; |
| |
183 } |
| |
184 ``` |
| 37 |
185 |
| 38 > In the following Sections, the type for the key is denoted `KeyType`. |
186 > In the following Sections, the type for the key is denoted `KeyType`. |
| 39 > All functions are implemented as generics, so that the following types are supported: |
187 > All functions are implemented as generics, so that the following types are supported: |
| 40 > `CxHashKey`, `cxstring`, `cxmutstr`, `const char*`, and `char*`. |
188 > `CxHashKey`, `cxstring`, `cxmutstr`, `const char*`, and `char*`. |
| 41 > {style="note"} |
189 > {style="note"} |