Objectively
Object oriented framework for C.
Loading...
Searching...
No Matches
Class.h File Reference

Classes describe the state and behavior of an Objectively type. More...

Go to the source code of this file.

Data Structures

struct  Class
 The runtime representation of a Class. More...
 
struct  ClassDef
 ClassDefs are passed to _initialize via an archetype to initialize a Class. More...
 

Macros

#define alloc(type)    ((type *) _alloc(_##type()))
 Allocate and initialize and instance of type.
 
#define cast(type, obj)    ((type *) _cast(_##type(), (const ident) obj))
 Safely cast obj to type.
 
#define classnameof(obj)    classof(obj)->def.name
 Resolve the Class name of the given Object instance.
 
#define classof(obj)    ((Object *) obj)->clazz
 Resolve the Class of an Object instance.
 
#define instanceof(type, obj)    (isobject(obj) && $((Object *) obj, isKindOfClass, _##type()))
 Test if the given pointer is an instance of the specified type.
 
#define interfaceof(type, clazz)    ((type##Interface *) (clazz)->interface)
 Resolve the typed interface of a Class.
 
#define isobject(obj)    (obj && *((unsigned int *) obj) == OBJECTIVELY_MAGIC)
 Test if the given pointer is an Object.
 
#define obj
 
#define OBJECTIVELY_MAGIC   0xdeadbeef
 The header value identifying Objectively types.
 
#define super(type, obj, method, ...)    interfaceof(type, _Class()->def.superclass)->method(cast(type, obj), ## __VA_ARGS__)
 
#define type
 

Functions

OBJECTIVELY_EXPORT ident _alloc (Class *clazz)
 Instantiate a type through the given Class.
 
OBJECTIVELY_EXPORT ident _cast (const Class *clazz, const ident obj)
 Perform a type-checking cast.
 
OBJECTIVELY_EXPORT Class * _initialize (const ClassDef *clazz)
 Initializes the given Class.
 
OBJECTIVELY_EXPORT void addClassImage (ident handle, const ident address)
 Registers an image that provides Classes, e.g. a plugin.
 
OBJECTIVELY_EXPORT Class * classForName (const char *name)
 
OBJECTIVELY_EXPORT ident release (ident obj)
 Atomically decrement the given Object's reference count. If the resulting reference count is 0, the Object is deallocated.
 
OBJECTIVELY_EXPORT void removeClassImage (ident handle)
 Unregisters an image, and every Class it declared.
 
OBJECTIVELY_EXPORT ident retain (ident obj)
 Atomically increment the given Object's reference count.
 

Variables

OBJECTIVELY_EXPORT size_t _pageSize
 The page size, in bytes, of the target host.
 

Detailed Description

Classes describe the state and behavior of an Objectively type.

Definition in file Class.h.

Macro Definition Documentation

◆ alloc

#define alloc (   type)     ((type *) _alloc(_##type()))

Allocate and initialize and instance of type.

Definition at line 226 of file Class.h.

