CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
Object System

Modules

 Classes
 
 Interfaces
 
 Class Implementation
 

Detailed Description

The CX object system brings object-oriented programming to C with interfaces, inheritance, reference counting, and runtime type information. It uses a custom Interface Definition Language (IDL) to minimize boilerplate while maintaining type safety and C compatibility.

Key Features

Working with .cxh Files

Instead of writing verbose C code, define interfaces and classes in .cxh files:

// myclass.cxh
interface Printable {
void print();
}
class Document implements Printable {
string title;
string content;
factory create(string title);
}

The build system (via add_cxautogen() in CMakeLists.txt) generates:

Never edit generated .h files directly - modify the .cxh source instead.

Advanced .cxh Features

Abstract classes cannot be instantiated directly:

abstract class Iterator {
bool valid();
bool next();
}

Mixins are abstract classes that can be included in multiple classes using uses:

mixin class Logging {
void log(string message);
}
class Document uses Logging {
// Document gets all Logging methods and members
}

Method qualifiers control how methods are bound and called:

Member and method annotations in [brackets]:

Example:

[methodprefix doc]
class Document {
[noinit] string title; // Factory must initialize manually
override print; // Just the method name, prototype inherited
unbound void validate(); // Not polymorphic, always calls Document's version
standalone Document *fromFile(string path); // No implicit self parameter
}

Note: Method implementations receive the object instance as the self parameter (not this).

Using Objects

Objects use reference counting for memory management:

// Create object using factory method (usually 'create', but can vary)
Document *doc = documentCreate(_S"My Document");
// Call methods using wrapper macros (most common)
documentPrint(doc);
// Release when done (decrements refcount, destroys at 0)
objRelease(&doc); // doc is set to NULL
#define objRelease(pinst)
Definition objclass.h:223
#define _S
Creates a static ASCII string from a string literal.
Definition strbase.h:392

If declaring an object pointer without immediately creating it, initialize to NULL:

Document *doc = 0; // NULL until assigned
if (condition) {
doc = documentCreate(_S"Title");
}

Object Lifecycle: Initializers and Destructors

Classes can define optional init and destroy callbacks for custom initialization and cleanup. Declare them in the .cxh file and implement in the .c file:

// document.cxh
class Document {
string title;
string content;
init(); // Optional: validate/initialize after construction
destroy(); // Optional: clean up resources before deallocation
}
// document.c - implement the callbacks
bool Document_init(Document *self) {
// Called after factory sets up members, before returning to caller
// Return false to abort construction
return validateDocument(self);
}
void Document_destroy(Document *self) {
// Called just before memory deallocation
// Clean up owned resources
strDestroy(&self->title);
strDestroy(&self->content);
}
void strDestroy(strhandle ps)

Initialization order: Parent class init() → Child class init()

Destruction order: Child class destroy() → Parent class destroy()

Factory functions must call objInstInit() after setting up instance data. If init returns false, the factory should clean up and return NULL. The framework automatically handles calling init/destroy for the entire class hierarchy.

Reference Counting Rules

Interfaces

Classes can implement multiple interfaces. While you can call methods directly using class wrapper macros (e.g., documentPrint()), you can also query for interfaces when working with interface types or need to access interface methods explicitly.

Classes that declare their own methods implicitly implement a "class interface" containing those methods, accessible through the class's interface table.

// Most common: use class wrapper macros
Document *doc = documentCreate(_S"Title");
documentPrint(doc); // Calls Printable->print implementation
// Query interface when you only have an interface pointer
// Best practice: check for NULL to ensure object implements the interface
ObjInst *obj = getSomeObject();
Printable *printIf = objInstIf(obj, Printable);
if (printIf) {
printIf->print(obj);
}
// Get interface from class (without instance)
Printable *classIf = objClassIf(Document, Printable);
#define objInstIf(inst, ifname)
Definition objclass.h:259
#define objClassIf(clsname, ifname)
Definition objclass.h:237

Class Hierarchy

Classes support single inheritance. Child classes inherit and can override parent methods. Method wrappers use the full class name in lowercase:

class SpecialDocument extends Document {
int priority;
factory create(string title, int priority);
}
// Usage - class name is fully lowercase in wrapper
SpecialDocument *special = specialdocumentCreate(_S"Important", 10);
// Optionally override the prefix with methodprefix annotation:
[methodprefix sdoc]
class SpecialDocument extends Document {
// ...
}
// Now methods use: sdocCreate()

Dynamic Casting

Use objDynCast() to safely cast within the class hierarchy:

ObjInst *baseObj = getObject();
Document *doc = objDynCast(Document, baseObj);
if (doc) {
// Safe to use as Document
}
#define objDynCast(clsname, inst)
Definition objclass.h:350

Weak References

Weak references allow observing objects without ownership:

Document *doc = documentCreate(_S"Title");
Weak(Document) *weak = objGetWeak(Document, doc);
// Later, try to acquire (returns NULL if object was destroyed)
Document *doc2 = objAcquireFromWeak(Document, weak);
if (doc2) {
// Object still exists
objRelease(&doc2);
}
objRelease(&doc);
#define objGetWeak(clsname, inst)
Definition objclass.h:273
#define Weak(clsname)
Definition objclass.h:128
#define objAcquireFromWeak(clsname, ref)
Definition objclass.h:328
#define objDestroyWeak(pref)
Definition objclass.h:303

Naming Conventions

Interfaces:

Classes:

Interface Implementations:

PascalCase is preferred for classes and interfaces, but not enforced.