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)
 Registers an image that provides Classes, e.g. a plugin.
 
OBJECTIVELY_EXPORT ClassclassForName (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 222 of file Class.h.

252 { \
253 typeof(obj) _obj = obj; \
254 _obj->interface->method(_obj, ## __VA_ARGS__); \
255 })
256
258#define $$(type, method, ...) \
259 ({ \
260 interfaceof(type, _##type())->method(__VA_ARGS__); \
261 })
262
266#define super(type, obj, method, ...) \
267 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:228
#define type
#define super(type, obj, method,...)

◆ cast

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

Safely cast obj to type.

Definition at line 228 of file Class.h.

◆ classnameof

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

Resolve the Class name of the given Object instance.

Definition at line 240 of file Class.h.

◆ classof

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

Resolve the Class of an Object instance.

Definition at line 234 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 216 of file Class.h.

◆ interfaceof

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

Resolve the typed interface of a Class.

Definition at line 246 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 210 of file Class.h.

◆ obj

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

◆ OBJECTIVELY_MAGIC

#define OBJECTIVELY_MAGIC   0xdeadbeef

The header value identifying Objectively types.

Definition at line 205 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 170 of file Class.c.

170 {
171
172 ident obj = calloc(1, clazz->def.instanceSize);
173 assert(obj);
174
175 Object *object = (Object *) obj;
176
177 object->magic = OBJECTIVELY_MAGIC;
178 object->clazz = clazz;
179 object->referenceCount = 1;
180
181 ident interface = clazz->interface;
182 do {
183 *(ident *) (obj + clazz->def.interfaceOffset) = interface;
184 } while ((clazz = clazz->def.superclass));
185
186 return obj;
187}
#define OBJECTIVELY_MAGIC
The header value identifying Objectively types.
Definition Class.h:205
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
ptrdiff_t interfaceOffset
The interface offset (required).
Definition Class.h:74
Class * superclass
The superclass (required). e.g. _Object().
Definition Class.h:89
ClassDef def
The Class definition.
Definition Class.h:100
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 189 of file Class.c.

189 {
190
191 if (obj) {
192 const Class *c = ((Object *) obj)->clazz;
193 while (c) {
194
195 // as a special case, we optimize for _Object
196 if (c == clazz || clazz == _Object()) {
197 break;
198 }
199
200 c = c->def.superclass;
201 }
202 assert(c);
203 }
204
205 return (ident) obj;
206}
Class * _Object(void)
Definition Object.c:136
The runtime representation of a Class.
Definition Class.h:95

◆ _initialize()

OBJECTIVELY_EXPORT Class * _initialize ( const ClassDef clazz)

Initializes the given Class.

Parameters
clazzThe Class descriptor.
Returns
The initialized Class.

Definition at line 129 of file Class.c.

129 {
130
131 static Once once;
132 do_once(&once, setup());
133
134 assert(def);
135 assert(def->name);
136 assert(def->instanceSize);
137 assert(def->interfaceSize);
138 assert(def->interfaceOffset);
139
140 Class *clazz = calloc(1, sizeof(Class));
141 assert(clazz);
142
143 clazz->def = *def;
144
145 clazz->interface = calloc(1, def->interfaceSize);
146 assert(clazz->interface);
147
148 Class *superclass = clazz->def.superclass;
149 if (superclass) {
150
151 assert(superclass->def.instanceSize <= def->instanceSize);
152 assert(superclass->def.interfaceSize <= def->interfaceSize);
153
154 memcpy(clazz->interface, superclass->interface, superclass->def.interfaceSize);
155 }
156
157 if (clazz->def.initialize) {
158 clazz->def.initialize(clazz);
159 }
160
161 /* def.name is a literal in the declaring image, where the ClassDef itself is
162 * a compound literal with automatic storage. */
163 clazz->image = imageForAddress((ident) def->name);
164
165 clazz->next = __sync_lock_test_and_set(&_classes, clazz);
166
167 return clazz;
168}
static const ident imageForAddress(const ident address)
Definition Class.c:109
static void setup(void)
Called when initializing Object to setup Objectively.
Definition Class.c:91
static Class * _classes
Definition Class.c:49
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:79
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:118
Class * next
Provides chaining of initialized Classes.
Definition Class.h:110
ident interface
The interface of the Class.
Definition Class.h:105

◆ addClassImage()

OBJECTIVELY_EXPORT void addClassImage ( ident  handle)

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

Parameters
handleA handle from dlopen.

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.

Definition at line 284 of file Class.c.

284 {
285
286 assert(handle);
288
289 _classImages[_classImageCount++] = handle;
290}
#define MAX_CLASS_IMAGES
The registered images providing Classes.
Definition Class.c:57
static ident _classImages[MAX_CLASS_IMAGES]
Definition Class.c:58
static size_t _classImageCount
Definition Class.c:59

◆ 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 292 of file Class.c.

292 {
293
294 if (name) {
295 Class *c = _classes;
296 while (c) {
297 if (strcmp(name, c->def.name) == 0) {
298 return c;
299 }
300 c = c->next;
301 }
302
303 char *s;
304 if (asprintf(&s, "_%s", name) > 0) {
305 Class *clazz = NULL;
306 Class *(*archetype)(void) = NULL;
307
308 for (size_t i = _classImageCount; i > 0 && archetype == NULL; i--) {
309 archetype = dlsym(_classImages[i - 1], s);
310 }
311
312 if (archetype == NULL) {
313#if defined(_WIN32)
314 static Once once;
315 static ident handle;
316 do_once(&once, { handle = dlopen(NULL, RTLD_LAZY); });
317 archetype = handle ? dlsym(handle, s) : NULL;
318#else
319 archetype = dlsym(RTLD_DEFAULT, s);
320#endif
321 }
322
323 if (archetype) {
324 clazz = archetype();
325 }
326
327 free(s);
328 return clazz;
329 }
330 }
331
332 return NULL;
333}
const char * name
The Class name (required).
Definition Class.h:84

◆ 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 335 of file Class.c.

335 {
336
337 if (obj) {
338 Object *object = cast(Object, obj);
339
340 assert(object);
341
342 if (__sync_add_and_fetch(&object->referenceCount, -1) == 0) {
343 $(object, dealloc);
344 }
345 }
346
347 return NULL;
348}
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.

Definition at line 251 of file Class.c.

251 {
252
253 assert(handle);
254
255 for (size_t i = 0; i < _classImageCount; i++) {
256 if (_classImages[i] == handle) {
257
258 memmove(_classImages + i, _classImages + i + 1,
259 (_classImageCount - i - 1) * sizeof(ident));
260
262 break;
263 }
264 }
265
266 const ident image = imageForHandle(handle);
267 if (image == NULL) {
268 return;
269 }
270
271 Class **link = &_classes;
272 while (*link) {
273 Class *clazz = *link;
274
275 if (clazz->image == image) {
276 *link = clazz->next;
277 clazz->next = NULL;
278 } else {
279 link = &clazz->next;
280 }
281 }
282}
static ident imageForHandle(ident handle)
Definition Class.c:215

◆ 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 350 of file Class.c.

350 {
351
352 Object *object = cast(Object, obj);
353
354 assert(object);
355
356 __sync_add_and_fetch(&object->referenceCount, 1);
357
358 return obj;
359}

Variable Documentation

◆ _pageSize

OBJECTIVELY_EXPORT size_t _pageSize

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

Definition at line 200 of file Class.h.