data_set.h
1 // SPDX-FileCopyrightText: 2023-2025 Le'Sec Core collective 2 // 3 // SPDX-License-Identifier: LGPL-3.0-or-later 4 5 #ifndef LSC_DATA_SET_H 6 # define LSC_DATA_SET_H 7 8 # include <stddef.h> 9 # include <stdarg.h> 10 # include <leutils/status.h> 11 # include <lscore/macros.h> 12 # include <lscore/types.h> 13 14 // The data set is a C type factory, implemented as a macro. 15 // 16 // A typical template for the data set class |name| would be this for the 17 // simple case: 18 // 19 // LSC_IMPL_DATA_SET(name); 20 // 21 // or this, for the more complex case where extra library data is needed. 22 // 23 // LSC_DATA_SET_TYPES(name); 24 // LSC_DATA_SET_STRUCT(name) { 25 // LSC_DATA_SET_STRUCT_FIELDS(name); 26 // // Library fields added here 27 // }; 28 // LSC_DATA_SET_ALLOC_FUNCTIONS(name); 29 // LSC_DATA_SET_BASE_FUNCTIONS(name); 30 // 31 // In these examples, |name| is a "class name", which generates the following 32 // symbols (struct name, types and function names): 33 // 34 // LSC_{name}_st 35 // LSC_{name}_t 36 // LSC_{name}_dispatch_fn 37 // LSC_{name}_destroy_fn 38 // LSC_new_{name}() 39 // LSC_free_{name}() 40 // 41 // The data set fields are: 42 // 43 // lsc_dispatch The dispatch function, which performs all the 44 // commands based on a command number. 45 // lsc_destroy The destroy function, which destroys the data set. 46 // lsc_dispatch_data A constant pointer of data to be used by 47 // lsc_dispatch and lsc_destroy. 48 // This can be seen as (hidden) class data. 49 // lsc_data The pointer to the internal data set data. 50 // This can be seen as (hidden) instance data. 51 52 # define LSC_DATA_SET_TYPES(lsc_name) \ 53 typedef struct LSC_NAME(lsc_name##_st) LSC_NAME(lsc_name##_t); \ 54 typedef LE_STATUS \ 55 LSC_NAME(lsc_name##_dispatch_fn)(LSC_NAME(lsc_name##_t) *lsc_data_set, \ 56 int lsc_num, ...); \ 57 typedef LE_STATUS \ 58 LSC_NAME(lsc_name##_destroy_fn)(LSC_NAME(lsc_name##_t) *lsc_data_set) 59 # define LSC_DATA_SET_STRUCT(lsc_name) \ 60 struct LSC_NAME(lsc_name##_st) 61 # define LSC_DATA_SET_STRUCT_FIELDS(lsc_name) \ 62 LSC_NAME(lsc_name##_dispatch_fn) *lsc_dispatch; \ 63 LSC_NAME(lsc_name##_destroy_fn) *lsc_destroy; \ 64 const void *lsc_dispatch_data; \ 65 void *lsc_data 66 # define LSC_DATA_SET_NEW_FUNCTION(lsc_name) \ 67 LSC_EXPORT LE_STATUS \ 68 LSC_NAME(new_##lsc_name)(LSC_NAME(lsc_name##_t) **lsc_data_set) 69 # define LSC_DATA_SET_FREE_FUNCTION(lsc_name) \ 70 static inline LE_STATUS \ 71 LSC_NAME(free_##lsc_name)(LSC_NAME(lsc_name##_t) *lsc_data_set) \ 72 { \ 73 if (lsc_data_set != NULL && lsc_data_set->lsc_destroy != NULL) \ 74 return lsc_data_set->lsc_destroy(lsc_data_set); \ 75 return LE_STS_SUCCESS; \ 76 } 77 # define LSC_DATA_SET_ALLOC_FUNCTIONS(lsc_name) \ 78 LSC_DATA_SET_NEW_FUNCTION(lsc_name); \ 79 LSC_DATA_SET_FREE_FUNCTION(lsc_name) 80 81 // Reserve a set of command numbers for common data set dispatch commands. 82 // Note the double underscore after 'LSC_NR', which indicates that these 83 // aren't to be used as normal command numbers. 84 // 85 // LSC_NR__data_set_class_start indicates where those common numbers stop, 86 // and where commands for specific C types (data set classes) built with 87 // this factory may start their C type (data set class) specific command 88 // number series. 89 // 90 // Do note that each such C type (data set class) may start their own series 91 // from the same number, i.e. their specific command number series may 92 // (in fact, is assumed to) overlap. This requires that all C types (data 93 // set classes) are distinguishable by the compiler, or when this isn't 94 // the case, that proper checks are done (see the standard identification 95 // commands further down, as they can help in that endeavour). 96 // 97 // Also, command number zero is forbidden and is considered unsupported. 98 99 // For common data set commands, 1..31 is reserved 100 # define LSC_NR__data_set_start 1 101 // data set class may start their number series from here 102 # define LSC_NR__data_set_class_start 32 103 104 // Data sets that are part of a larger eco-system of interconnected 105 // instances of various types are strongly encouraged to include support 106 // for these minimum common functions. 107 // 108 // The generated functions are: 109 // 110 // LSC_get_{lsc_name}_class(): 111 // Gives back a class name, preferably corresponding to {lsc_long_name}. 112 // The exact meaning and class name is left for the dispatch implementation. 113 // Some other functionality may use this as a registration tag, so it should 114 // be carefully chosen to be unique enough within that eco-system. 115 // LSC_can_{lsc_name}_do(): 116 // Given a command number, this gives back true if this implementation 117 // can perform that command number. 118 // 119 // For dispatch implementation, see the corresponding implementation 120 // macros, LSC_DATA_SET_BASE_COMMANDS() and LSC_DATA_SET_BASE_DISPATCHES(), 121 // below. 122 # define LSC_DATA_SET_BASE_FUNCTIONS(lsc_name) \ 123 enum { \ 124 LSC_NR_get_##lsc_name##_class = 1, \ 125 LSC_NR_can_##lsc_name##_do = 2, \ 126 }; \ 127 static inline LE_STATUS \ 128 LSC_NAME(get_##lsc_name##_class) \ 129 (LSC_NAME(lsc_name##_t) *lsc_data_set, const char **lsc_class) \ 130 { \ 131 if (lsc_data_set == NULL || lsc_class == NULL) \ 132 return LE_STS_ERROR; \ 133 return \ 134 lsc_data_set->lsc_dispatch(lsc_data_set, \ 135 LSC_NR_get_##lsc_name##_class, \ 136 lsc_class); \ 137 } \ 138 static inline LE_STATUS \ 139 LSC_NAME(can_##lsc_name##_do) \ 140 (LSC_NAME(lsc_name##_t) *lsc_data_set, int lsc_cmd, _Bool *lsc_b) \ 141 { \ 142 if (lsc_data_set == NULL) \ 143 return LE_STS_ERROR; \ 144 return lsc_data_set->lsc_dispatch(lsc_data_set, \ 145 LSC_NR_can_##lsc_name##_do, \ 146 lsc_cmd, lsc_b); \ 147 } 148 149 // For dispatch implementations of the minimum common functions, the following 150 // two macros can be helpful: 151 // 152 // LSC_DATA_SET_BASE_COMMANDS() 153 // This offers the list of command numbers, to be used in an array. 154 // It takes no arguments. 155 // LSC_DATA_SET_BASE_DISPATCHES() 156 // This offers a standard implementation for the dispatch function. It 157 // takes the following arguments: 158 // 159 // lsc_prefix: the prefix for class names. This can be seen 160 // as a namespace for the class or type being made. 161 // lsc_name: the object class name 162 // lsc_obj: the object variable 163 // lsc_status: the LE_STATUS variable 164 // lsc_valist: the va_list variable 165 // lsc_cmds: the variable of supported extra command numbers. 166 // This must be a zero-terminated array. 167 // 168 // They are meant to be used together like this: 169 // 170 // LE_STATUS dispatch(LSC_{lsc_name}_t *ds, int num, ...) 171 // { 172 // LE_STATUS sts = LT_STS_WARNING; // There should be a better warning 173 // va_list ap; 174 // static const int cmds[] = { 175 // LSC_DATA_SET_BASE_COMMANDS({lsc_name}), 176 // // ... 177 // // There are certainly other similar dispatch macros, or custom commands 178 // // ... 179 // 0 180 // }; 181 // 182 // va_start(ap, num); 183 // switch (num) { 184 // LSC_DATA_SET_BASE_DISPATCHES({lsc_name}, ds, sts, ap, cmds); 185 // // ... 186 // // There are certainly other similar dispatch macros, or custom cases 187 // // ... 188 // } 189 // va_end(ap, num); 190 // 191 // return sts; 192 // } 193 # define LSC_DATA_SET_BASE_COMMANDS(lsc_name) \ 194 LSC_NR_get_##lsc_name##_class, LSC_NR_can_##lsc_name##_do 195 # define LSC_DATA_SET_BASE_DISPATCHES(lsc_prefix, lsc_name, lsc_obj, \ 196 lsc_status, lsc_valist, lsc_cmds) \ 197 case LSC_NR_get_##lsc_name##_class: \ 198 { \ 199 const char **lsc_result \ 200 = va_arg(lsc_valist, const char **); /* Result */ \ 201 \ 202 *lsc_result = lsc_prefix #lsc_name; \ 203 } \ 204 lsc_status = LE_STS_SUCCESS; \ 205 break; \ 206 case LSC_NR_can_##lsc_name##_do: \ 207 lsc_status = LE_STS_ERROR; \ 208 { \ 209 int lsc_idx; \ 210 int lsc_cmd = va_arg(lsc_valist, int); \ 211 _Bool *lsc_result \ 212 = va_arg(lsc_valist, _Bool *); /* Result */ \ 213 \ 214 if (!(*lsc_result \ 215 = (lsc_cmd == LSC_NR_get_##lsc_name##_class \ 216 || lsc_cmd == LSC_NR_can_##lsc_name##_do))) \ 217 for (*lsc_result = 0, lsc_idx = 0; \ 218 lsc_cmds[lsc_idx] != 0; lsc_idx++) { \ 219 if ((*lsc_result = (lsc_cmds[lsc_idx] == lsc_cmd))) \ 220 break; \ 221 } \ 222 } \ 223 lsc_status = LE_STS_SUCCESS; \ 224 break 225 226 // ---------------------------------------------------------------- 227 228 // Data sets that support "subclassing" (i.e. varied dispatch and 229 // destroy functions) may need further identification. This function 230 // may be useful for this: 231 // 232 // LSC_get_{lsc_name}_identity(): 233 // Gives back the identity of the object class implementation (or subclass 234 // if you will). This is useful for types made with this factory where 235 // multiple dispatch implementations make sense, and they need to be 236 // identified with that level of granularity. 237 // 238 // For dispatch implementation, see the corresponding implementation 239 // macros, LSC_DATA_SET_ID_COMMANDS() and LSC_DATA_SET_ID_DISPATCHES(), 240 // below. 241 # define LSC_DATA_SET_ID_FUNCTIONS(lsc_name) \ 242 enum { \ 243 LSC_NR_get_##lsc_name##_identity = 3, \ 244 }; \ 245 static inline LE_STATUS \ 246 LSC_NAME(get_##lsc_name##_identity) \ 247 (LSC_NAME(lsc_name##_t) *lsc_data_set, const char **lsc_id) \ 248 { \ 249 if (lsc_data_set == NULL) \ 250 return LE_STS_ERROR; \ 251 return \ 252 lsc_data_set->lsc_dispatch(lsc_data_set, \ 253 LSC_NR_get_##lsc_name##_identity, \ 254 lsc_id); \ 255 } 256 257 // For dispatch implementations of identity functions, the following two 258 // macros can be helpful: 259 // 260 // LSC_DATA_SET_ID_COMMANDS() 261 // This offers the list of command numbers, to be used in an array. 262 // It takes no arguments. 263 // LSC_DATA_SET_ID_DISPATCHES() 264 // This offers a standard implementation for the dispatch function. It 265 // takes the following arguments: 266 // 267 // lsc_id: the identity (an rvalue of type char[] or char*) 268 // lsc_obj: the data set variable (an lvalue) 269 // lsc_status: the LE_STATUS variable (an lvalue) 270 // lsc_valist: the va_list variable (an lvalue) 271 // 272 // They are meant to be used in the same way as LSC_DATA_SET_BASE_COMMANDS() 273 // and LSC_DATA_SET_BASE_DISPATCHES(), shown above. All that's needed it 274 // to add calls to these macros in the right spots. 275 # define LSC_DATA_SET_ID_COMMANDS(lsc_name) \ 276 LSC_NR_get_##lsc_name##_identity 277 # define LSC_DATA_SET_ID_DISPATCHES(lsc_name, lsc_id, lsc_obj, \ 278 lsc_status, lsc_valist) \ 279 case LSC_NR_get_##lsc_name##_identity: \ 280 { \ 281 const char **lsc_result \ 282 = va_arg(lsc_valist, const char **); /* Result */ \ 283 \ 284 *lsc_result = lsc_id; \ 285 } \ 286 lsc_status = LE_STS_SUCCESS; \ 287 break 288 289 // ---------------------------------------------------------------- 290 291 // Extension to support associated application specific data. For this 292 // to work, each application must provide a unique index to identify its 293 // data. It's not specified here how to get a unique index, that has to 294 // be figured out somewhere else. 295 // 296 // This macro helps to support associated application data with the following 297 // generated functions: 298 // 299 // LSC_set_{lsc_name}_application_data(): 300 // Given an index and a void pointer, that pointer is stored away, and 301 // is identifiable with that index. 302 // LSC_get_{lsc_name}_application_data(): 303 // Given an index and a reference to a void pointer, this function retrieves 304 // the address of the data that's identifiable with that index, and assigns 305 // it to the given pointer. 306 // 307 // No assumptions are made about the application data that's stored away like 308 // this. The data set dispatch code isn't expect to do anything other than 309 // store away the pointer. 310 // 311 // For dispatch implementation, see the corresponding implementation macros, 312 // LSC_DATA_SET_APPDATA_COMMANDS() and LSC_DATA_SET_APPDATA_DISPATCHES(), 313 // below. 314 # define LSC_DATA_SET_APPDATA_FUNCTIONS(lsc_name) \ 315 enum { \ 316 LSC_NR_set_##lsc_name##_application_data = 4, \ 317 LSC_NR_get_##lsc_name##_application_data = 5, \ 318 }; \ 319 static inline LE_STATUS \ 320 LSC_NAME(set_##lsc_name##_application_data) \ 321 (LSC_NAME(lsc_name##_t) *lsc_data_set, int lsc_index, \ 322 void *lsc_data) \ 323 { \ 324 if (lsc_data_set == NULL) \ 325 return LE_STS_ERROR; \ 326 return lsc_data_set->lsc_dispatch(lsc_data_set, \ 327 LSC_NR_set_##lsc_name##_application_data, \ 328 lsc_index, lsc_data); \ 329 } \ 330 static inline LE_STATUS \ 331 LSC_NAME(get_##lsc_name##_application_data) \ 332 (LSC_NAME(lsc_name##_t) *lsc_data_set, int lsc_index, \ 333 void *lsc_data) \ 334 { \ 335 if (lsc_data_set == NULL) \ 336 return LE_STS_ERROR; \ 337 return lsc_data_set->lsc_dispatch(lsc_data_set, \ 338 LSC_NR_get_##lsc_name##_application_data, \ 339 lsc_index, lsc_data); \ 340 } 341 342 // For dispatch implementations of application data functions, the 343 // following two macros can be helpful: 344 // 345 // LSC_DATA_SET_APPDATA_COMMANDS() 346 // This offers the list of command numbers, to be used in an array. 347 // It takes no arguments. 348 // LSC_DATA_SET_APPDATA_DISPATCHES() 349 // This offers a standard implementation for the dispatch function. It 350 // takes the following arguments: 351 // 352 // lsc_slotindex: the variable to be used for the application data index. 353 // lsc_slotptr: the variable to be used as slot pointer 354 // lsc_slotptrinit: the expression that sets {lsc_slotptr}, given 355 // {lsc_slotindex}. It must always result in a LE_STATUS. 356 // lsc_status: the LE_STATUS variable (an lvalue) 357 // lsc_valist: the va_list variable (an lvalue) 358 // 359 // They are meant to be used in the same way as LSC_DATA_SET_BASE_COMMANDS() 360 // and LSC_DATA_SET_BASE_DISPATCHES(), shown above. All that's needed it 361 // to add calls to these macros in the right spots. 362 # define LSC_DATA_SET_APPDATA_COMMANDS(lsc_name) \ 363 LSC_NR_set_##lsc_name##_application_data, \ 364 LSC_NR_get_##lsc_name##_application_data 365 # define LSC_DATA_SET_APPDATA_DISPATCHES(lsc_name, \ 366 lsc_slotindex, lsc_slotptr, \ 367 lsc_slotptrinit, lsc_status, \ 368 lsc_valist) \ 369 case LSC_NR_set_##lsc_name##_application_data: \ 370 lsc_status = LE_STS_ERROR; \ 371 { \ 372 int lsc_slotindex = va_arg(lsc_valist, int); \ 373 void *lsc_data = va_arg(lsc_valist, void *); \ 374 if (lsc_data == NULL) \ 375 break; /* TODO: Set more precise status */ \ 376 \ 377 void **lsc_slotptr = NULL; \ 378 lsc_status = lsc_slotptrinit; \ 379 if (!LE_status_is_OK(lsc_status)) \ 380 break; \ 381 *lsc_slotptr = lsc_data; \ 382 } \ 383 lsc_status = LE_STS_SUCCESS; \ 384 break; \ 385 case LSC_NR_get_##lsc_name##_application_data: \ 386 lsc_status = LE_STS_ERROR; \ 387 { \ 388 int lsc_slotindex = va_arg(lsc_valist, int); \ 389 void **lsc_data = va_arg(lsc_valist, void **); /* Result */ \ 390 if (lsc_data == NULL) \ 391 break; /* TODO: Set more precise status */ \ 392 \ 393 void **lsc_slotptr = NULL; \ 394 lsc_status = lsc_slotptrinit; \ 395 if (!LE_status_is_OK(lsc_status)) \ 396 break; \ 397 *lsc_data = *lsc_slotptr; \ 398 } \ 399 lsc_status = LE_STS_SUCCESS; \ 400 break 401 402 # define LSC_IMPL_DATA_SET(lsc_name) \ 403 LSC_DATA_SET_TYPES(lsc_name); \ 404 LSC_DATA_SET_STRUCT(lsc_name) { \ 405 LSC_DATA_SET_STRUCT_FIELDS(lsc_name); \ 406 }; \ 407 LSC_DATA_SET_ALLOC_FUNCTIONS(lsc_name) \ 408 LSC_DATA_SET_BASE_FUNCTIONS(lsc_name) 409 410 #endif