Objectively
Object oriented framework for C.
Loading...
Searching...
No Matches
Objectively User Guide

A guide to declaring, implementing, and using your own types with Objectively.

Declaring a Type

Every Objectively type consists of three components:

1. The instance struct — starts with the parent type, then the zero-length interface member, then instance variables:

struct Hello {
Object object; // parent (starts-with = single inheritance)
HelloInterface *interface[0]; // carries a type, occupies no storage
const char *greeting;
};
Object is the root Class of The Objectively Class hierarchy.
Definition Object.h:46

The interface member exists only so that $ can read its type with typeof; it holds nothing. The interface itself lives on the Class and is reached through Object::clazz. The member MUST directly follow the parent, where its pointer alignment costs no padding.

2. The interface struct — starts with the parent interface, then method function pointers:

struct HelloInterface {
ObjectInterface objectInterface;
Hello *(*helloWithGreeting)(const char *greeting);
Hello *(*initWithGreeting)(Hello *self, const char *greeting);
void (*sayHello)(const Hello *self);
};

3. The Class archetype — a single exported function that returns the Class:

OBJECTIVELY_EXPORT Class *_Hello(void);
#define OBJECTIVELY_EXPORT
Definition Types.h:36
The runtime representation of a Class.
Definition Class.h:90

Implementing a Type

#include <stdio.h>
#include <Objectively.h>
#define _Class _Hello
static Hello *helloWithGreeting(const char *greeting) {
return $(alloc(Hello), initWithGreeting, greeting);
}
static Hello *initWithGreeting(Hello *self, const char *greeting) {
self = (Hello *) super(Object, self, init);
if (self) {
self->greeting = greeting ?: "Hello World!";
}
return self;
}
static void sayHello(const Hello *self) {
printf("%s\n", self->greeting);
}
static void initialize(Class *clazz) {
((HelloInterface *) clazz->interface)->helloWithGreeting = helloWithGreeting;
((HelloInterface *) clazz->interface)->initWithGreeting = initWithGreeting;
((HelloInterface *) clazz->interface)->sayHello = sayHello;
}
Class *_Hello(void) {
static Class *clazz;
static Once once;
do_once(&once, {
clazz = _initialize(&(const ClassDef) {
.name = "Hello",
.superclass = _Object(),
.instanceSize = sizeof(Hello),
.interfaceSize = sizeof(HelloInterface),
.initialize = initialize,
});
});
return clazz;
}
#undef _Class
static void initialize(Class *clazz)
Definition Array.c:710
static Array * init(Array *self)
Definition Array.c:420
Class * _initialize(const ClassDef *def)
Initializes the given Class.
Definition Class.c:151
#define alloc(type)
Allocate and initialize and instance of type.
Definition Class.h:226
#define super(type, obj, method,...)
Class * _Object(void)
Definition Object.c:136
Objectively: Ultra-lightweight object oriented framework for GNU C.
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
ClassDefs are passed to _initialize via an archetype to initialize a Class.
Definition Class.h:41
ident interface
The interface of the Class.
Definition Class.h:100

Using a Type

Using your custom type is trivial. Instantiate it like you would any Objectively type.

Hello *hello = $(alloc(Hello), initWithGreeting, "Hello, World!");
$(hello, sayHello);
release(hello);
ident release(ident obj)
Atomically decrement the given Object's reference count. If the resulting reference count is 0,...
Definition Class.c:356

See Hello.h and Hello.c for the full source.

Initialization

There is no explicit setup or teardown with Objectively. To instantiate a type, simply call alloc from anywhere in your program. The first time a type is instantiated, its Class initializer, initialize, is called. Use initialize to setup your interface, override methods, or initialize a library your class wraps. When your application terminates, an optional Class destructor, destroy, is also called.

Invoking an instance method

To invoke an instance method, use the $ macro.

$(condition, waitUntilDate, date);
static bool waitUntilDate(Condition *self, const Date *date)
Definition Condition.c:106
static Date * date(void)
Definition Date.c:98

Invoking a Class method

To invoke a Class method, use the $$ macro.

static Dictionary * dictionary(void)
Definition Dictionary.c:242
Key-value stores.
Definition Dictionary.h:60

Overriding a method

To override a method, overwrite the function pointer from within your Class' initialize method.

((ObjectInterface *) clazz->interface)->dealloc = dealloc;
((ObjectInterface *) clazz->interface)->isEqual = isEqual;
static bool isEqual(const Object *self, const Object *other)
Definition Array.c:145
static void dealloc(Object *self)
Definition Array.c:99

Calling super

To invoke a supertype's method implementation, use the super macro.

Re-classing an instance

An instance carries no interface pointer of its own: $, cast, and isKindOfClass all resolve through Object::clazz. Reassigning clazz after allocation therefore changes an instance's behavior and its type identity together, immediately. This permits one-off Classes minted purely for behavior, with no struct, header, or archetype of their own:

static bool resizeHandle_captureEvent(Control *self, const SDL_Event *event) {
// ...
return true;
}
static void resizeHandle_initialize(Class *clazz) {
((ControlInterface *) clazz->interface)->captureEvent = resizeHandle_captureEvent;
}
Class *proxy = _initialize(&(const ClassDef) {
.name = "Control(resizeHandle)",
.superclass = classof(resizeHandle),
.instanceSize = classof(resizeHandle)->def.instanceSize,
.interfaceSize = classof(resizeHandle)->def.interfaceSize,
.initialize = resizeHandle_initialize,
});
((Object *) resizeHandle)->clazz = proxy;
#define classof(obj)
Resolve the Class of an Object instance.
Definition Class.h:238

The new Class inherits every method of the original, since _initialize copies the superclass interface before calling initialize. The instance struct MUST NOT change: a Class minted this way SHOULD reuse the superclass's instanceSize exactly, because the memory has already been allocated.

Managing memory

Objectively uses reference counting to govern object retention. Newly instantiated Objects have a reference count of 1. To retain a strong reference to an Object, call retain(obj). To relinquish it, call release(obj). Once an Object's reference count reaches 0, it is deallocated. Remember to balance every retain with a release.

Shared instances

A shared instance or singleton pattern can be achieved through Class methods and release-on-destroy.

static *URLSession sharedInstance(void) {
static Once once;
do_once(&once, {
});
}
static void destroy(Class *clazz) {
}
// ...
static void destroy(Class *clazz)
Definition Boole.c:102
static Log * _sharedInstance
Definition Log.c:206
static Log * sharedInstance(void)
Definition Log.c:212
A management context for loading resources via URLs.
Definition URLSession.h:59

Remember to wire up the desctructor in your Class' initialization block. See Once.h for details on do_once.

Code Templates

Objectively ships code templates that cut down the boilerplate of declaring and implementing a type — highly recommended. Install the Xcode template by building the Xcode project, or import the Eclipse CDT templates through the Eclipse CDT preferences.