IIINSTRUMENT Pipeline Reference Manual 4.4.3
sbrm.h
1/*
2 * This file is part of the ESO Pipelines
3 * Copyright (C) 2016 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Created on: Jul 17, 2020
20 * Author: lluvaul
21 */
22
23#ifndef SBRM_H_
24#define SBRM_H_
25
26/*----------------------------------------------------------------------------*
27 * This module implements, in C, a restricted form of the RAII (Resource Acqui-
28 * sition Is Initialisation) concept from C++, although a better name for it is
29 * SBRM (Scope Bound Resource Management). Unlike true RAII/SBRM, C can't tie
30 * dynamically-allocated object lifetimes to arbitrary scopes/levels, so instead
31 * this module limits itself to just the outermost scope enclosed by a function.
32 * Despite this limitation, it is a more readable alternative than the more gen-
33 * erally recommended technique for combining resource management with error
34 * handling in C, which is to use a "goto chain" (refer to MEM12-C from Carnegie
35 * Mellon's SEI CERT C Coding Standard)
36 *
37 * There are two APIs offered, a pure C API and a Macro API. Both APIs provide
38 * the same functionality but the Macro API is both less verbose and safer to
39 * use than the C API. The Macro API is a type-safe wrapper around the C API.
40 * The type-safety of the Macro API is expressed through "incompatible pointer
41 * types" compiler warnings, hereby referred to as IPT warnings. It is there-
42 * fore recommended to compile with -Werror=incompatible-pointer-types (or your
43 * compiler's equivalent) to turn this warning into an error because, if you get
44 * one, you have an unsafe operation somewhere. When using the Macro API,
45 * strive for zero compiler warnings/errors. C API examples below marked with
46 * ~> are not type-safe.
47 *
48 * [This implementation was only designed to manage pointers to resources, not
49 * non-pointer resources (the SBRM_SET macros, for example, will only work with
50 * resource pointers). You may try to use the C API directly to force SBRM to
51 * manage non-pointer resources but, although it may work on your chosen compil-
52 * er/OS combo, it may not be portable... caveat emptor.]
53 *----------------------------------------------------------------------------*/
54
55/*-----------------------------------------------------------------------------
56 Includes
57 -----------------------------------------------------------------------------*/
58
59#include <cpl.h>
60#include <string.h> // for memcpy (on a Mac, memcpy is a macro so enclose any
61 // args that may use __VA_ARGS__ in parens to avoid a 'Too
62 // many arguments' error, which can happen when __VA_ARGS__
63 // contains commas)
64
65CPL_BEGIN_DECLS
66
67/*-----------------------------------------------------------------------------
68 Private Defines
69 -----------------------------------------------------------------------------*/
70
71/*----------------------------------------------------------------------------*
72 * private helper macros (not to be used outside this file)
73 *----------------------------------------------------------------------------*/
74#define SBRM_PRIV_HERE_SIG const char * func, const char * file, const int line
75#define SBRM_PRIV_ATOM(statements) do { statements; } while (0)
76#define SBRM_PRIV_NARGS___(_5, _4, _3, _2, _1, N, ...) N
77#define SBRM_PRIV_NARGS__(...) SBRM_PRIV_NARGS___(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
78#define SBRM_PRIV_NARGS_0(...) SBRM_PRIV_NARGS__(__VA_ARGS__)
79#define SBRM_PRIV_NARGS_1(...) 0
80#define SBRM_PRIV_NARGS(...) \
81 SBRM_PRIV_CAT(SBRM_PRIV_NARGS_, SBRM_PRIV_ISEMPTY(__VA_ARGS__))(__VA_ARGS__)
82#define SBRM_PRIV_CAT_(x, y) x ## y
83#define SBRM_PRIV_CAT(x, y) SBRM_PRIV_CAT_(x, y)
84#define SBRM_PRIV_CASSERT(cond, msg) /* compile-time assertion */ \
85 typedef char STATIC_ASSERT__##msg[2 * !!(cond) - 1]
86#define SBRM_PRIV_DECONSTIFY(name) ((__sbrm_deconst_t*)&name)->mut
87// credit for the following goes to Jens Gustedt (see:
88// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/)
89#define SBRM_PRIV_ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
90 _13, _14, _15, ...) _15
91#define SBRM_PRIV_HAS_COMMA(...) SBRM_PRIV_ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1,\
92 1, 1, 1, 1, 1, 1, 1, 1, 0)
93#define SBRM_PRIV_TRIGGER_PAREN(...) ,
94#define SBRM_PRIV_EMPTY_CASE_0001 ,
95#define SBRM_PRIV_PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
96#define SBRM_PRIV_ISEMPTY_(_0, _1, _2, _3) SBRM_PRIV_HAS_COMMA(SBRM_PRIV_PASTE5\
97 (SBRM_PRIV_EMPTY_CASE_, _0, _1, _2, _3))
98#define SBRM_PRIV_ISEMPTY(...) SBRM_PRIV_ISEMPTY_( \
99 SBRM_PRIV_HAS_COMMA(__VA_ARGS__), \
100 SBRM_PRIV_HAS_COMMA(SBRM_PRIV_TRIGGER_PAREN __VA_ARGS__), \
101 SBRM_PRIV_HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
102 SBRM_PRIV_HAS_COMMA(SBRM_PRIV_TRIGGER_PAREN __VA_ARGS__ (/*empty*/)))
103
104/*-----------------------------------------------------------------------------
105 Public Defines
106 -----------------------------------------------------------------------------*/
107
108/*----------------------------------------------------------------------------*
109 * REGISTRY_VARNAME: defines a custom registry name; useful when using the C API
110 * in conjunction with the _ macro (if left undefined, and you wish to use _,
111 * you must name your registry __sbrm_registry).
112 *----------------------------------------------------------------------------*/
113#if defined(SBRM_REGISTRY_VARNAME)
114# define SBRM_PRIV_REGISTRY SBRM_REGISTRY_VARNAME
115#else
116# define SBRM_PRIV_REGISTRY __sbrm_registry
117#endif
118
119/*----------------------------------------------------------------------------*
120 * HERE: convenience macro expanding to usual trio of location params
121 *----------------------------------------------------------------------------*/
122#define SBRM_HERE cpl_func, __FILE__, __LINE__
123
124/*----------------------------------------------------------------------------*
125 * HANDLE_{POINTR,STRUCT}: these can help make the C API easier to read; use
126 * them as you would typedefs. Variables of these types represent typed handles
127 * to the managed objects and should be initialised with the return value from
128 * the C API routine ->set(). HANDLE_STRUCT expands to a type that is a ptr to
129 * a registry slot managing an object of type @type (the slot's first member .o
130 * points to this object and thus has type @type*). HANDLE_POINTR expands to a
131 * type that is a ptr to the first member of a slot, which has type @type*. The
132 * two are the same because the C standard guarantees that the address of a
133 * struct is identical to the address of its first member. It only makes a
134 * difference in how the user accesses the managed object: 'name->o' using
135 * HANDLE_STRUCT, and '*name' using HANDLE_POINTR. The macro API exclusively
136 * uses HANDLE_STRUCT as the ->o makes it clear that the user is dealing with a
137 * managed object.
138 *
139 * Hybrid* API (*so-called because use of these macros means it's not pure C):
140 * #define HANDLE SBRM_HANDLE_STRUCT // shortened for brevity
141 * HANDLE(type) * name = reg->set(reg, fp, dtor, 0, 0); name->o = initval;
142 * ~> HANDLE(type) * name = reg->set(reg, fp, dtor, initval, 0);
143 * ~> HANDLE(type) * const cname = reg->set(reg, fp, dtor, initval, 0);
144 * cname = name; // won't compile (assignment of read-only variable)
145 *----------------------------------------------------------------------------*/
146#define SBRM_HANDLE_POINTR(type) type *
147#define SBRM_HANDLE_STRUCT(type) struct { type * o; }
148
149/*----------------------------------------------------------------------------*
150 * INITV/RESET: convenience macros providing aliases for overloaded method modes
151 * (see INIT & SET, respectively for more details)
152 *----------------------------------------------------------------------------*/
153#define SBRM_INITV(size, name, ...) SBRM_INIT(size, name, ##__VA_ARGS__)
154#define SBRM_RESET(name) SBRM_SET(name)
155
156/*----------------------------------------------------------------------------*
157 * INIT: sets up the object registry and optionally designates a return object.
158 * Call it near the beginning of your function, in the outermost scope. Ensure
159 * @size is bigger than the combined number of calls to [C]SET that follow (0 is
160 * fine if there are no such calls: initialising a zero-sized registry is often
161 * done just to gain access to the _ macro (described below)).
162 *
163 * Any arguments supplied after @size are passed verbatim to SET* to declare/in-
164 * itialise a managed object which is then designated the return object by in-
165 * voking RVAL on it. Usage of this object within the function is exactly the
166 * same as for any other object defined via SET except that _ returns (a pointer
167 * to) it and ABORT/CLEANUP both evaluate to the same... that is unless RVAL
168 * nominates a different object before ABORT, CLEANUP, or _ are encountered.
169 * (See below for detailed descriptions of SET, RVAL, ABORT, CLEANUP, and _.) If
170 * your function's return type doesn't match the designated object type, the
171 * Macro API will generate an IPT warning but the C API will *not* warn you, so
172 * take extra care to ensure that they match.
173 *
174 * If INIT is _not_ used to define a return object, _ will instead return NULL
175 * and ABORT/CLEANUP will evaluate to the same (again assuming RVAL is not later
176 * used to nominate some other object) in which case it is recommended that your
177 * function's return type be void*.
178 *
179 * Note that, when using SBRM, if you exit the function without returning ABORT
180 * or CLEANUP or invoking _, you risk a memory leak as only these routines are
181 * gauranteed to invoke registry deallocation.
182 *
183 * (*INIT will generate a non-IPT warning [-Wunused-value] when @defval is miss-
184 * ing from the INITV form [described below]. Refer to SET to understand why.)
185 *
186 * Macro API:
187 * #define INIT SBRM_INIT // shortened for brevity
188 * #define INITV SBRM_INITV // shortened for brevity
189 * INIT(size); // non-INITV mode
190 * INIT(size, name, type [,fp [,dtor]]) = defval; // INITV mode
191 * INITV(size, name, type [,fp [,dtor]]) = defval; // same as above
192 * INITV(size); // won't compile: INIT aliases INITV, not vice versa
193 *
194 * C API:
195 * #define HERE cpl_func, __FILE__, __LINE__ // or use SBRM_HERE
196 * sbrm_registry * reg = sbrm_init(size, HERE); // mandatory
197 * // the following optional lines define an alternate return value/object
198 * // other than the default (NULL)
199 * ~> struct { type * o; } * name = reg->set(reg, fp, dtor, defval, 0);
200 * reg->rval(reg, name);
201 *
202 *
203 * Methods A & B below show two different approaches to working with SBRM-
204 * enhanced functions. Assume for each that the SBRM-enhanced function looks
205 * like the following:
206 *
207 * int * sbrm_enhanced_func(...) {
208 * SBRM_INIT(5, rv, int, v, cpl_free) = ALLOC(int, -1);
209 * if (cond) SBRM_RESET(rv) = ALLOC(int, 42);
210 * return SBRM_CLEANUP(); // pointer to -1 returned if cond is false
211 * }
212 *
213 * A) if the API demands a non-pointer return value, write a short wrapper
214 * function calling the SBRM-enhanced function to catch & deref the returned
215 * object, free the space holding it, and return its value (the LOCAL macro
216 * does all this for you):
217 *
218 * int func_wrapper(...) {
219 * return *SBRM_LOCAL(int, sbrm_enhanced_func(...));
220 * }
221 * whatever_t caller(...) {
222 * const int val = func_wrapper(...); // val is -1 or 42
223 * return my_whatever;
224 * }
225 *
226 * B) or you could enhance the calling routine with SBRM and use it to auto-
227 * free the allocated return value itself (which you do by declaring a man-
228 * aged ptr to the returned object which you then use/reuse throughout the
229 * caller):
230 *
231 * whatever * sbrm_enhanced_caller(...) {
232 * SBRM_INIT(5, rv, whatever) = NULL; // auto-uses dtor whatever_delete
233 * SBRM_SET(val, int, v, cpl_free) = sbrm_enhanced_func(...);
234 * // access via val->o, which now points to -1 or 42
235 * const int tmp = *val->o + 1; // tmp is 0 or 43
236 * return SBRM_CLEANUP(); // val->o auto-freed & rv->o returned
237 * }
238 *----------------------------------------------------------------------------*/
239#define SBRM_PRIV_INIT4(size, name, type, fp, dtor) SBRM_PRIV_INIT0(size + 1); \
240 SBRM_SET(name, type, fp, dtor) = NULL; SBRM_RVAL(name); name->o
241#define SBRM_PRIV_INIT3(size, name, type, fp, ...) \
242 SBRM_PRIV_INIT4(size, name, type, fp, SBRM_PRIV_CAT(type, _delete))
243#define SBRM_PRIV_INIT2(size, name, type, ...) \
244 SBRM_PRIV_INIT4(size, name, type, v, SBRM_PRIV_CAT(type, _delete))
245#define SBRM_PRIV_INIT1(size, name, ...) \
246 SBRM_PRIV_CASSERT(0, Wrong_number_of_arguments_given_to_SBRM_INIT)
247#define SBRM_PRIV_INIT0(size, ...) \
248 sbrm_registry * SBRM_PRIV_REGISTRY = sbrm_init(size, SBRM_HERE)
249#define SBRM_INIT(size, ...) SBRM_PRIV_CAT(SBRM_PRIV_INIT, \
250 SBRM_PRIV_NARGS(__VA_ARGS__)) (size, ##__VA_ARGS__)
251
252/*----------------------------------------------------------------------------*
253 * [C]SET, "set" mode (2 or more args): passes (a ptr to) an object (@initval)
254 * and its destructor (@fp+@dtor) to the management infrastructure, to be stored
255 * in the slot @ident, whose content (if any) is first freed. If slot @ident is
256 * 0 or not found, @initval, @fp, @dtor, and @ident are stored in the next unoc-
257 * cupied slot, now known as @ident (unless @ident is 0 in which case the slot
258 * is unidentifiable). Once this is all done a handle, @name, will be declared
259 * locally and initialised with a ptr to the slot, whose .o member is of type
260 * @type* and points to @initval (if @initval is not of type @type, the C API
261 * will *not* warn you, whereas the Macro API will generate an IPT warning).
262 * @name is the typed handle to the registry-managed object, accessed thusly:
263 * @name->o
264 *
265 * The object is now managed by SBRM and will be destroyed by calling @dtor on
266 * it whenever _, ABORT, or CLEANUP are invoked: _ calls ABORT which in turn
267 * calls CLEANUP, but of course you may invoke ABORT or CLEANUP directly. The
268 * CSET variant will declare the managed object as const. All instances of
269 * [C]SET within a function must be preceded with a call to INIT.
270 *
271 * Unless you provide them, the Macro API will automatically try to set @fp to
272 * 'v', @dtor to <@type>_delete (or to <@type>_unwrap if you set @fp to 'p'),
273 * and @ident to __LINE__. A missing @initval is a bit more complicated: the C
274 * API takes @initval as an argument, but the Macro API expects it to be given
275 * as a trailing assignment. If this assignment is missing, the Macro API gen-
276 * erates a non-IPT warning (-Wunused-value) yet still proceeds to use NULL in
277 * place of the missing @initval. You may silence the warning with a trailing,
278 * though redundant, assignment to NULL.
279 *
280 * The @fp argument, a single char, describes @dtor's signature thus informing
281 * SBRM how it should be invoked. 'v' means @dtor returns nothing, 'i' means it
282 * returns an int, 'f' a float, 'd' a double, and 'p' a pointer to some object.
283 * These also indicate that @dtor takes a pointer to the managed object as its
284 * argument. For all but 'v', SBRM discards the return value by prefixing the
285 * @dtor call with (void). Yet another value, 'w', indicates that @dtor takes
286 * a pointer^ to the object pointer and returns nothing (^this is usually done
287 * to set the pointer to NULL after releasing the memory... something SBRM al-
288 * ready does for *all* object pointers). The 'w' value allows SBRM to support
289 * this style of destructor even though it means the object pointer is set to
290 * NULL twice, once by @dtor and again by SBRM immediately following the @dtor
291 * call.
292 *
293 * The @ident argument is handy for declaring handles within loops: give a uni-
294 * que number (or __LINE__ as the Macro API does) and, in the first pass thru
295 * the loop, an empty slot gets filled (with @initval) and tagged with @ident;
296 * then, in subsequent passes, @ident is used to lookup & assign the same slot
297 * back to the handle, after first freeing the previous pass's @initval. The
298 * slot (through the handle) is then used to store the current pass's @initval.
299 *
300 * [C]SET, "reset" mode (1 arg): calls FREE on @name (frees & NULL's @name->o)
301 * then sets it to @newval (if provided) thus changing the object (but _not_ the
302 * type) managed by the slot that @name points to. If @newval isn't given, both
303 * APIs will generate a non-IPT warning (-Wunused-value). Additionally, the Ma-
304 * cro API will generate an IPT warning if @newval is not of type @type*, but
305 * the C API will *not* warn you. The C API offers two variants, ->reset1() and
306 * ->reset2(), depending on the style of access you prefer.
307 *
308 * [A word of caution about using _ with @newval in "reset" mode: keep in mind
309 * that, because the left hand side of the assignment always runs first,
310 * @name->o is always freed regardless of what happens on the right-hand side.
311 * If _ detects a CPL error on the right, whatever value results from the failed
312 * routine (usually NULL) is very briefly assigned to @name->o before being re-
313 * turned. This is important to understand when @name is the designated return
314 * value (see RVAL): in this case, it is tempting to think that whatever is in
315 * @name->o _before_ the right-side executes will be returned by _ if there is
316 * an error (after all, this is what happens in all other cases); however, it is
317 * _not_ true. (I thought of issuing an informative warning when SET is used on
318 * the designated RVAL, but it would be unwarranted unless the _ macro is also
319 * in use, as it's only then that the situation is deceptive.)]
320 *
321 *
322 * Macro API ("set" mode: 2+ args):
323 * #define SET SBRM_SET // shortened for brevity
324 * //SET(name, type [,fp [,dtor]]); // sets to NULL, but warns(non-IPT)
325 * SET(name, type [,fp [,dtor]]) = NULL; // same as above, no warning
326 * SET(name, type [,fp [,dtor]]) = initval; // warns(IPT) iff initval wrong type
327 * SET(name, type [,fp [,dtor]]) = func _(...); // _ in initval is ok
328 * SBRM_CSET(cname, type [,fp [,dtor]]) = initval;
329 *
330 * // a handy trick is to define your type's dtor in the .h file like this:
331 * #define foolist_delete cpl_free // then the dtor is...
332 * SET(listA, foolist) = VALLOC(foolist, foo, 17); // ...inferred, always
333 * SET(myfooz, foolist) = VALLOC(foolist, foo, 4); // ...and everywhere
334 *
335 * 1st C API (clearly indicates managed object, but visually ugly):
336 * struct { type * o; } * name = reg->set(reg, fp, dtor, 0, 0); name->o = initval;
337 * ~> struct { type * o; } * name = reg->set(reg, fp, dtor, initval, 0);
338 * struct { type * o; } * name = reg->set(reg, fp, dtor, 0, ident);
339 *
340 * 2nd C API (terse, but error prone):
341 * type ** name = reg->set(reg, fp, dtor, 0, 0); *name = initval;
342 * ~> type ** name = reg->set(reg, fp, dtor, initval, 0);
343 * type ** name = reg->set(reg, fp, dtor, 0, ident);
344 *
345 * Hybrid API (same advantage as 1st C API, but minor macro hides ugliness):
346 * #define HANDLE SBRM_HANDLE_STRUCT // shortened for brevity
347 * HANDLE(type) * name = reg->set(reg, fp, dtor, 0, 0); name->o = initval;
348 * ~> HANDLE(type) * name = reg->set(reg, fp, dtor, initval, 0);
349 * HANDLE(type) * name = reg->set(reg, fp, dtor, 0, ident);
350 *
351 *
352 * Macro API ("reset" mode: 1 arg):
353 * #define SET SBRM_SET // shortened for brevity
354 * #define RESET SBRM_RESET // shortened for brevity
355 *
356 * SET(name) = newval; // RESET mode
357 * RESET(name) = newval; // same as above
358 * RESET(name, type [,fp [,dtor]]); // won't compile: SET aliases RESET,
359 * // not vice versa
360 *
361 * //RESET(name); // frees & NULLifies, but warns(non-IPT)
362 * RESET(name) = NULL; // same as above, no warning; or use FREE(name)
363 * RESET(name) = newval; // warns(IPT) iff newval wrong type
364 *
365 * RESET(name) = func _(...); // _ in newval is ok
366 * if (...) RESET(name) = func(...); // no {} req'd in conds/loops...
367 * if (...) { RESET(name) = func _(...); } // ...unless _ is present
368 *
369 * cname = name; // won't compile (CSET defines const handles)
370 * name = cname; // won't compile (SET also defines const handles)
371 * cname->o->member = ...; // won't compile (*o is const)
372 * cname->o = newval; // ok (o is not const), but may leak mem
373 * RESET(cname) = newval; // better, no mem leak
374 *
375 * 1st C API:
376 * ~> reg->reset1(reg, name)->o = newval;
377 * reg->free(reg, name); name->o = newval;
378 *
379 * 2nd C API:
380 * ~> *reg->reset2(reg, name) = newval;
381 * reg->free(reg, name); *name = newval;
382 *----------------------------------------------------------------------------*/
383#define SBRM_PRIV_SETv(type) SBRM_PRIV_CAT(type, _delete)
384#define SBRM_PRIV_SETi(type) SBRM_PRIV_CAT(type, _delete)
385#define SBRM_PRIV_SETf(type) SBRM_PRIV_CAT(type, _delete)
386#define SBRM_PRIV_SETd(type) SBRM_PRIV_CAT(type, _delete)
387#define SBRM_PRIV_SETp(type) SBRM_PRIV_CAT(type, _unwrap)
388#define SBRM_PRIV_SETw(type) SBRM_PRIV_CAT(type, _delete)
389
390#define SBRM_PRIV_SET3(name, type, fp, dtor) \
391 SBRM_HANDLE_STRUCT(type) * const name = SBRM_PRIV_REGISTRY->set( \
392 SBRM_PRIV_REGISTRY, #fp[0], dtor, NULL, __LINE__); name->o
393#define SBRM_PRIV_SET2(name, type, fp, ...) \
394 SBRM_PRIV_SET3(name, type, fp, SBRM_PRIV_CAT(SBRM_PRIV_SET, fp)(type))
395#define SBRM_PRIV_SET1(name, type, ...) \
396 SBRM_PRIV_SET3(name, type, v, SBRM_PRIV_CAT(SBRM_PRIV_SET, v)(type))
397#define SBRM_PRIV_SET0(name, ...) *(SBRM_FREE(name), &name->o)
398#define SBRM_SET(name, ...) SBRM_PRIV_CAT(SBRM_PRIV_SET, \
399 SBRM_PRIV_NARGS(__VA_ARGS__)) (name, ##__VA_ARGS__)
400
401#define SBRM_PRIV_CSET3(name, type, fp, dtor) \
402 SBRM_HANDLE_STRUCT(const type) * const name = SBRM_PRIV_REGISTRY->set( \
403 SBRM_PRIV_REGISTRY, #fp[0], dtor, NULL, __LINE__); name->o
404#define SBRM_PRIV_CSET2(name, type, fp, ...) \
405 SBRM_PRIV_CSET3(name, type, fp, SBRM_PRIV_CAT(SBRM_PRIV_SET, fp)(type))
406#define SBRM_PRIV_CSET1(name, type, ...) \
407 SBRM_PRIV_CSET3(name, type, v, SBRM_PRIV_CAT(SBRM_PRIV_SET, v)(type))
408#define SBRM_PRIV_CSET0(name, ...) *(SBRM_FREE(name), &name->o)
409#define SBRM_CSET(name, ...) SBRM_PRIV_CAT(SBRM_PRIV_CSET, \
410 SBRM_PRIV_NARGS(__VA_ARGS__)) (name, ##__VA_ARGS__)
411
412/*----------------------------------------------------------------------------*
413 * RVAL: redefines which object is to be the new return value. This must be one
414 * of those managed by SBRM, indicated by @name, previously declared via INIT or
415 * [C]SET (passing anything else will result in a runtime assertion during the
416 * registry deallocation routine). If the type of @name->o is not the same as
417 * the function's declared return type, the Macro API will cause an IPT warning
418 * to be generated but the C API will not generate any warning at all, so take
419 * extra care to pass a handle (pointing to a registry slot that manages an obj-
420 * ect) of the same type as the function's return type.
421 *
422 * Macro API:
423 * SBRM_RVAL(name)
424 * C API:
425 * ~> reg->rval(reg, name);
426 *----------------------------------------------------------------------------*/
427#define SBRM_RVAL(name) SBRM_PRIV_ATOM( \
428 SBRM_PRIV_REGISTRY->rval(SBRM_PRIV_REGISTRY, name); if (0) return name->o; )
429
430/*----------------------------------------------------------------------------*
431 * FREE: destroys the object tracked by the registry slot pointed to by the
432 * handle @name by invoking the registered destructor on @name->o (unless it is
433 * NULL), then sets @name->o to NULL.
434 *
435 * Macro API:
436 * SBRM_FREE(name);
437 * assert(name->o == NULL); // ptr to object is auto-NULLed
438 * C API:
439 * reg->free(reg, name);
440 * assert(name->o == NULL); // ptr to object is auto-NULLed
441 *----------------------------------------------------------------------------*/
442#define SBRM_FREE(name) SBRM_PRIV_REGISTRY->free(SBRM_PRIV_REGISTRY, name)
443
444/*----------------------------------------------------------------------------*
445 * CLEANUP: destroys all* objects in the registry in the reverse order that they
446 * were registered by invoking their registered destructors on them (unless they
447 * or their destructors are NULL), and frees all memory used by the registry it-
448 * self. It returns a pointer to an object that is meant to be returned by the
449 * function. The returned object should be freed by the function's caller.
450 *
451 * *By default, CLEANUP returns NULL, but there are two ways it will preserve an
452 * object from the registry and return that instead:
453 * 1) if a registry-managed object has been designated as the function's
454 * return value via RVAL or INIT (which internally calls RVAL), then
455 * CLEANUP will return it instead; or
456 * 2) if a handle @name is passed to CLEANUP, the object (managed by the
457 * registry slot) it points to will instead be returned while any prev-
458 * iously RVAL- or INIT-designated objects will be destroyed along with
459 * the rest of the registered objects; this actually works by internally
460 * calling RVAL on @name immediately before calling the ->cleanup() rou-
461 * tine, converting this case into just another instance of case #1
462 *
463 * In case #2, if the type of @name->o is not the same as the function's declar-
464 * ed return type, the Macro API will generate an IPT warning but the C API will
465 * *not* warn you, so take extra care to pass a handle (pointing to a registry
466 * slot managing an object) of the same type as the function's return type.
467 *
468 * Macro API:
469 * return SBRM_CLEANUP([name]);
470 * C API:
471 * return reg->cleanup(reg, 0); // free everything, return NULL
472 * ~> return reg->cleanup(reg, name); // don't free @name->o, return it
473 *----------------------------------------------------------------------------*/
474#define SBRM_PRIV_CLEAN1(name) (SBRM_PRIV_REGISTRY->rv = (void*)name, \
475 SBRM_PRIV_DECONSTIFY(name) = SBRM_LCP(__sbrm_entry_t, name), \
476 SBRM_PRIV_REGISTRY->cleanup(SBRM_PRIV_REGISTRY, 0), name->o)
477// this _may_ be more portable than the DECONSTIFY(...) = LCP(...) construct:
478//memcpy((void*)&name, &(void*){SBRM_LCP(__sbrm_entry_t, name)}, sizeof(void*))
479#define SBRM_PRIV_CLEAN0() SBRM_PRIV_REGISTRY->cleanup(SBRM_PRIV_REGISTRY, 0)
480#define SBRM_CLEANUP(...) \
481 SBRM_PRIV_CAT(SBRM_PRIV_CLEAN, SBRM_PRIV_NARGS(__VA_ARGS__)) (__VA_ARGS__)
482
483/*----------------------------------------------------------------------------*
484 * YANK: writes the ptr to the managed object into dest and de-registers it so
485 * that the FREE, CLEANUP, ABORT, and _ commands will not free it when called.
486 * The .o member of the slot it occupied (pointed to by @name) is set to NULL,
487 * but the slot (and the @name handle pointing to it) can still be reused for
488 * other objects of the same type. Future work: make YANK return the slot to
489 * the usable pool so it can be reused with objects of different types.
490 *
491 * Macro API:
492 * type * dest;
493 * ~> if (...) dest = SBRM_YANK(name);
494 * if (...) dest = SBRM_YANK(name, type); // type-safe alternative
495 * C API:
496 * type * dest;
497 * ~> if (...) dest = reg->yank(reg, name);
498 * if (...) { dest = name->o; name->o = NULL; } // type-safe alternative
499 *----------------------------------------------------------------------------*/
500#define SBRM_PRIV_YANK1(name, type) (type*)SBRM_PRIV_REGISTRY->yank(name)
501#define SBRM_PRIV_YANK0(name, ...) SBRM_PRIV_REGISTRY->yank(name)
502#define SBRM_YANK(name, ...) SBRM_PRIV_CAT(SBRM_PRIV_YANK, \
503 SBRM_PRIV_NARGS(__VA_ARGS__)) (name, ##__VA_ARGS__)
504
505/*----------------------------------------------------------------------------*
506 * ABORT: propagates any existing error to the ABORT call site (or sets code if
507 * supplied, or UNSPECIFIED if not), prints the error stack, then invokes
508 * CLEANUP and returns its result (either NULL or a pointer to the return object
509 * established by INIT or RVAL) and so can and should be used with 'return'. A
510 * printf-style format string and trailing arguments may optionally accompany
511 * the supplied error code.
512 *
513 * Macro API:
514 * if (!cpl_error_get_code()) return SBRM_ABORT(); // CPL_ERROR_UNSPECIFIED
515 * if (cpl_error_get_code()) return SBRM_ABORT(); // propagates
516 * if (something_bad) return SBRM_ABORT(code);
517 * if (really_bad) return SBRM_ABORT(code, "Not %s", "good");
518 * C API:
519 * #define HERE cpl_func, __FILE__, __LINE__ // or use SBRM_HERE
520 * if (!cpl_error_get_code()) return reg->abort(reg, HERE, 0, 0); // UNSPEC
521 * if (cpl_error_get_code()) return reg->abort(reg, HERE, 0, 0); // propgt
522 * if (something_bad) return reg->abort(reg, HERE, code, 0);
523 * if (really_bad) return reg->abort(reg, HERE, code, "Not %s",
524 * "good");
525 *----------------------------------------------------------------------------*/
526#define SBRM_PRIV_ABORT11(code, ...) 0, 0
527#define SBRM_PRIV_ABORT10(code, ...) 0, ##__VA_ARGS__
528#define SBRM_PRIV_ABORT01(code, ...) code, 0
529#define SBRM_PRIV_ABORT00(code, ...) code, ##__VA_ARGS__
530#define SBRM_ABORT(code, ...) SBRM_PRIV_REGISTRY->abort(SBRM_PRIV_REGISTRY, \
531 SBRM_HERE, SBRM_PRIV_CAT(SBRM_PRIV_ABORT, SBRM_PRIV_CAT(SBRM_PRIV_ISEMPTY( \
532 code), SBRM_PRIV_ISEMPTY(__VA_ARGS__))) (code, ##__VA_ARGS__))
533
534/*----------------------------------------------------------------------------*
535 * DBG/INFO/WARN: propagates any existing error to the DBG/INFO/WARN call site
536 * (or sets UNSPECIFIED if none), prints the error stack and any supplied addi-
537 * tional details using cpl_msg_debug/info/warning, then clears the error stack.
538 * Additional details are optionally given as a printf-style format string and
539 * trailing arguments. You can think of these routines as converting what would
540 * otherwise be a fatal CPL error into a non-fatal debugging, informational, or
541 * warning message.
542 *
543 * Macro API:
544 * if (!cpl_error_get_code()) SBRM_WARN(); // UNSPECIFIED, log, reset
545 * if (cpl_error_get_code()) SBRM_WARN(); // propagate, log, reset
546 * SBRM_WARN("additional %s", "details"); // UNSP|prop, log+details, reset
547 * C API:
548 * #define HERE cpl_func, __FILE__, __LINE__ // or use SBRM_HERE
549 * if (!cpl_error_get_code()) reg->warn(reg, HERE, 0);
550 * if (cpl_error_get_code()) reg->warn(reg, HERE, 0);
551 * if (...) reg->warn(reg, HERE, "additional %s", "details");
552 *
553 * See Also: the X macro
554 *----------------------------------------------------------------------------*/
555#define SBRM_PRIV_DBG0(...) __VA_ARGS__
556#define SBRM_PRIV_DBG1(...) 0
557#define SBRM_DBG(...) SBRM_PRIV_REGISTRY->debug(SBRM_PRIV_REGISTRY, SBRM_HERE, \
558 SBRM_PRIV_CAT(SBRM_PRIV_DBG, SBRM_PRIV_ISEMPTY(__VA_ARGS__)) (__VA_ARGS__))
559
560#define SBRM_PRIV_INFO0(...) __VA_ARGS__
561#define SBRM_PRIV_INFO1(...) 0
562#define SBRM_INFO(...) SBRM_PRIV_REGISTRY->info(SBRM_PRIV_REGISTRY, SBRM_HERE, \
563 SBRM_PRIV_CAT(SBRM_PRIV_INFO, SBRM_PRIV_ISEMPTY(__VA_ARGS__)) (__VA_ARGS__))
564
565#define SBRM_PRIV_WARN0(...) __VA_ARGS__
566#define SBRM_PRIV_WARN1(...) 0
567#define SBRM_WARN(...) SBRM_PRIV_REGISTRY->warn(SBRM_PRIV_REGISTRY, SBRM_HERE, \
568 SBRM_PRIV_CAT(SBRM_PRIV_WARN, SBRM_PRIV_ISEMPTY(__VA_ARGS__)) (__VA_ARGS__))
569
570/*----------------------------------------------------------------------------*
571 * _: this macro is syntactic suger to discretely inject error checking and re-
572 * source handling in a way that doesn't visually clutter the program or detract
573 * from the meaning of the code. It simply adds a CPL error check after any
574 * function it's used on and, if there is one, invokes ABORT and returns its re-
575 * sult (either NULL or a ptr to the object established by INIT or RVAL). ABORT
576 * of course frees all memory buffers pointed to by the registered objects so no
577 * memory is leaked. _ is disabled by default so you must add
578 * '#define SBRM_UNDERSCORE_MACRO' before '#include "sbrm.h"' if you want to use
579 * it.
580 *
581 * An easy way to disable it after adding it throughout your code is to simply
582 * add '#define _' after '#include "sbrm.h"'. This is usually sufficient, but
583 * you may need to put '#define _(...) (__VA_ARGS__)' after '#include "sbrm.h"'
584 * instead, especially if your code uses '_' elsewhere (as a variable name per-
585 * haps).
586 *
587 * X: this macro operates similarly to _ except that it simply logs and clears
588 * the error via a call to WARN, allowing execution to continue.
589 *
590 * Macro API:
591 * The _ & X macros only work in two simple forms:
592 * 1) var = function _(...);
593 * 2) function _(...);
594 *
595 * For example, functionA(functionB _(...)); won't compile unless you decom-
596 * pose it into the above forms: ret = functionB _(...); functionA(ret);
597 *
598 * The above forms must be enclosed in {} if used as the only statement in a
599 * conditional or loop block:
600 * - if (test) var = function(); -> if (test) { var = function _(); }
601 * - while (test) function(...); -> while (test) { function _(...); }
602 *
603 * Explicitly call ABORT for anything more complex than the above.
604 *
605 * C API:
606 * #define HERE cpl_func, __FILE__, __LINE__ // or use SBRM_HERE
607 *
608 * // the following is equivalent to: [var =] function _(...);
609 * [var =] function(...);
610 * if (cpl_error_get_code()) return reg->abort(reg, HERE-1, 0, 0);
611 *
612 * // the following is equivalent to: [var =] function X(...);
613 * [var =] function(...);
614 * if (cpl_error_get_code()) reg->warn(reg, HERE-1, 0);
615 *----------------------------------------------------------------------------*/
616#if defined(_)
617# if defined(SBRM_UNDERSCORE_MACRO)
618# error "Sorry, SBRM's _ macro cannot be used: the identifier is taken"
619# endif
620#else
621# if defined(SBRM_UNDERSCORE_MACRO)
622# define _(...) (__VA_ARGS__);/*detect, log, & return*/ \
623 SBRM_PRIV_ATOM( if (cpl_error_get_code()) return SBRM_ABORT(); )
624# define X(...) (__VA_ARGS__);/*detect, log, & reset (for known errs)*/ \
625 SBRM_PRIV_ATOM( if (cpl_error_get_code()) SBRM_WARN(); )
626# endif
627#endif
628
629/*-----------------------------------------------------------------------------
630 Typedefs
631 -----------------------------------------------------------------------------*/
632
633typedef union { void * mut; void * const immut; } __sbrm_deconst_t;
634typedef SBRM_HANDLE_STRUCT(void) * __sbrm_void_wrapper_t;
635typedef struct { void * o; char fp; void * dtor; int ident; } __sbrm_entry_t;
636typedef struct __sbrm_registry_s sbrm_registry; // forward declare
637struct __sbrm_registry_s {
638 const int sz; // this must be first in the struct
639 int avail;
640 __sbrm_entry_t * rv;
641 cpl_errorstate estate;
642 void * (*set)(sbrm_registry * r, char fp, void * dtor, void * initval,
643 const int ident);
644 __sbrm_void_wrapper_t (*reset1)(sbrm_registry * r, void * target);
645 void ** (*reset2)(sbrm_registry * r, void * target);
646 void (*free)(sbrm_registry * r, void * target);
647 void * (*cleanup)(sbrm_registry * r, void * target);
648 void (*debug)(sbrm_registry * r, SBRM_PRIV_HERE_SIG, const char * fmt, ...);
649 void (*info)(sbrm_registry * r, SBRM_PRIV_HERE_SIG, const char * fmt, ...);
650 void (*warn)(sbrm_registry * r, SBRM_PRIV_HERE_SIG, const char * fmt, ...);
651 void * (*abort)(sbrm_registry * r, SBRM_PRIV_HERE_SIG, const unsigned code,
652 const char * fmt, ...);
653 void (*rval)(sbrm_registry * r, void * target);
654 void * (*yank)(void * target);
655 __sbrm_entry_t list[]; // this must be last in the struct
656};
657
658/*-----------------------------------------------------------------------------
659 Function Prototypes
660 -----------------------------------------------------------------------------*/
661
662void * __sbrm_cp( void * dest, void * src, size_t sz, int release );
663sbrm_registry * sbrm_init(const int sz, SBRM_PRIV_HERE_SIG);
664
665/*-----------------------------------------------------------------------------
666 Other Public Defines
667 -----------------------------------------------------------------------------*/
668
669/*-----------------------------------------------------------------------------
670 * These are useful on their own and so should probably be factored out into
671 * a separate file. Some are used by SBRM.
672 *----------------------------------------------------------------------------*/
673
674/*----------------------------------------------------------------------------*
675 * ALLOC: in-place dynamic allocation & initialisation of n POD types, n>=1, re-
676 * turned as a ptr to a contiguous array of type @type and size @n. @n can be
677 * supplied in one of 2 ways:
678 * 1 - n initial values specified after @type, or
679 * 2 - inside brackets as part of @type, for example: int[n]
680 *
681 * Because the array is first created on the stack before being copied into dy-
682 * namically allocated memory, there is a risk of overflowing the stack... so
683 * only use ALLOC to create smallish items. Initialisation values, if provided,
684 * are evaluated more than once so it is strongly recommended that they be sup-
685 * plied as literals or variables: expressions will have side effects (if any)
686 * incurred multiple times.
687 *
688 * Macro API:
689 * #define ALLOC SBRM_ALLOC
690 * int * buf = ALLOC(int, 5); // ptr to a single int, initialised to 5
691 * double * buf = ALLOC(double[50]); // 50 doubles, uninitialised
692 * float * buf = ALLOC(float[], 2.3, 3.4, 4.5); // 3 floats, initialised
693 * char * buf = ALLOC(char, 'a', 'b'); // 1 char init'd to 'a', 'b' ignored
694 * size_t * buf = ALLOC(size_t[10], 56, 12) // 10 size_t's, partly init'd
695 * int var = 7; int * buf = ALLOC(int[var]); // won't compile: no VLAs
696 * typedef struct { int x,y; } P;
697 * P * p = ALLOC(P, 10, 20);
698 * P * p = ALLOC(P, .y = 7); // x defaults to 0
699 * P * p = ALLOC(P[], {10,20}, {.y=7}); // 2 initialised P's
700 *----------------------------------------------------------------------------*/
701#define SBRM_ALLOC(type, ...) memcpy(cpl_malloc(sizeof((type){__VA_ARGS__})), \
702 (&(type){__VA_ARGS__}), sizeof((type){__VA_ARGS__}))
703
704/*----------------------------------------------------------------------------*
705 * VLIST/VALLOC/NALLOC: these macros operate on special structs whose first mem-
706 * ber is an integral type (preferrably const and unsigned) and whose last mem-
707 * ber is a flexible array member (FAM) of type @scalar[]. VLIST declares the
708 * simplest of such structs (here referred to as a vlist) containing only the 2
709 * required members: 'sz' (a const size_t) and 'data' (an array of @scalar's).
710 * VALLOC dynamically allocates & initialises vlists and more complex variants
711 * with additional members between the first & last. It sizes the last member
712 * (the FAM) to hold @n @scalar's, initialises the first member to @n, and op-
713 * tionally initialises members between them (if any) with arguments supplied
714 * after @n (if any). The FAM itself is not initialised except for very basic
715 * initialisation provided by the use of cpl_calloc (setting all bits to zero),
716 * so you should do your own array element initialisation after the VALLOC call
717 * or, if using a simple vlist generated by VLIST, use NALLOC instead of VALLOC:
718 * it takes the same args as VALLOC and behaves almost the same except that any
719 * args after @n are used to initialise the FAM instead of the members between
720 * it and @n (since there are none). As a result, there should be at least @n
721 * scalar initialisers after @n or else you'll get Undefined Behaviour. VALLOC
722 * and NALLOC both allocate the struct and the FAM as a single block of memory
723 * which means a single cpl_free call is sufficient to release its memory. FAM
724 * structs provide an easy way to define "list-of" types where not already pro-
725 * vided.
726 *
727 * Macro API:
728 * typedef SBRM_VLIST(scalar) list; // define a list of scalar's
729 * list * ls = SBRM_VALLOC(list, scalar, n); // instantiate the list
730 *
731 * Examples:
732 * #define VALLOC SBRM_VALLOC // shortened for brevity
733 * #define VLIST SBRM_VLIST // shortened for brevity
734 *
735 * // list of PODs & simple structs
736 * // ----------------------------------------------------------------------
737 * typedef struct { int i; float f; char * s; } foo;
738 * typedef VLIST(foo) foolist;
739 *
740 * // with SBRM
741 * SBRM_SET(list1, foolist, cpl_free) = VALLOC(foolist, foo, 17);
742 * for (size_t i = 0; i < list1->o->sz; ++i) // initialise / access
743 * list1->o->data[i] = { .f=7.1, .i=42, .s="bar" };
744 * SBRM_SET(list2, foolist, cpl_free) = NALLOC(foolist, foo, 2,
745 * { .f=7.1, .i=42, .s="bar" }, { 0, 3.3, "foo" }, { 7, 0.0, "baz" });
746 *
747 * // without SBRM
748 * foolist * list1 = VALLOC(foolist, foo, 17); // instantiate
749 * for (size_t i = 0; i < list1->sz; ++i) // initialise / access
750 * list1->data[i] = { .f=7.1, .i=42, .s="bar" };
751 * foolist * list2 = NALLOC(foolist, foo, 1, { .f=7.1, .i=42, .s="bar" });
752 * cpl_free(list1); // destroy
753 * cpl_free(list2); // destroy
754 *
755 * // list of pointers to dynamically allocated objects
756 * // ----------------------------------------------------------------------
757 * typedef VLIST(cpl_mask*) masklist;
758 * void masklist_delete(masklist * ls) { // custom dtor
759 * for (size_t i = 0; i < ls->sz; ++i) cpl_mask_delete(ls->data[i]);
760 * cpl_free(ls); }
761 *
762 * // with SBRM
763 * SBRM_SET(list, masklist) = VALLOC(masklist, cpl_mask*, 17);
764 * for (size_t i = 0; i < list->o->sz; ++i) // initialise / access
765 * list->o->data[i] = cpl_mask_new(...);
766 *
767 * // without SBRM
768 * masklist * list = VALLOC(masklist, cpl_mask*, 17); // instantiate
769 * for (size_t i = 0; i < list->sz; ++i) // initialise / access
770 * list->data[i] = cpl_mask_new(...);
771 * masklist_delete(list); // destroy
772 *----------------------------------------------------------------------------*/
773#define SBRM_PRIV_VALLOC1(...)
774#define SBRM_PRIV_VALLOC0(...) , ##__VA_ARGS__
775#define SBRM_VALLOC(list, scalar, n, ...) memcpy(cpl_calloc(1, sizeof(list) + \
776 (n) * sizeof(scalar)), (&(list){(n) SBRM_PRIV_CAT(SBRM_PRIV_VALLOC, \
777 SBRM_PRIV_ISEMPTY(__VA_ARGS__))(__VA_ARGS__)}), sizeof(list))
778#define SBRM_NALLOC(list, scalar, n, ...) memcpy(SBRM_VALLOC(list, scalar, n) +\
779 offsetof(list, data), (&(scalar[]){__VA_ARGS__}), (n) * sizeof(scalar)) - \
780 offsetof(list, data)
781#define SBRM_VLIST(scalar) struct { const size_t sz; scalar data[]; }
782
783/*----------------------------------------------------------------------------*
784 * LOCAL: in-place localisation of a dynamic object, which is then freed; ass-
785 * umes the object was created using the CPL allocators (cpl_malloc, etc); re-
786 * turns a ptr to the local value just created: it takes a ptr to a heap-based
787 * object and returns a ptr to a stack-based object of the same value... but the
788 * new stack-based instance is only a shallow copy of the heap-based instance,
789 * so should probably only be used to localise simple types; because it copies
790 * an object from the heap to the stack, you must be careful not to localise
791 * very large objects or arrays that may overflow the stack; intended to be used
792 * in functions that must return values rather than ptrs to values. I haven't
793 * found a way yet to make it work with _, ABORT, or CLEANUP... unfortunately.
794 *
795 * LCP: similar to LOCAL but doesn't release the 'src' object; useful for making
796 * in-place copies of local (on the stack) objects: int x, *y = LCP(int, &x);
797 * Also doesn't typecast the resulting pointer (handy for type punning).
798 *----------------------------------------------------------------------------*/
799#define SBRM_LOCAL(type, src) (type*)__sbrm_cp(&(type){0},(src),sizeof(type),1)
800#define SBRM_LCP(type, src) __sbrm_cp(&(type){0}, (src), sizeof(type), 0)
801
802
803CPL_END_DECLS
804
805#endif /* SBRM_H_ */