Ultra-lightweight object oriented framework for GNU C.
Foundation-inspired core library.
Zlib license.
Objectively is a cross-platform object oriented framework for the C programming language. Unlike GObject, Objectively is not a platform on which to build OO languages. Rather, Objectively provides rich OO semantics to enable object oriented programming directly in C. It is based on C99, but utilizes several GNU extensions, and therefore requires gcc
or clang
. While others may work, the following targets are actively supported:
- Apple OS X with clang
- Apple OS X with gcc
- GNU / Linux with clang
- GNU / Linux with gcc
- MinGW-w64 cross compile
- MinGW-w64 Windows native
- Visual Studio 2015 or later with clang
- Single inheritance through starts-with structure composition
- Class and instance methods with strongly typed interfaces
- Automatic class loading and lifecycle management
- Automatic memory management with reference counting
- Unicode and multibyte character set String support
- Object primitives for Boole, Date, Null, Number, String
- Mutable and immutable collections variants such as Array and MutableDictionary
- JSON parsing, marshaling and introspection with JSONSerialization and JSONPath
- Low-level concurrency constructs such as Lock, Condition, and Thread
- High-level concurrency with Operation and OperationQueue
- Resource loading via Internet protocols with URLSession and URLSessionTask
- Do the Autotools dance.
autoreconf -i; ./configure; make; sudo make install
- Include the main header file in your source.
#include <Objectively.h>
- Compile and link with Objectively.
gcc `pkg-config --cflags --libs Objectively` -o myprogram *.c
Types in Objectively are comprised of 3 components:
- The instance
struct
, beginning with the parent type, the type interface and then any additional instance variables.
/**
* @brief The Hello type.
*/
struct Hello {
/**
* @brief The parent.
*/
Object object;
/**
* @brief The typed interface.
*/
HelloInterface *interface;
/**
* @brief The greeting.
*/
const char *greeting;
};
- The interface
struct
, beginning with the parent interface, followed by any Class or instance methods.
/**
* @brief The Hello interface.
*/
struct HelloInterface {
/**
* @brief The parent.
*/
ObjectInterface objectInterface;
/**
* @static
* @fn Hello *Hello::helloWithGreeting(const char *greeting)
* @brief A factory method for instantiating Hello.
* @param greeting The greeting.
* @return A new Hello with the given `greeting`.
* @memberof Hello
*/
Hello *(*helloWithGreeting)(const char *greeting);
/**
* @fn Hello *Hello::initWithGreeting(Hello *self, const char *greeting)
* @brief Initializes this Hello with the given `greeting`.
* @param self The Hello.
* @param greeting The greeting.
* @return The initialized Hello, or `NULL` on error.
* @memberof Hello
*/
Hello *(*initWithGreeting)(Hello *self, const char *greeting);
/**
* @fn void Hello::sayHello(const Hello *self)
* @brief Prints this Hello's greeting to the console.
* @param self The Hello.
* @memberof Hello
*/
void (*sayHello)(const Hello *self);
};
- The class archetype, serving to tie 1) and 2) together.
/**
* @fn Class *Hello::_Hello(void)
* @brief The Hello archetype.
* @return The Hello Class.
* @memberof Hello
*/
OBJECTIVELY_EXPORT Class *_Hello(void);
To implement a type, implement its instance and Class methods and Class initializer:
#include <stdio.h>
#include <Objectively.h>
#define _Class _Hello
/**
* @fn Hello *HelloInterface::helloWithGreeting(const char *greeting)
* @memberof Hello
*/
static Hello *helloWithGreeting(const char *greeting) {
return $(alloc(Hello), initWithGreeting, greeting);
}
/**
* @fn Hello *HelloInterface::initWithGreeting(Hello *self, const char *greeting)
* @memberof Hello
*/
static Hello *initWithGreeting(Hello *self, const char *greeting) {
self = (Hello *) super(Object, self, init);
if (self) {
self->greeting = greeting ? : "Hello World!";
}
return self;
}
/**
* @fn void HelloInterface::sayHello(const Hello *self)
* @memberof Hello
*/
static void sayHello(const Hello *self) {
printf("%s\n", self->greeting);
}
#pragma mark - Class lifecycle
/**
* @see Class::initialize(Class *)
*/
static void initialize(Class *clazz) {
((HelloInterface *) clazz->def->interface)->helloWithGreeting = helloWithGreeting;
((HelloInterface *) clazz->def->interface)->initWithGreeting = initWithGreeting;
((HelloInterface *) clazz->def->interface)->sayHello = sayHello;
}
/**
* @fn Class *Hello::_Hello(void)
* @memberof Hello
*/
Class *_Hello(void) {
static Class clazz;
static Once once;
do_once(&once, {
clazz.name = "Hello";
clazz.superclass = _Object();
clazz.instanceSize = sizeof(Hello);
clazz.interfaceOffset = offsetof(Hello, interface);
clazz.interfaceSize = sizeof(HelloInterface);
clazz.initialize = initialize;
});
return &clazz;
};
#undef _Class
Hello *hello = $$(Hello, helloWithGreeting, NULL);
$(hello, sayHello);
release(hello);
See Hello.c for the full source to this example.
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.
To invoke an instance method, use the $
macro.
$(condition, waitUntilDate, date);
To invoke a Class method, use the $$
macro.
Dictionary *dict = $$(JSONSerialization, objectWithData, data);
To override a method, overwrite the function pointer from within your Class' initialize
method.
((ObjectInterface *) clazz->def->interface)->dealloc = dealloc;
((ObjectInterface *) clazz->def->interface)->isEqual = isEqual;
To invoke a supertype's method implementation, use the super
macro.
super(Object, self, dealloc);
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
.
A shared instance or singleton pattern can be achieved through Class methods and release-on-destroy.
static URLSession *_sharedInstance;
/**
* @fn URLSession *URLSessionInterface::sharedInstance(void)
* @memberof URLSession
*/
static *URLSession sharedInstance(void) {
static Once once;
do_once(&once, {
_sharedInstance = $(alloc(URLSession), init);
});
return _sharedInstance;
}
/**
* @see Class::destroy(Class *)
*/
static void destroy(Class *clazz) {
release(_sharedInstance);
}
// ...
URLSession *session = $$(URLSession, sharedInstance);
Remember to wire up the desctructor in your Class' initialization block. See Once.h for details on do_once
.
- The Hello example above can be seen here.
- The unit tests provide examples for using every Objectively class.
- ObjectivelyMVC is a cross-platform user interface and interaction framework for SDL2 and OpenGL built on Objectively.
Objectively provides code templates for Xcode and Eclipse CDT that greatly cut down on the boilerplate required to declare and implement a type. These are highly recommended, as they will save you time and reduce errors in type declaration.
Install the Xcode 8.x code templates by simply building the Xcode project.
Install the Eclipse CDT templates through the Eclipse CDT preferences:
The API documentation can be browsed online or generated with Doxygen by running make html
.