/ include / lscore / data_set.h
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