Interface99
Full-featured interfaces for C99
Install / Use
/learn @hirrolot/Interface99README
Full-featured interfaces inspired by Rust and Golang. Multiple inheritance, superinterfaces, and default implementations supported. No external tools required, pure C99.
<table> <tr> <td><div align="center"><b>Shape</b></div></td> </tr> <tr> <td> <div align="left">#include <interface99.h>
#include <stdio.h>
#define Shape_IFACE \
vfunc( int, perim, const VSelf) \
vfunc(void, scale, VSelf, int factor)
interface(Shape);
</div>
</td>
</tr>
</table>
<table>
<tr>
<td><div align="center"><b>Rectangle</b></div></td>
<td><div align="center"><b>Triangle</b></div></td>
</tr>
<tr>
<td>
<div align="left">
typedef struct {
int a, b;
} Rectangle;
int Rectangle_perim(const VSelf) {
VSELF(const Rectangle);
return (self->a + self->b) * 2;
}
void Rectangle_scale(VSelf, int factor) {
VSELF(Rectangle);
self->a *= factor;
self->b *= factor;
}
impl(Shape, Rectangle);
</div>
</td>
<td>
<div align="left">
typedef struct {
int a, b, c;
} Triangle;
int Triangle_perim(const VSelf) {
VSELF(const Triangle);
return self->a + self->b + self->c;
}
void Triangle_scale(VSelf, int factor) {
VSELF(Triangle);
self->a *= factor;
self->b *= factor;
self->c *= factor;
}
impl(Shape, Triangle);
</div>
</td>
</tr>
</table>
<table>
<tr>
<td><div align="center"><b>Test</b></div></td>
</tr>
<tr>
<td>
<div align="left">
void test(Shape shape) {
printf("perim = %d\n", VCALL(shape, perim));
VCALL(shape, scale, 5);
printf("perim = %d\n", VCALL(shape, perim));
}
int main(void) {
Shape r = DYN_LIT(Rectangle, Shape, {5, 7});
Shape t = DYN_LIT(Triangle, Shape, {10, 20, 30});
test(r);
test(t);
}
</div>
</td>
</tr>
</table>
</div>
(Based on examples/shape.c.)
perim = 24
perim = 120
perim = 60
perim = 300
</details>
Highlights
-
Minimum boilerplate. Forget about maintaining virtual tables -- just write
impl(Shape, Rectangle)and Interface99 will do it for you! -
Portable. Everything you need is a standard-conforming C99 compiler; neither the standard library, nor compiler/platform-specific functionality or VLA are required.
-
Predictable. Interface99 comes with formal code generation semantics, meaning that the generated data layout is guaranteed to always be the same.
-
Comprehensible errors. Interface99 is resilient to bad code.
-
Battle-tested. Interface99 is used at OpenIPC to develop real-time streaming software for IP cameras; this includes an RTSP 1.0 implementation along with ~50k lines of private code.
Features
| Feature | Status | Description |
|---------|--------|-------------|
| Multiple interface inheritance | ✅ | A type can inherit multiple interfaces at the same time. |
| Superinterfaces | ✅ | One interface can require a set of other interfaces to be implemented as well. |
| Marker interfaces | ✅ | An interface with no functions. |
| Single/Dynamic dispatch | ✅ | Determine a function to be called at runtime based on self. |
| Multiple dispatch | ❌ | Determine a function to be called at runtime based on multiple arguments. Likely to never going to be implemented. |
| Dynamic objects of multiple interfaces | ✅ | Given interfaces Foo and Bar, you can construct an object of both interfaces, FooBar obj. |
| Default implementations | ✅ | Some interface functions may be given default implementations. A default function can call other functions and vice versa. |
| Data and implementation separation | ✅ | New interfaces can be implemented for existing types. |
Installation
Interface99 consists of one header file interface99.h and one dependency Metalang99. To use it in your project, you need to:
- Add
interface99andmetalang99/includeto your include directories. - Specify
-ftrack-macro-expansion=0(GCC) or-fmacro-backtrace-limit=1(Clang) to avoid useless macro expansion errors.
If you use CMake, the recommended way is FetchContent:
include(FetchContent)
FetchContent_Declare(
interface99
URL https://github.com/hirrolot/interface99/archive/refs/tags/vx.y.z.tar.gz # vx.y.z
)
FetchContent_MakeAvailable(interface99)
target_link_libraries(MyProject interface99)
# Disable full macro expansion backtraces for Metalang99.
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
target_compile_options(MyProject PRIVATE -fmacro-backtrace-limit=1)
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(MyProject PRIVATE -ftrack-macro-expansion=0)
endif()
(By default, interface99/CMakeLists.txt downloads Metalang99 v1.13.5 from the GitHub releases; if you want to override this behaviour, you can do so by invoking FetchContent_Declare earlier.)
Optionally, you can precompile headers in your project that rely on Interface99. This will decrease compilation time, because the headers will not be compiled each time they are included.
Happy hacking!
Tutorial
This section is based on a collection of well-documented examples, each of which demonstrates one specific aspect of Interface99.
Basic usage
- Interface definition.
Syntax: interface(Shape);
An interface definition expands to a virtual table structure and a so-called interface object type. In the case of examples/shape.c:
// interface(Shape);
typedef struct ShapeVTable ShapeVTable;
typedef struct Shape Shape;
struct ShapeVTable {
int (*perim)(const VSelf);
void (*scale)(VSelf, int factor);
};
struct Shape {
void *self;
const ShapeVTable *vptr;
};
Here, Shape.self is the pointer to an object whose type implements Shape, and Shape.vptr points to a corresponding virtual table instance. Inside ShapeVTable, you can observe the mysterious VSelf bits -- they expand to parameters of type void * restrict (with extra const for perim); when calling these methods, Interface99 will substitute Shape.self for these parameters.
Usually, interface definitions go in *.h files.
- Implementation definition.
| Linkage | Syntax |
|---------|--------|
| Internal | impl(Shape, Rectangle); |
| External | implExtern(Shape, Rectangle); |
An implementation definition expands to nothing but a virtual table instance of a particular implementer. In the case of examples/shape.c:
// impl(Shape, Rectangle);
static const ShapeVTable Rectangle_Shape_impl = {
.perim = Rectangle_perim,
.scale = Rectangle_scale,
};
(If you were using implExtern, this definition would be extern likewise.)
Note that inside function implementations, we use VSELF, which simply casts the parameter introduced by VSelf to a user-defined type (const Rectangle or Rectangle in our case):
int Rectangle_perim(const VSelf) {
VSELF(const Rectangle);
return (self->a + self->b) * 2;
}
void Rectangle_scale(VSelf, int factor) {
VSELF(Rectangle);
self->a *= factor;
self->b *= factor;
}
- Dynamic dispatch.
Once an interface and its implementations are both generated, it is time to instantiate an interface object and invoke some functions upon it.
First of all, to instantiate Shape, use the DYN_LIT macro:
Shape r = DYN_LIT(Rectangle, Shape, {5, 7});
test(r);
Here, DYN_LIT(Rectangle, Shape, {5, 7}) creates Shape by assigning Shape.self to &(Rectangle){5, 7} and Shape.vptr to the aforementioned &Rectangle_Shape_impl. Eventually, you can accept Shape as a function parameter and perform dynamic dispatch through the VCALL macro:
void test(Shape shape) {
printf("perim = %d\n", VCALL(shape, perim));
VCALL(shape, scale, 5);
printf("perim = %d\n", VCALL(shape, perim));
}
Finally, just a few brief notes:
- Besides
VCALL, you also haveVCALL_OBJ,VCALL_SUPER, andVCALL_SUPER_OBJ. They all serve a different purpose; for more information, please refer to their documentation. - In practice,
DYNis used more often thanDYN_LIT; it just accepts an ordinary pointer instead of an initialiser list, which means that you canmallocit beforehand. - If your virtual function does not accept
self, you can invoke it asobj.vptr->foo(...). - If you want to call an interface function on some concrete type, just write
VTABLE(T, Iface).foo(...).
Congratulations, this is all you need to know to write most of the stuff!
Superinterfaces
Interface99 has the feature called superinterfaces, or interface requirements. examples/airplane.c demonstrates how to extend interfaces with new functionality:
#define Ve
Related Skills
node-connect
335.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
335.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
82.5kCommit, push, and open a PR