267 { \
268 typeof(obj) _obj = (obj); \
269 ((typeof(_obj->interface[0])) classof(_obj)->interface)->method(_obj, ## __VA_ARGS__); \
270 })
271
273#define $$(type, method, ...) \
274 ({ \
275 interfaceof(type, _##type())->method(__VA_ARGS__); \
276 })
277
281#define super(type, obj, method, ...) \
282 interfaceof(type, _Class()->def.superclass)->method(cast(type, obj), ## __VA_ARGS__)
#define _Class
Definition Array.c:75
#define obj
#define cast(type, obj)
Safely cast obj to type.
Definition Class.h:232
#define type
#define classof(obj)
Resolve the Class of an Object instance.
Definition Class.h:238
#define super(type, obj, method,...)

◆ cast

#define cast (   type,
  obj 
)     ((type *) _cast(_##type(), (const ident) obj))

Safely cast obj to type.

Definition at line 232 of file Class.h.

◆ classnameof

#define classnameof (   obj)     classof(obj)->def.name

Resolve the Class name of the given Object instance.

Definition at line 244 of file Class.h.

◆ classof

#define classof (   obj)     ((Object *) obj)->clazz

Resolve the Class of an Object instance.

Definition at line 238 of file Class.h.

◆ instanceof

#define instanceof (   type,
  obj 
)     (isobject(obj) && $((Object *) obj, isKindOfClass, _##type()))

Test if the given pointer is an instance of the specified type.

Definition at line 220 of file Class.h.

◆ interfaceof

#define interfaceof (   type,
  clazz 
)     ((type##Interface *) (clazz)->interface)

Resolve the typed interface of a Class.

Definition at line 250 of file Class.h.

◆ isobject

#define isobject (   obj)     (obj && *((unsigned int *) obj) == OBJECTIVELY_MAGIC)

Test if the given pointer is an Object.

Definition at line 214 of file Class.h.

◆ obj

#define obj
Value:
, method, ...) \
({ \
typeof(obj) _obj = (obj); \
((typeof(_obj->interface[0])) classof(_obj)->interface)->method(_obj, ## __VA_ARGS__); \
})

◆ OBJECTIVELY_MAGIC

#define OBJECTIVELY_MAGIC   0xdeadbeef

The header value identifying Objectively types.

Definition at line 209 of file Class.h.

◆ super

#define super (   type,
  obj,
  method,
  ... 
)     interfaceof(type, _Class()->def.superclass)->method(cast(type, obj), ## __VA_ARGS__)

◆ type

#define type
Value:
, method, ...) \
({ \
interfaceof(type, _##type())->method(__VA_ARGS__); \
})

Function Documentation

◆ _alloc()

OBJECTIVELY_EXPORT ident _alloc ( Class *  clazz)

Instantiate a type through the given Class.

Definition at line 198 of file Class.c.

198 {
199
200 ident obj = calloc(1, clazz->def.instanceSize);
201 assert(obj);
202
203 Object *object = (Object *) obj;
204
205 object->magic = OBJECTIVELY_MAGIC;
206 object->clazz = clazz;
207 object->referenceCount = 1;
208
209 return obj;
210}
#define OBJECTIVELY_MAGIC
The header value identifying Objectively types.
Definition Class.h:209
void * ident
The identity type, similar to Objective-C id.
Definition Types.h:49
size_t instanceSize
The instance size (required).
Definition Class.h:69
ClassDef def
The Class definition.
Definition Class.h:95
Object is the root Class of The Objectively Class hierarchy.
Definition Object.h:46
unsigned int magic
A header to allow introspection of Object types.
Definition Object.h:50

◆ _cast()

OBJECTIVELY_EXPORT ident _cast ( const Class *  clazz,
const ident  obj 
)

Perform a type-checking cast.

Definition at line 212 of file Class.c.

212 {
213
214 if (obj) {
215 const Class *c = ((Object *) obj)->clazz;
216 while (c) {
217
218 // as a special case, we optimize for _Object
219 if (c == clazz || clazz == _Object()) {
220 break;
221 }
222
223 c = c->def.superclass;
224 }
225 assert(c);
226 }
227
228 return (ident) obj;
229}
Class * _Object(void)
Definition Object.c:136
Class * superclass
The superclass (required). e.g. _Object().
Definition Class.h:84
The runtime representation of a Class.
Definition Class.h:90

◆ _initialize()

OBJECTIVELY_EXPORT Class * _initialize ( const ClassDef *  clazz)

Initializes the given Class.

Parameters
clazzThe Class descriptor.
Returns
The initialized Class.

Definition at line 151 of file Class.c.

151 {
152
153 static Once once;
154 do_once(&once, setup());
155
156 assert(def);
157 assert(def->name);
158 assert(def->instanceSize);
159 assert(def->interfaceSize);
160
161 Class *clazz = calloc(1, sizeof(Class));
162 assert(clazz);
163
164 clazz->def = *def;
165
166 clazz->interface = calloc(1, def->interfaceSize);
167 assert(clazz->interface);
168
169 Class *superclass = clazz->def.superclass;
170 if (superclass) {
171
172 assert(superclass->def.instanceSize <= def->instanceSize);
173 assert(superclass->def.interfaceSize <= def->interfaceSize);
174
175 memcpy(clazz->interface, superclass->interface, superclass->def.interfaceSize);
176 }
177
178 if (clazz->def.initialize) {
179 clazz->def.initialize(clazz);
180 }
181
182 /* def.name is a literal in the declaring image, where the ClassDef itself is
183 * a compound literal with automatic storage. */
184 clazz->image = imageForAddress((ident) def->name);
185
186 /* Taken here rather than around the whole function: def.initialize above can
187 * reach other archetypes, and so this, before that Class is published. */
188 pthread_mutex_lock(&_classesLock);
189
190 clazz->next = _classes;
191 _classes = clazz;
192
193 pthread_mutex_unlock(&_classesLock);
194
195 return clazz;
196}
static const ident imageForAddress(const ident address)
Definition Class.c:131
static pthread_mutex_t _classesLock
Guards the structure of _classes. MUST NOT be held across dlsym, dlopen, or a Class initializer,...
Definition Class.c:52
static void setup(void)
Called when initializing Object to setup Objectively.
Definition Class.c:113
static Class * _classes
Definition Class.c:46
long Once
The Once type.
Definition Once.h:37
#define do_once(once, block)
Executes the given block at most one time.
Definition Once.h:43
size_t interfaceSize
The interface size (required).
Definition Class.h:74
void(* initialize)(Class *clazz)
The Class initializer (optional).
Definition Class.h:64
ident image
The base address of the image that declared this Class.
Definition Class.h:113
Class * next
Provides chaining of initialized Classes.
Definition Class.h:105
ident interface
The interface of the Class.
Definition Class.h:100

◆ addClassImage()

OBJECTIVELY_EXPORT void addClassImage ( ident  handle,
const ident  address 
)

Registers an image that provides Classes, e.g. a plugin.

Parameters
handleA handle from dlopen.
addressAny address within that image, such as the entry point the application resolved from handle.

classForName resolves a name it has not yet initialized through the process-wide namespace, which holds only images loaded RTLD_GLOBAL, and which Windows does not have at all. An application that loads Classes from a plugin registers it here instead, and may then load it RTLD_LOCAL - which is how it keeps two plugins exporting the same symbols from coalescing.

Remarks
Registered images are searched most recently added first, so a newly loaded plugin answers ahead of the one it replaced.
Resolution is by Objectively's own convention, the Class name prefixed with an underscore, so nothing about the image has to be declared.
address is what identifies the image. Classes record the base address of the image that declared them, and the platforms report that for an address, not for a handle, so this takes one that the caller can vouch for. Aborts if no loaded image contains it.

Definition at line 231 of file Class.c.

231 {
232
233 assert(handle);
234 assert(address);
235
236 const ident image = imageForAddress(address);
237 if (image == NULL) {
238 fprintf(stderr, "%s: no image contains %p\n", __func__, address);
239 abort();
240 }
241
242 ClassImage *classImage = calloc(1, sizeof(ClassImage));
243 assert(classImage);
244
245 classImage->handle = handle;
246 classImage->image = image;
247
248 /* Published the same way a Class is, and for the same reason: classForName
249 * walks this list on any thread. */
250 classImage->next = __atomic_load_n(&_images, __ATOMIC_RELAXED);
251 while (!__atomic_compare_exchange_n(&_images, &classImage->next, classImage, 1,
252 __ATOMIC_RELEASE, __ATOMIC_RELAXED)) ;
253}
static ClassImage * _images
The registered images providing Classes, most recently added first. Published atomically rather than ...
Definition Class.c:71
A registered image: the handle the application holds, and the base address that the Classes it declar...
Definition Class.c:59
ident image
Definition Class.c:61
ident handle
Definition Class.c:60
ClassImage * next
Definition Class.c:62

◆ classForName()

OBJECTIVELY_EXPORT Class * classForName ( const char *  name)
Returns
The Class with the given name, or NULL if no such Class has been initialized.
Remarks
Classes already initialized are answered first, then each registered image, then the process-wide namespace.

Definition at line 300 of file Class.c.

300 {
301
302 if (name) {
303 pthread_mutex_lock(&_classesLock);
304
305 Class *c = _classes;
306 while (c) {
307 if (strcmp(name, c->def.name) == 0) {
308 break;
309 }
310 c = c->next;
311 }
312
313 pthread_mutex_unlock(&_classesLock);
314
315 if (c) {
316 return c;
317 }
318
319 char *s;
320 if (asprintf(&s, "_%s", name) > 0) {
321 Class *clazz = NULL;
322 Class *(*archetype)(void) = NULL;
323
324 for (ClassImage *i = __atomic_load_n(&_images, __ATOMIC_ACQUIRE);
325 i && archetype == NULL; i = i->next) {
326
327 ident handle = __atomic_load_n(&i->handle, __ATOMIC_ACQUIRE);
328 if (handle) {
329 archetype = dlsym(handle, s);
330 }
331 }
332
333 if (archetype == NULL) {
334#if defined(_WIN32)
335 static Once once;
336 static ident handle;
337 do_once(&once, { handle = dlopen(NULL, RTLD_LAZY); });
338 archetype = handle ? dlsym(handle, s) : NULL;
339#else
340 archetype = dlsym(RTLD_DEFAULT, s);
341#endif
342 }
343
344 if (archetype) {
345 clazz = archetype();
346 }
347
348 free(s);
349 return clazz;
350 }
351 }
352
353 return NULL;
354}
const char * name
The Class name (required).
Definition Class.h:79

◆ release()

OBJECTIVELY_EXPORT ident release ( ident  obj)

Atomically decrement the given Object's reference count. If the resulting reference count is 0, the Object is deallocated.

Returns
This function always returns NULL.

Definition at line 356 of file Class.c.

356 {
357
358 if (obj) {
359 Object *object = cast(Object, obj);
360
361 assert(object);
362
363 if (__atomic_fetch_sub(&object->referenceCount, 1, __ATOMIC_RELEASE) == 1) {
364 __atomic_thread_fence(__ATOMIC_ACQUIRE);
365 $(object, dealloc);
366 }
367 }
368
369 return NULL;
370}
static void dealloc(Object *self)
Definition Array.c:99

◆ removeClassImage()

OBJECTIVELY_EXPORT void removeClassImage ( ident  handle)

Unregisters an image, and every Class it declared.

Parameters
handleThe handle given to addClassImage.
Remarks
Classes initialized from an image outlive it otherwise: they are cached by name, and classForName answers from that cache ahead of any image. On a platform where closing an image really unmaps it - which dlclose does on Linux and FreeLibrary does on Windows - the next lookup would then read a ClassDef that is no longer mapped. classForName compares the name of every Class it walks, so one left behind breaks every lookup, not only its own.
The Classes are not destroyed, and this is not an oversight. dlclose does not unmap on macOS, so an archetype that has already run keeps answering from its own static Class * for as long as the process lives, and would hand back whatever this freed. Unregistering is the only thing that is safe whether the image goes away or stays: the Class becomes unreachable by name, and remains valid for the archetype that owns it. The cost is the Class and its interface, which are not reclaimed.
MUST be called while the handle is still open, and only once nothing instantiated from that image survives.
Aborts if handle was not registered, rather than leaving the Classes it declared behind, which is the failure this exists to prevent. Two calls for the same handle abort on the second, whichever order they arrive in.

Definition at line 255 of file Class.c.

255 {
256
257 assert(handle);
258
259 /* Held from the search to the last unlink, so that retiring an image and
260 * dropping its Classes is one operation, and a second call for the same handle
261 * finds it already gone. Nothing here reaches the loader. */
262 pthread_mutex_lock(&_classesLock);
263
264 ident image = NULL;
265
266 /* Retired in place rather than unlinked, so that a concurrent classForName
267 * parked on this node still has a next to follow, and never reads a node that
268 * has been freed. Retiring is a single store of the handle it matches on, so
269 * that walk sees this image or does not, and never half of it. Retired nodes
270 * are freed at teardown; reusing one would put a newly registered image where
271 * the retired one sat, and lookup order is newest first. */
272 for (ClassImage *i = __atomic_load_n(&_images, __ATOMIC_ACQUIRE); i; i = i->next) {
273 if (__atomic_load_n(&i->handle, __ATOMIC_ACQUIRE) == handle) {
274 image = i->image;
275 __atomic_store_n(&i->handle, NULL, __ATOMIC_RELEASE);
276 break;
277 }
278 }
279
280 if (image == NULL) {
281 fprintf(stderr, "%s: %p was never registered\n", __func__, handle);
282 abort();
283 }
284
285 Class **classes = &_classes;
286 while (*classes) {
287 Class *clazz = *classes;
288
289 if (clazz->image == image) {
290 *classes = clazz->next;
291 clazz->next = NULL;
292 } else {
293 classes = &clazz->next;
294 }
295 }
296
297 pthread_mutex_unlock(&_classesLock);
298}

◆ retain()

OBJECTIVELY_EXPORT ident retain ( ident  obj)

Atomically increment the given Object's reference count.

Returns
The Object.
Remarks
By calling this, the caller is expressing ownership of the Object, and preventing it from being released. Be sure to balance calls to retain with calls to release.

Definition at line 372 of file Class.c.

372 {
373
374 Object *object = cast(Object, obj);
375
376 assert(object);
377
378 /* A reference count of zero means another thread is already inside dealloc,
379 * and the caller is retaining memory that is about to be freed. */
380 unsigned int referenceCount = __atomic_load_n(&object->referenceCount, __ATOMIC_RELAXED);
381 do {
382 if (referenceCount == 0) {
383 fprintf(stderr, "%s: %p is being deallocated\n", __func__, object);
384 abort();
385 }
386 } while (!__atomic_compare_exchange_n(&object->referenceCount, &referenceCount,
387 referenceCount + 1, 1, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
388
389 return obj;
390}

Variable Documentation

◆ _pageSize

OBJECTIVELY_EXPORT size_t _pageSize

The page size, in bytes, of the target host.

Definition at line 204 of file Class.h.