|
CX Framework
Cross-platform C utility framework
|
Modules | |
| Classes | |
| Interfaces | |
| Class Implementation | |
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.
.cxh files generate boilerplate C code automaticallyInstead of writing verbose C code, define interfaces and classes in .cxh files:
The build system (via add_cxautogen() in CMakeLists.txt) generates:
myclass.h - Public API declarationsmyclass.c - Implementation stubs to fill inmyclass.auto.inc - Internal boilerplateNever edit generated .h files directly - modify the .cxh source instead.
Abstract classes cannot be instantiated directly:
Mixins are abstract classes that can be included in multiple classes using uses:
Method qualifiers control how methods are bound and called:
override - Mark method as overriding parent implementation (just override methodname;, no need to repeat parameters)unbound - Method does not dynamically resolve to child class implementations (analogous to Java's final or C++'s non-virtual methods)standalone - Function not bound to class and does NOT take self parameter (like static)Member and method annotations in [brackets]:
[methodprefix xyz] - Override default method prefix (class name in lowercase)[noinit] - Member is not initialized by generated code, factory must handle it[sal ...] - SAL annotations for static analysis[opt] - Function can return NULL/optional result[out], [inout] - Parameter direction hintsExample:
Note: Method implementations receive the object instance as the self parameter (not this).
Objects use reference counting for memory management:
If declaring an object pointer without immediately creating it, initialize to NULL:
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:
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.
create(), but can have other names) return objects with refcount = 1documentCreate() wraps the underlying factoryobjAcquire(obj) increments the reference countobjRelease(&obj) decrements and destroys when count reaches 0xaFree() directly on objectsClasses 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.
Classes support single inheritance. Child classes inherit and can override parent methods. Method wrappers use the full class name in lowercase:
Use objDynCast() to safely cast within the class hierarchy:
Weak references allow observing objects without ownership:
Interfaces:
MyInterface - Interface type nameMyInterface_tmpl - Interface template objectClasses:
MyClass - Class instance type nameMyClass_clsinfo - Class metadata structuremyClassCreate() - Typical factory wrapper (from factory create(); in .cxh)create as defined in .cxh fileInterface Implementations:
MyClass_MyInterface - Function table for this class's implementationPascalCase is preferred for classes and interfaces, but not enforced.