/ include / lscore / object.h
object.h
  1  // SPDX-FileCopyrightText: 2023-2025 Le'Sec Core collective
  2  //
  3  // SPDX-License-Identifier: LGPL-3.0-or-later
  4  
  5  #ifndef LSC_OBJECT_H
  6  # define LSC_OBJECT_H
  7  
  8  # include <stdlib.h>
  9  # include <lscore/registration.h>
 10  # include <lscore/operation.h>
 11  
 12  # ifdef __cplusplus
 13  extern "C" {
 14  # endif
 15  
 16    // Object is an abstract concept, to be used for object classes.
 17    // To simplify implementing them, a base structure and template macros are
 18    // provided here.
 19    //
 20    // A typical template for the object class |obclass| would start like this:
 21    //
 22    // LSC_OBJECT_CLASS_TYPES(obclass);
 23    // LSC_OBJECT_CLASS_STRUCT(obclass) {
 24    //   LSC_OBJSCT__BASE_STRUCT_FIELDS(obclass);
 25    //   // Library fields added here
 26    // };
 27    //
 28    // Additionally, there's a choice of function generators:
 29    //
 30    // LSC_OBJECT_CLASS_ALLOC_FUNCTIONS(obclass, objectclass);
 31    // LSC_OBJECT_CLASS_BASE_FUNCTIONS(obclass, objectclass);
 32    // LSC_OBJECT_CLASS_REGISTRATION_FUNCTIONS(obclass, objectclass);
 33    //
 34    // |obclass| and |objectclass| have the same explanation as |ds| and
 35    // |dataset| in the introducing comment in <lscore/data_set.h>.
 36    //
 37    // For Object Type implementors, there are code snippets that can be inserted
 38    // into a switch(num) { } to implement the common bread and butter cases:
 39    //
 40    // LSC_OBJECT_TYPE_BASE_DISPATCHES(objectclass, idstr, obj, sts, ap, cmds);
 41  
 42    // The base struct components.
 43  # define LSC_OBJECT_CLASS_TYPES(lsc_object)                             \
 44    LSC_DATA_SET_TYPES(lsc_object)
 45  # define LSC_OBJECT_CLASS_STRUCT(lsc_object)                            \
 46    LSC_DATA_SET_STRUCT(lsc_object)
 47  # define LSC_OBJECT_CLASS_STRUCT_FIELDS(lsc_object)                     \
 48    LSC_DATA_SET_STRUCT_FIELDS(lsc_object)
 49  
 50    // The prefix used for types generated by this factory
 51  # define LSC_OBJECT_CLASS_PREFIX        "OBJECT::"
 52  
 53    // The allocator function template.
 54    //
 55    // The allocator function template takes two arguments:
 56    // lsc_object:        the object's class name.
 57    //
 58    // The generated functions are:
 59    //
 60    // LSC_new_{lsc_object}():
 61    //    Given a dispatch and a destroy function, this gives back a new
 62    //    object instance with the type LSC_{lsc_object}_t.
 63    //    This would usually only be called from the libraries
 64    // LSC_free_{lsc_object}():
 65    //    Frees an objec instance of type LSC_{lsc_object}_t
 66  # define LSC_OBJECT_CLASS_ALLOC_FUNCTIONS(lsc_object)                   \
 67    static inline LE_STATUS                                               \
 68    LSC_NAME(new_##lsc_object)                                            \
 69         (LSC_NAME(lsc_object##_dispatch_fn) *dispatch,                   \
 70          LSC_NAME(lsc_object##_destroy_fn) *destroy,                     \
 71          const void *dispatch_data,                                      \
 72          LSC_NAME(lsc_object##_t) **obj)                                 \
 73    {                                                                     \
 74      if ((*obj = malloc(sizeof(**obj))) == NULL)                         \
 75        return LE_STS_FATAL_ERROR;                                        \
 76      (*obj)->lsc_dispatch = dispatch;                                    \
 77      (*obj)->lsc_destroy = destroy;                                      \
 78      (*obj)->lsc_dispatch_data = dispatch_data;                          \
 79      (*obj)->lsc_data = NULL;                                            \
 80      return LE_STS_SUCCESS;                                              \
 81    }                                                                     \
 82    LSC_DATA_SET_FREE_FUNCTION(lsc_object)
 83  
 84    // Reserve a set of command numbers that are common object dispatch commands.
 85    // Note the double underscore after 'LSC_NR', which indicates that these
 86    // aren't to be used as normal command numbers.
 87  
 88    // LSC_NR__data_set_class_start .. LSC_NR__data_set_class_start + 31
 89    // is reserved for common object commands
 90  # define LSC_NR__object_start           (0 + LSC_NR__data_set_class_start)
 91    // LSC_NR__object_class_start indicates where command numbers for refined
 92    // object classes may start.
 93  # define LSC_NR__object_class_start     (32 + LSC_NR__data_set_class_start)
 94  
 95    // Standard commands that objects must support.  They are simply
 96    // borrowed from the data set factory.  See the commentary there
 97    // for the meaning of the arguments.
 98    //
 99    // We distinguish between the generated functions and the generated
100    // dispatch code.  The generated functions belong with the Object
101    // Class (i.e. the C type) that's generated, while the dispatch code
102    // belong with the Object Type (or subclass of the Object Class if
103    // you will).  This is reflected in the macro names (OBJECT_CLASS
104    // vs OBJECT_TYPE).
105    //
106    // For convenience (or vanity), there are also Object Class specific
107    // aliases for the data set command numbers.  Numerically, they are
108    // the exact same, and must remain this way.
109  # define LSC_OBJECT_CLASS_BASE_FUNCTIONS(lsc_object)                    \
110    LSC_DATA_SET_BASE_FUNCTIONS(lsc_object);                              \
111    LSC_DATA_SET_ID_FUNCTIONS(lsc_object)
112  # define LSC_OBJECT_TYPE_BASE_COMMANDS(lsc_object)                      \
113    LSC_DATA_SET_BASE_COMMANDS(lsc_object),                               \
114      LSC_DATA_SET_ID_COMMANDS(lsc_object)
115  # define LSC_OBJECT_TYPE_BASE_DISPATCHES(lsc_object, lsc_id,            \
116                                           lsc_obj, lsc_status,           \
117                                           lsc_valist, lsc_cmds)          \
118    LSC_DATA_SET_BASE_DISPATCHES(LSC_OBJECT_CLASS_PREFIX,                 \
119                                 lsc_object, lsc_obj, lsc_status,         \
120                                 lsc_valist, lsc_cmds);                   \
121    LSC_DATA_SET_ID_DISPATCHES(lsc_object, lsc_id, lsc_obj,               \
122                               lsc_status, lsc_valist);
123  
124    // The registration function factory
125    //
126    // The function template takes two arguments:
127    // lsc_object:        the object's class name.
128    //
129    // The longer explanation is found in <lscore/registration.h>
130  # define LSC_OBJECT_CLASS_REGISTRATION_FUNCTIONS(lsc_object)            \
131    LSC_REGISTRATION_FUNCTIONS(LSC_OBJECT_CLASS_PREFIX, lsc_object)
132  
133    // Template macro for quick implementation of a basic object
134  # define LSC_IMPL_BASIC_OBJECT_CLASS(lsc_object)                        \
135    LSC_OBJECT_CLASS_TYPES(lsc_object);                                   \
136    LSC_OBJECT_CLASS_STRUCT(lsc_object) {                                 \
137      LSC_OBJECT_CLASS_STRUCT_FIELDS(lsc_object);                         \
138    };                                                                    \
139    LSC_OBJECT_CLASS_BASE_FUNCTIONS(lsc_object);                          \
140    LSC_OBJECT_CLASS_ALLOC_FUNCTIONS(lsc_object)
141  
142    //////////////////////////////////////////////////////////////////////
143    //
144    // Data object classes are object classes with associated operators
145    // that have intimate knowledge of the object's internals.  To reflect
146    // that, these operations are not defined in <lscore/operation.h>, but
147    // here.
148    //
149    // The general nomenclature used here is:
150    //
151    //     operator       the type name (LSC_{operator}_t), also used in
152    //                    getters and setters
153    //     operation      the nominalized verb, usually used in performing
154    //                    function names
155    //
156    // Object subclasses may decide on their own how they use {operator},
157    // and {operation}.
158    //
159    // For each retrievable operator, there is an Object function named
160    // get_{object}_{operator}(), with arguments tailored for the way
161    // the operation works.  The first argument should be a pointer to
162    // the object to retrieve the operator from, and the last argument
163    // should be a reference to the pointer where the operator should be
164    // received.
165    // The operator pointer is expected to be owned by the object, so
166    // should not be freed by the caller.  These call are the only way to
167    // get hold of these sorts of operations.
168    //
169    //////////////////////////////////////////////////////////////////////
170  
171    // The generator is an operator that takes some input parameters,
172    // and generates a new object payload from them.  It's up to the
173    // implementation to determine what the possible parameters are
174    // and how they can be combined.
175  
176    // The generator operator types and struct.
177    // Note that all these macros are called with the Object class name.
178    // The operator gets the same name with '_generator' appended.
179  # define LSC_OPERATION_CLASS_GENERATOR_TYPES(lsc_object)                \
180    LSC_OPERATION_CLASS_TYPES(lsc_object##_generator)
181  # define LSC_OPERATION_CLASS_GENERATOR_STRUCT(lsc_object)               \
182    LSC_DATA_SET_STRUCT(lsc_object##_generator)
183  # define LSC_OPERATION_CLASS_GENERATOR_STRUCT_FIELDS(lsc_object)        \
184    LSC_DATA_SET_STRUCT_FIELDS(lsc_object##_generator)
185  
186    // Standard function command numbers and template for the generator
187    // Operation functions.
188    //
189    // Apart from the standard operation functions, the following function
190    // is generated:
191    //
192    // LSC_generate_{lsc_object}():
193    //    Generates a new value for the associated Object.  This is to be
194    //    done after having given this operation all the parameters it
195    //    expects, so works as a finalizer, and is therefore expected to
196    //    check that all its inputs are complete and will work together.
197  # define LSC_OPERATION_CLASS_GENERATOR_FUNCTIONS(lsc_object)            \
198    LSC_OPERATION_CLASS_BASE_FUNCTIONS(lsc_object##_generator);           \
199    LSC_OPERATION_CLASS_ALLOC_FUNCTIONS(lsc_object##_generator);          \
200    LSC_OPERATION_CLASS_PARAM_FUNCTIONS(lsc_object##_generator,           \
201                                        lsc_object##_generation);         \
202    enum {                                                                \
203      LSC_NR_generate_##lsc_object = (0 + LSC_NR__operation_class_start), \
204    };                                                                    \
205    static inline LE_STATUS                                               \
206    LSC_NAME(generate_##lsc_object)                                       \
207      (LSC_NAME(lsc_object##_generator_t) *op)                            \
208    {                                                                     \
209      if (op == NULL)                                                     \
210        return LE_STS_ERROR;                                              \
211      return op->lsc_dispatch(op, LSC_NR_generate_##lsc_object);          \
212    }
213  
214    // Standard dispatch cases that all generators should implement
215  # define LSC_OBJECT_GENERATOR_TYPE_COMMANDS(lsc_object)                 \
216    LSC_OPERATION_TYPE_BASE_COMMANDS(lsc_object##_generator),             \
217      LSC_OPERATION_TYPE_PARAM_COMMANDS(lsc_object##_generator),          \
218      LSC_NR_generate_##lsc_object
219  # define LSC_OBJECT_GENERATOR_TYPE_DISPATCHES(lsc_object, lsc_id,       \
220                                                   lsc_op, lsc_status,    \
221                                                   lsc_valist, lsc_cmds)  \
222    LSC_OPERATION_TYPE_BASE_DISPATCHES(lsc_object##_generator, lsc_id,    \
223                                       lsc_op, lsc_status,                \
224                                       lsc_valist, lsc_cmds)
225  
226    // Standard function command numbers and template for the Object
227    // functions.
228    //
229    // The generated function is:
230    //
231    // LSC_get_{lsc_object}_generator():
232    //    Gives back a pointer to the generator operation, given an Object
233    //    pointer and an input selection number.  The input selection is a
234    //    set of bits, where each bit describes to the generator operation
235    //    what sort of input it should expect, and possibly check the
236    //    validity of.
237    //
238    //    The input selection bits are not defined here, but will be by
239    //    code using these macros.
240  # define LSC_OBJECT_CLASS_GENERATOR_FUNCTIONS(lsc_object)               \
241    enum{                                                                 \
242      LSC_NR_get_##lsc_object##_generator = (0 + LSC_NR__object_start),   \
243    };                                                                    \
244    static inline LE_STATUS                                               \
245    LSC_NAME(get_##lsc_object##_generator)                                \
246      (LSC_NAME(lsc_object##_t) *obj,                                     \
247       LSC_NAME(lsc_object##_generator_t) **op)                           \
248    {                                                                     \
249      if (obj == NULL || op == NULL)                                      \
250        return LE_STS_ERROR;                                              \
251      return                                                              \
252        obj->lsc_dispatch(obj, LSC_NR_get_##lsc_object##_generator, op);  \
253    }
254  
255  # define LSC_OBJECT_TYPE_GENERATOR_COMMANDS(lsc_object)                 \
256    LSC_NR_get_##lsc_object##_generator
257  
258  # define LSC_IMPL_BASIC_OBJECT_CLASS_GENERATOR(lsc_object)              \
259    LSC_OPERATION_CLASS_GENERATOR_TYPES(lsc_object);                      \
260    LSC_OPERATION_CLASS_GENERATOR_STRUCT(lsc_object) {                    \
261      LSC_OPERATION_CLASS_GENERATOR_STRUCT_FIELDS(lsc_object);            \
262    };                                                                    \
263    LSC_OPERATION_CLASS_GENERATOR_FUNCTIONS(lsc_object);                  \
264    LSC_OBJECT_CLASS_GENERATOR_FUNCTIONS(lsc_object)
265  
266    //--------------------------------------------------------------------
267  
268    // The constructor is an operator that takes some input object material
269    // and constructs a new object payload from them.  This is very much like
270    // the generator, but with a semantic difference: the generator creates
271    // new object material based on parameters that work as hints, while the
272    // constructor uses object material given by the caller.  Sometimes, the
273    // difference can be a bit of a grey area, and it's the implementation's
274    // job to determine exactly what makes sense where.
275  
276    // The constructor operator types and struct.
277    // Note that all these macros are called with the Object class name.
278    // The operator gets the same name with '_constructor' appended.
279  # define LSC_OPERATION_CLASS_CONSTRUCTOR_TYPES(lsc_object)              \
280    LSC_OPERATION_CLASS_TYPES(lsc_object##_constructor)
281  # define LSC_OPERATION_CLASS_CONSTRUCTOR_STRUCT(lsc_object)             \
282    LSC_DATA_SET_STRUCT(lsc_object##_constructor)
283  # define LSC_OPERATION_CLASS_CONSTRUCTOR_STRUCT_FIELDS(lsc_object)      \
284    LSC_DATA_SET_STRUCT_FIELDS(lsc_object##_constructor)
285  
286    // Standard function command numbers and template for the constructor
287    // Operation functions.
288    //
289    // Apart from the standard operation functions, the following function
290    // is generated:
291    //
292    // LSC_construct_{lsc_object}():
293    //    Constructs a new payload for the associated Object.  This is to
294    //    be done after having given this operation all the parameters it
295    //    expects, so works as a finalizer, and is therefore expected to
296    //    check that all its inputs are complete and will work together.
297  # define LSC_OPERATION_CLASS_CONSTRUCTOR_FUNCTIONS(lsc_object)          \
298    LSC_OPERATION_CLASS_BASE_FUNCTIONS(lsc_object##_constructor);         \
299    LSC_OPERATION_CLASS_ALLOC_FUNCTIONS(lsc_object##_constructor);        \
300    LSC_OPERATION_CLASS_PARAM_FUNCTIONS(lsc_object##_constructor,         \
301                                        lsc_object##_construction);       \
302    enum{                                                                 \
303      LSC_NR_construct_##lsc_object = (0 + LSC_NR__operation_class_start), \
304    };                                                                    \
305    static inline LE_STATUS                                               \
306    LSC_NAME(construct_##lsc_object)                                      \
307      (LSC_NAME(lsc_object##_constructor_t) *op)                          \
308    {                                                                     \
309      if (op == NULL)                                                     \
310        return LE_STS_ERROR;                                              \
311      return op->lsc_dispatch(op, LSC_NR_construct_##lsc_object);         \
312    }
313  
314    // Standard dispatch cases that all constructors should implement
315  # define LSC_OBJECT_CONSTRUCTOR_TYPE_COMMANDS(lsc_object)               \
316    LSC_OPERATION_TYPE_BASE_COMMANDS(lsc_object##_constructor),           \
317      LSC_OPERATION_TYPE_PARAM_COMMANDS(lsc_object##_constructor),        \
318      LSC_NR_construct_##lsc_object
319  # define LSC_OBJECT_CONSTRUCTOR_TYPE_DISPATCHES(lsc_object, lsc_id,     \
320                                                  lsc_op, lsc_status,     \
321                                                  lsc_valist, lsc_cmds)   \
322    LSC_OPERATION_TYPE_BASE_DISPATCHES(lsc_object##_constructor,          \
323                                       lsc_id, lsc_op, lsc_status,        \
324                                       lsc_valist, lsc_cmds)
325  
326    // Standard function command numbers and template for the Object
327    // functions.
328    //
329    // The generated function is:
330    //
331    // LSC_get_{lsc_object}_constructor():
332    //    Gives back a pointer to the constructor operation, given an Object
333    //    pointer and an input selection number.  The input selection is a
334    //    set of bits, where each bit describes to the constructor operation
335    //    what sort of input it should expect, and possibly check the
336    //    validity of.
337    //
338    //    The input selection bits are not defined here, but will be by
339    //    code using these macros.
340    // LSC_get_{lsc_object}_constructor_selection_descs():
341    //    Gives back a pointer to an array of parameter descriptors, as well
342    //    as the number of elements - which is also the amount of possible
343    //    selection bits.
344  # define LSC_OBJECT_CLASS_CONSTRUCTOR_FUNCTIONS(lsc_object)             \
345    enum {                                                                \
346      LSC_NR_get_##lsc_object##_constructor = (1 + LSC_NR__object_start), \
347    };                                                                    \
348    static inline LE_STATUS                                               \
349    LSC_NAME(get_##lsc_object##_constructor)                              \
350      (LSC_NAME(lsc_object##_t) *obj,                                     \
351       LSC_NAME(lsc_object##_constructor_t) **op)                         \
352    {                                                                     \
353      if (obj == NULL || op == NULL)                                      \
354        return LE_STS_ERROR;                                              \
355      return obj->lsc_dispatch(obj,                                       \
356                               LSC_NR_get_##lsc_object##_constructor,     \
357                               op);                                       \
358    }
359  
360  # define LSC_OBJECT_TYPE_CONSTRUCTOR_COMMANDS(lsc_object)               \
361    LSC_NR_get_##lsc_object##_constructor
362  
363  # define LSC_IMPL_BASIC_OBJECT_CLASS_CONSTRUCTOR(lsc_object)            \
364    LSC_OPERATION_CLASS_CONSTRUCTOR_TYPES(lsc_object);                    \
365    LSC_OPERATION_CLASS_CONSTRUCTOR_STRUCT(lsc_object) {                  \
366      LSC_OPERATION_CLASS_CONSTRUCTOR_STRUCT_FIELDS(lsc_object);          \
367    };                                                                    \
368    LSC_OBJECT_CLASS_CONSTRUCTOR_FUNCTIONS(lsc_object);                   \
369    LSC_OPERATION_CLASS_CONSTRUCTOR_FUNCTIONS(lsc_object)
370  
371    //--------------------------------------------------------------------
372  
373    // The extractor is an operation that can be used to retrieve payload
374    // details in form of parameters.
375  
376    // The extractor operation types and struct.
377    // Note that all these macros are called with the Object class name.
378    // The operation gets the same name with '_extractor' appended.
379  # define LSC_OPERATION_CLASS_EXTRACTOR_TYPES(lsc_object)                  \
380    LSC_OPERATION_CLASS_TYPES(lsc_object##_extractor)
381  # define LSC_OPERATION_CLASS_EXTRACTOR_STRUCT(lsc_object)                 \
382    LSC_DATA_SET_STRUCT(lsc_object##_extractor)
383  # define LSC_OPERATION_CLASS_EXTRACTOR_STRUCT_FIELDS(lsc_object)          \
384    LSC_DATA_SET_STRUCT_FIELDS(lsc_object##_extractor)
385  
386    // Standard function and template for the extractor Operation functions.
387    // This is very much defined like a standard operation, except it does
388    // not have a way to set parameters.
389  # define LSC_OPERATION_CLASS_EXTRACTOR_FUNCTIONS(lsc_object)            \
390    LSC_OPERATION_CLASS_BASE_FUNCTIONS(lsc_object##_extractor);           \
391    LSC_OPERATION_CLASS_ALLOC_FUNCTIONS(lsc_object##_extractor);          \
392    LSC_OPERATION_CLASS_GET_PARAM_FUNCTIONS(lsc_object##_extractor,       \
393                                            lsc_object##_extraction)
394  
395    // Standard dispatch cases that all extractors should implement
396  # define LSC_OBJECT_EXTRACTOR_TYPE_COMMANDS(lsc_object)                 \
397    LSC_OPERATION_TYPE_BASE_COMMANDS(lsc_object##_extractor),             \
398    LSC_OPERATION_TYPE_PARAM_COMMANDS(lsc_object##_extractor)
399  # define LSC_OBJECT_EXTRACTOR_TYPE_DISPATCHES(lsc_object, lsc_id,       \
400                                                lsc_op, lsc_status,       \
401                                                lsc_valist, lsc_cmds)     \
402    LSC_OPERATION_TYPE_BASE_DISPATCHES(lsc_object##_extractor, lsc_id,    \
403                                       lsc_op, lsc_status,                \
404                                       lsc_valist, lsc_cmds)
405  
406    // Standard function command numbers and template for the Object
407    // functions.
408    //
409    // The generated function is:
410    //
411    // LSC_get_{lsc_object}_extractor():
412    //    Gives back a pointer to the extractor operation, given an Object
413    //    pointer.
414  # define LSC_OBJECT_CLASS_EXTRACTOR_FUNCTIONS(lsc_object)               \
415    enum {                                                                \
416      LSC_NR_get_##lsc_object##_extractor = (2 + LSC_NR__object_start),   \
417    };                                                                    \
418    static inline LE_STATUS                                               \
419    LSC_NAME(get_##lsc_object##_extractor)                                \
420      (LSC_NAME(lsc_object##_t) *obj,                                     \
421       LSC_NAME(lsc_object##_extractor_t) **op)                           \
422    {                                                                     \
423      if (obj == NULL || op == NULL)                                      \
424        return LE_STS_ERROR;                                              \
425      return obj->lsc_dispatch(obj, LSC_NR_get_##lsc_object##_extractor,  \
426                               op);                                       \
427    }
428  
429  # define LSC_OBJECT_TYPE_EXTRACTOR_COMMANDS(lsc_object)                 \
430    LSC_NR_get_##lsc_object##_extractor
431  
432  # define LSC_IMPL_BASIC_OBJECT_CLASS_EXTRACTOR(lsc_object)              \
433    LSC_OPERATION_CLASS_EXTRACTOR_TYPES(lsc_object);                      \
434    LSC_OPERATION_CLASS_EXTRACTOR_STRUCT(lsc_object) {                    \
435      LSC_OPERATION_CLASS_EXTRACTOR_STRUCT_FIELDS(lsc_object);            \
436    };                                                                    \
437    LSC_OBJECT_CLASS_EXTRACTOR_FUNCTIONS(lsc_object);                     \
438    LSC_OPERATION_CLASS_EXTRACTOR_FUNCTIONS(lsc_object)
439  
440    //--------------------------------------------------------------------
441  
442  # ifdef __cplusplus
443  }
444  # endif
445  
446  #endif