Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hirrolot/interface99
Full-featured interfaces for C99
https://github.com/hirrolot/interface99
c99 dynamic-dispatch generic-programming macros metalang99 metaprogramming object-oriented oop polymorphism type-system typeclasses
Last synced: 29 days ago
JSON representation
Full-featured interfaces for C99
- Host: GitHub
- URL: https://github.com/hirrolot/interface99
- Owner: Hirrolot
- License: mit
- Created: 2021-05-26T12:47:27.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2024-04-06T08:42:42.000Z (8 months ago)
- Last Synced: 2024-05-21T03:24:59.396Z (7 months ago)
- Topics: c99, dynamic-dispatch, generic-programming, macros, metalang99, metaprogramming, object-oriented, oop, polymorphism, type-system, typeclasses
- Language: C
- Homepage:
- Size: 412 KB
- Stars: 277
- Watchers: 9
- Forks: 13
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- awesome-c-preprocessor - `hirrolot/interface99` - - Zero-boilerplate interfaces for C99. (C language extensions)
README
Interface99
Full-featured interfaces inspired by Rust and Golang. Multiple inheritance, superinterfaces, and default implementations supported. No external tools required, pure C99.
Shape```c
#include#include
#define Shape_IFACE \
vfunc( int, perim, const VSelf) \
vfunc(void, scale, VSelf, int factor)interface(Shape);
```RectangleTriangle```c
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);
``````c
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);
```Test```c
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);
}
```(Based on [`examples/shape.c`](examples/shape.c).)
Output
```
perim = 24
perim = 120
perim = 60
perim = 300
```## 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.
[code generation semantics]: #semantics
[resilient to bad code]: #q-what-about-compile-time-errors
[OpenIPC]: https://openipc.org/
[RTSP 1.0 implementation]: https://github.com/OpenIPC/smolrtsp/## Features
| Feature | Status | Description |
|---------|--------|-------------|
| [Multiple interface inheritance](examples/read_write.c) | ✅ | A type can inherit multiple interfaces at the same time. |
| [Superinterfaces](examples/airplane.c) | ✅ | One interface can require a set of other interfaces to be implemented as well. |
| [Marker interfaces](examples/marker.c) | ✅ | An interface with no functions. |
| [Single/Dynamic dispatch](examples/shape.c) | ✅ | 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](examples/read_write_both.c) | ✅ | Given interfaces `Foo` and `Bar`, you can construct an object of both interfaces, `FooBar obj`. |
| [Default implementations](examples/default_impl.c) | ✅ | 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:
[Metalang99]: https://github.com/hirrolot/metalang99
1. Add `interface99` and `metalang99/include` to your include directories.
2. Specify [`-ftrack-macro-expansion=0`] (GCC) or [`-fmacro-backtrace-limit=1`] (Clang) to avoid useless macro expansion errors.[`-ftrack-macro-expansion=0`]: https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html
[`-fmacro-backtrace-limit=1`]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fmacro-backtrace-limitIf you use CMake, the recommended way is [`FetchContent`]:
[`FetchContent`]: https://cmake.org/cmake/help/latest/module/FetchContent.html
```cmake
include(FetchContent)FetchContent_Declare(
interface99
URL https://github.com/hirrolot/interface99/archive/refs/tags/v1.2.3.tar.gz # v1.2.3
)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.2](https://github.com/hirrolot/metalang99/releases/tag/v1.13.2) from the GitHub releases; if you want to override this behaviour, you can do so by invoking [`FetchContent_Declare`] earlier.)
[`FetchContent_Declare`]: https://cmake.org/cmake/help/latest/module/FetchContent.html#command:fetchcontent_declare
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.
[precompile headers]: https://en.wikipedia.org/wiki/Precompiled_header
Happy hacking!
## Tutorial
This section is based on a collection of well-documented [examples](examples/), each of which demonstrates one specific aspect of Interface99.
### Basic usage
1. **Interface definition.**
Syntax: [`interface(Shape);`](#interface)
An interface definition expands to a virtual table structure and a so-called _interface object type_. In the case of [`examples/shape.c`](examples/shape.c):
```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`](#vselfvself) 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.
2. **Implementation definition.**
| Linkage | Syntax |
|---------|--------|
| Internal | [`impl(Shape, Rectangle);`](#impl) |
| External | [`implExtern(Shape, Rectangle);`](#implExtern) |An implementation definition expands to nothing but a virtual table instance of a particular implementer. In the case of `examples/shape.c`:
```c
// impl(Shape, Rectangle);
static const ShapeVTable Rectangle_Shape_impl = {
.perim = Rectangle_perim,
.scale = Rectangle_scale,
};
```(If you were using [`implExtern`](#implExtern), this definition would be `extern` likewise.)
Note that inside function implementations, we use [`VSELF`](#vselfvself), which simply casts the parameter introduced by `VSelf` to a user-defined type (`const Rectangle` or `Rectangle` in our case):
```c
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;
}
```3. **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`](#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`](#vcall_) macro:
```c
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 have `VCALL_OBJ`, `VCALL_SUPER`, and `VCALL_SUPER_OBJ`. They all serve a different purpose; for more information, please refer to [their documentation](#vcall_).
- In practice, [`DYN`](#DYN) is used more often than [`DYN_LIT`](#DYN_LIT); it just accepts an ordinary pointer instead of an initialiser list, which means that you can `malloc` it beforehand.
- If your virtual function does not accept `self`, you can invoke it as `obj.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`](examples/airplane.c) demonstrates how to extend interfaces with new functionality:
```c
#define Vehicle_IFACE \
vfunc(void, move_forward, VSelf, int distance) \
vfunc(void, move_back, VSelf, int distance)interface(Vehicle);
#define Airplane_IFACE \
vfunc(void, move_up, VSelf, int distance) \
vfunc(void, move_down, VSelf, int distance)#define Airplane_EXTENDS (Vehicle)
interface(Airplane);
```(Note that `#define Airplane_EXTENDS` must appear prior to `interface(Airplane);`.)
Here, `Airplane` extends `Vehicle` with the new functions `move_up` and `move_down`. Everywhere you have `Airplane`, you can also operate `Vehicle`:
```c
Airplane my_airplane = DYN_LIT(MyAirplane, Airplane, {.x = 0, .y = 0});VCALL_SUPER(my_airplane, Vehicle, move_forward, 10);
VCALL_SUPER(my_airplane, Vehicle, move_back, 3);
```Internally, Interface99 embeds superinterfaces' virtual tables into those of subinterfaces, thereby forming a _virtual table hierarchy_. For example, you can specify `Repairable` and `Armoured` along with `Vehicle`, and they all will be included into `AirplaneVTable` like so:
```c
// #define Airplane_EXTENDS (Vehicle, Repairable, Armoured)
typedef struct AirplaneVTable {
void (*move_up)(VSelf, int distance);
void (*move_down)(VSelf, int distance);
const VehicleVTable *Vehicle;
const RepairableVTable *Repairable;
const ArmouredVTable *Armoured;
} AirplaneVTable;
```### Default implementations
Sometimes we wish to define default behaviour for several implementers; this is supported by _default implementations_.
Take a look at [`examples/default_impl.c`](examples/default_impl.c). In this example, we define the interface `Droid`:
```c
#define Droid_IFACE \
vfunc(const char *, name, void) \
vfuncDefault(void, turn_on, Droid droid)interface(Droid);
```The macro `vfuncDefault` tells Interface99 to use the default implementation for `turn_on` automatically. But where is it located? Here:
```c
void Droid_turn_on(Droid droid) {
printf("Turning on %s...\n", droid.vptr->name());
}
```As you can see, default implementations follow a strict naming convention, `_` , which provides Interface99 with sufficient information to generate a virtual table. Additionally, as a developer, you can also rely on this convention and call a default function of a third-party interface. For `C_3PO`, we use the default implementation of `turn_on`, and the resulting virtual table would look like this:
```c
static const DroidVTable C_3PO_Droid_impl = {
.name = C_3PO_name,
.turn_on = Droid_turn_on,
};
```But for `R2_D2`, we use a custom implementation `R2_D2_turn_on`:
```c
void R2_D2_turn_on(Droid droid) {
Droid_turn_on(droid);
puts("Waaaaoow!");
}#define R2_D2_turn_on_CUSTOM ()
impl(Droid, R2_D2);
```(`R2_D2_turn_on_CUSTOM` tells Interface99 to use the custom implementation instead of the default one; this is because it is impossible to detect at compile-time whether a specific function is defined or not.)
And the virtual table would be:
```c
static const DroidVTable R2_D2_Droid_impl = {
.name = R2_D2_name,
.turn_on = R2_D2_turn_on,
};
```Please, note that you have to specify `()` for the `*_CUSTOM` attribute; do not leave it empty.
## Syntax and semantics
Having a well-defined semantics of the macros, you can write an FFI which is quite common in C.
### EBNF syntax
```ebnf
::= "interface(" ")" ;
::= ;::= | ;
::= "vfunc(" "," "," ")" ;
::= "vfuncDefault(" "," "," ")" ;
::= ;
::= ;
::= ;::= "impl(" "," ")" ;
::= "implExtern(" "," ")" ;
::= "declImpl(" "," ")" ;
::= "declImplExtern(" "," ")" ;
::= ;::= "DYN(" "," "," ")" ;
::= "DYN_LIT(" "," "," "{" "}" ")" ;
::= "VTABLE(" "," ")" ;::= "VSelf" ;
::= "VSELF(" ")" ;(* must be an expression of an interface object type. *)
::= "VCALL(" "," ")" ;
::= "VCALL_OBJ(" "," ")" ;
::= "VCALL_SUPER(" "," "," ")" ;
::= "VCALL_SUPER_OBJ(" "," "," ")" ;
::= [ "," ] ;::= ;
```Note: shortened vs. postfixed versions
Each listed identifier in the above grammar corresponds to a macro name defined by default -- these are called _shortened versions_. On the other hand, there are also _postfixed versions_ (`interface99`, `impl99`, `vfunc99`, etc.), which are defined unconditionally. If you want to avoid name clashes caused by shortened versions, define `IFACE99_NO_ALIASES` before including `interface99.h`. Library headers are strongly advised to use the postfixed macros, but without resorting to `IFACE99_NO_ALIASES`.
Notes:
- For every interface ``, the macro `_IFACE` must expand to `{ }*`.
- For any interface, a macro `_EXTENDS` can be defined, which must expand to `"(" { "," }* ")"`.
- For any interface function implementation, a macro `__CUSTOM` can be defined, which must expand to `"()"`.[Clang-Format]: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
### Semantics
(It might be helpful to look at the [generated output](https://godbolt.org/z/Gr6f7TM83) of [`examples/shape.c`](examples/shape.c).)
#### `interface`
Expands to
```
typedef struct VTable VTable;
typedef struct ;struct VTable {
// Only if is a marker interface without superinterfaces:
char dummy;0 (*0)(0);
...
N (*N)(N);const 0VTable *;
...
const NVTable *;
};struct {
void *self;
const VTable *vptr;
}
```(`char dummy;` is needed for an empty `VTable` because a structure must have at least one member, according to C99.)
I.e., this macro defines a virtual table structure for ``, as well as the structure `` that is polymorphic over `` implementers. This is generated in two steps:
- **Function pointers**. For each `I` specified in the macro `_IFACE`, the corresponding function pointer is generated.
- **Requirements obligation.** If the macro `_EXTENDS` is defined, then the listed requirements are generated to obligate `` implementers to satisfy them.#### `impl`
Expands to
```
static const VTable VTABLE(, ) = {
// Only if is a marker interface without superinterfaces:
.dummy = '\0',0 = either _0 or _0,
...
N = either _N or _N,0 = &VTABLE(, 0),
...
N = &VTABLE(, N),
}
```I.e., this macro defines a virtual table instance of type `VTable` for ``. It is generated in two steps:
- **Function implementations.** If `I` is defined via `vfuncDefault` and `_I_CUSTOM` is **not** defined, `_I` is generated (default implementation). Otherwise, `_I` is generated (custom implementation).
- **Requirements satisfaction.** If the macro `_EXTENDS` is defined, then the listed requirements are generated to satisfy ``.#### `implExtern`
The same as [`impl`](#impl) but generates an `extern` definition instead of `static`.
#### `declImpl`
Expands to `static const VTable VTABLE(, )`, i.e., it declares a virtual table instance of `` of type `VTable`.
#### `declImplExtern`
The same as [`declImpl`](#declImpl) but generates an `extern` declaration instead of `static`.
#### `DYN`
Expands to an expression of type ``, with `.self` initialised to `` and `.vptr` initialised to `&VTABLE(, )`.
`` is guaranteed to be evaluated only once.
#### `DYN_LIT`
`DYN_LIT(, , ...)` expands to `DYN(, , &()...)`. The `...` must take the form of an initialiser list in [compound literals].
[compound literals]: https://en.cppreference.com/w/c/language/compound_literal
#### `VTABLE`
Expands to `__impl`, i.e., a virtual table instance of `` of type `VTable`.
#### `VSelf`/`VSELF`
`VSelf` is an object-like macro that expands to a function parameter of type `void * restrict`, with an implementation-defined name. In order to downcast this parameter to an implementer type, there exists a function-like macro `VSELF`. `VSELF(T)` which brings a variable `self` of type `T * restrict` into the scope, and initialises it to the `VSelf`-produced parameter name casted to `T * restrict`.
`VSelf` can be used on any position for any virtual function, however, it only makes sense to use it as a first parameter. `VSELF(T)` can be used everywhere inside a function with the `VSelf` parameter.
#### `VCALL_*`
The `VCALL_*` macros are meant to **call** a **v**irtual method, which is a `vfunc`/`vfuncDefault` that accepts either `VSelf` or an interface object (of a containing interface type) as a first parameter.
For methods accepting `VSelf`, there exist `VCALL` and `VCALL_SUPER`:
- `VCALL(obj, func)` => `obj.vptr->func(obj.self)`.
- `VCALL(obj, func, args...)` => `obj.vptr->func(obj.self, args...)`.
- `VCALL_SUPER(obj, superiface, func)` => `obj.vptr->superiface->func(obj.self)`.
- `VCALL_SUPER(obj, superiface, func, args...)` => `obj.vptr->superiface->func(obj.self, args...)`.For methods accepting an interface object, there are `VCALL_OBJ` and `VCALL_SUPER_OBJ`:
- `VCALL_OBJ` is the same as `VCALL` except that it passes `obj` to `func` instead of `obj.self`.
- `VCALL_SUPER_OBJ` is the same as `VCALL_SUPER` except that it passes `(superiface){obj.self, obj.vptr->superiface}` to `func` instead of `obj.self`.## Miscellaneous
- The macros `IFACE99_MAJOR`, `IFACE99_MINOR`, `IFACE99_PATCH`, `IFACE99_VERSION_COMPATIBLE(x, y, z)`, and `IFACE99_VERSION_EQ(x, y, z)` have the [same semantics as of Metalang99](https://metalang99.readthedocs.io/en/latest/#version-manipulation-macros).
- For each macro using `ML99_EVAL`, Interface99 provides its [Metalang99-compliant](https://metalang99.readthedocs.io/en/latest/#definitions) counterpart which can be used inside derivers and other Metalang99-compliant macros:
| Macro | Metalang99-compliant counterpart |
|----------|----------|
| `interface` | `IFACE99_interface` |
| `impl` | `IFACE99_impl` |
| `implExtern` | `IFACE99_implExtern` |(An [arity specifier] and [desugaring macro] are provided for each of the above macros.)
[arity specifier]: https://hirrolot.gitbook.io/metalang99/partial-application
[desugaring macro]: https://metalang99.readthedocs.io/en/latest/#definitions## Guidelines
- Write `impl(...)`/`implExtern(...)` right after all functions are implemented; do not gather all implementation definitions in a single place.
- If you use [Clang-Format], it can be helpful to add `vfunc` and `vfuncDefault` to the `StatementMacros` vector (see [our `.clang-format`](.clang-format)). It will instruct the formatter to place them onto different lines.## Pitfalls
- Both interfaces that you implement for a single type can have a function with the same name, thus resulting in a name collision. However, you can elegantly workaround like this:
```c
// `MyType_Iface1_foo` function definition here...#define Iface1_foo MyType_Iface1_foo
impl(Iface1, MyType);
#undef Iface1_foo// `MyType_Iface2_foo` function definition here...
#define Iface2_foo MyType_Iface2_foo
impl(Iface2, MyType);
#undef Iface2_foo
```The same holds for custom implementations:
```c
// Use a custom implementation for `Iface1::bar`.
#define MyType_bar_CUSTOM ()
impl(Iface1, MyType);
#undef MyType_bar_CUSTOM// Use the default `Iface2::bar`.
impl(Iface2, MyType);
```## Design choices
The design of Interface99 may raise some questions. In this section, you may find answers why it was designed in this way.
### `VCALL_*`
Instead of using the `VCALL_*` macros, we could instead generate functions that accept an interface object as a first parameter, with the rest of parameters being arguments to a particular method:
```c
void Shape_scale(Shape shape, int factor) {
shape.vptr->scale(shape.self, factor);
}
```But this approach does not work for superinterfaces' methods, as well as for methods accepting an interface object instead of `VSelf` or a combination thereof. For this reason, I decided to stick to more expressive `VCALL_*` macros, although at the cost of some IDE support.
### `self` type safety
Since there can be many specific implementations of a virtual method (like `Rectangle_scale` or `Triangle_scale`), `self` **must** be of type `void *`. But the problem is that in concrete implementations, we still want `self` to be of some concrete type; and since `void *` and `T *` may be incompatible types, assigning a concrete method accepting `T *` to a virtual method field [results in UB](https://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type).
To solve the problem, we may want to generate untyped wrapper functions that accept `void *restrict self` and pass the downcasted version to the underlying method:
```c
void Rectangle_scale_wrapper(void *restrict self, int factor) {
Rectangle_scale((Rectangle * restrict)self, factor);
}
```But the reason we do **not** do this is that in C99, it is impossible to differentiate `void` from other types; if the return type is `void`, we must not emit `return` with an expression, otherwise, we **must**. We could come up with something like `vfuncVoid` and `vfuncDefaultVoid` but this would increase the learning curve and complicate the design and implementation of Interface99.
However, casting untyped `self` to a particular type is still quite unpleasant. The best thing I came up with is the `VSelf` and `VSELF(T)` mechanism, which nonetheless works quite well.
## Credits
Thanks to Rust and Golang for their implementations of traits/interfaces.
## Publications
- [_Comparing Golang and Interface99_](https://www.reddit.com/r/C_Programming/comments/tgm5ft/comparing_golang_and_interface99/)
- [_What’s the Point of the C Preprocessor, Actually?_](https://hirrolot.github.io/posts/whats-the-point-of-the-c-preprocessor-actually.html)
- [_Macros on Steroids, Or: How Can Pure C Benefit From Metaprogramming_](https://hirrolot.github.io/posts/macros-on-steroids-or-how-can-pure-c-benefit-from-metaprogramming.html)
- [_Extend Your Language, Don’t Alter It_](https://hirrolot.github.io/posts/extend-your-language-dont-alter-it.html)## Release procedure
1. Update `IFACE99_MAJOR`, `IFACE99_MINOR`, and `IFACE99_PATCH` in `interface99.h`.
2. Update `CHANGELOG.md`.
3. Release the project in [GitHub Releases].[GitHub Releases]: https://github.com/hirrolot/interface99/releases
## FAQ
### Q: Why use C instead of Rust/Zig/whatever else?
A: See [Datatype99's README >>](https://github.com/hirrolot/datatype99#q-why-use-c-instead-of-rustzigwhatever-else).
### Q: Why not third-party code generators?
A: See [Metalang99's README >>](https://github.com/hirrolot/metalang99#q-why-not-third-party-code-generators).
### Q: How does it work?
A: Interface99 is implemented upon [Metalang99], a preprocessor metaprogramming library that allows enriching pure C with some custom syntax sugar.
### Q: Does it work on C++?
A: Yes, C++11 and onwards is supported.
### Q: How Interface99 differs from similar projects?
A:
- **Less boilerplate.** In particular, Interface99 deduces function implementations from the context, thus improving code maintenance. To my knowledge, no other alternative can do this.
- **Small.** Interface99 only features the software interface concept, no less and no more -- it does not bring all the other fancy OOP stuff, unlike [GObject] or [COS].
- **Depends on Metalang99.** Interface99 is built upon [Metalang99], the underlying metaprogramming framework. With Metalang99, you can also use [Datatype99].
Other worth-mentioning projects:
- [typeclass-interface-pattern], though it is rather a general idea than a ready-to-use implementation.
- [OOC] -- a book about OO programming in ANSI C.[`obj.h`]: https://github.com/small-c/obj.h
[GObject]: https://developer.gnome.org/gobject/stable/
[COS]: http://ldeniau.web.cern.ch/ldeniau/cos.html
[Datatype99]: https://github.com/hirrolot/datatype99
[typeclass-interface-pattern]: https://github.com/TotallyNotChase/typeclass-interface-pattern
[OOC]: https://www.cs.rit.edu/~ats/books/ooc.pdf### Q: What about compile-time errors?
#### Error: missing interface implementation
[`playground.c`]
```c
#define Foo_IFACE vfunc(void, foo, int x, int y)
interface(Foo);typedef struct {
char dummy;
} MyFoo;// Missing `void MyFoo_foo(int x, int y)`.
impl(Foo, MyFoo);
```[`/bin/sh`]
```
playground.c:12:1: error: ‘MyFoo_foo’ undeclared here (not in a function)
12 | impl(Foo, MyFoo);
| ^~~~
```----------
#### Error: improperly typed interface implementation
[`playground.c`]
```c
#define Foo_IFACE vfunc(void, foo, int x, int y)
interface(Foo);typedef struct {
char dummy;
} MyFoo;void MyFoo_foo(const char *str) {}
impl(Foo, MyFoo);
```[`/bin/sh`]
```
playground.c:12:1: warning: initialization of ‘void (*)(int, int)’ from incompatible pointer type ‘void (*)(const char *)’ [-Wincompatible-pointer-types]
12 | impl(Foo, MyFoo);
| ^~~~
playground.c:12:1: note: (near initialization for ‘MyFoo_Foo_impl.foo’)
```----------
#### Error: unsatisfied interface requirement
[`playground.c`]
```c
#define Foo_IFACE vfunc(void, foo, int x, int y)
interface(Foo);#define Bar_IFACE vfunc(void, bar, void)
#define Bar_EXTENDS (Foo)interface(Bar);
typedef struct {
char dummy;
} MyBar;void MyBar_bar(void) {}
// Missing `impl(Foo, MyBar)`.
impl(Bar, MyBar);
```[`/bin/sh`]
```
playground.c:19:1: error: ‘MyBar_Foo_impl’ undeclared here (not in a function); did you mean ‘MyBar_Bar_impl’?
19 | impl(Bar, MyBar);
| ^~~~
| MyBar_Bar_impl
```----------
#### Error: typo in `DYN`
[`playground.c`]
```c
#define Foo_IFACE vfunc(void, foo, void)
interface(Foo);typedef struct {
char dummy;
} MyFoo;void MyFoo_foo(void) {}
impl(Foo, MyFoo);
int main(void) { Foo foo = DYN(MyFoo, /* Foo */ Bar, &(MyFoo){0}); }
```[`/bin/sh`]
```
playground.c: In function ‘main’:
playground.c:14:28: error: ‘Bar’ undeclared (first use in this function)
14 | int main(void) { Foo foo = DYN(MyFoo, /* Foo */ Bar, &(MyFoo){0}); }
| ^~~
playground.c:14:28: note: each undeclared identifier is reported only once for each function it appears in
playground.c:14:31: error: expected ‘)’ before ‘{’ token
14 | int main(void) { Foo foo = DYN(MyFoo, /* Foo */ Bar, &(MyFoo){0}); }
| ~~~^
| )
```----------
#### Error: typo in `VTABLE`
[`playground.c`]
```c
#define Foo_IFACE vfunc(void, foo, void)
interface(Foo);typedef struct {
char dummy;
} MyFoo;void MyFoo_foo(void) {}
impl(Foo, MyFoo);
int main(void) { FooVTable foo = VTABLE(/* MyFoo */ MyBar, Foo); }
```[`/bin/sh`]
```
playground.c: In function ‘main’:
playground.c:14:34: error: ‘MyBar_Foo_impl’ undeclared (first use in this function); did you mean ‘MyFoo_Foo_impl’?
14 | int main(void) { FooVTable foo = VTABLE(/* MyFoo */ MyBar, Foo); }
| ^~~~~~
| MyFoo_Foo_impl
```----------
From my experience, nearly 95% of errors make sense.
If an error is not comprehensible at all, try to look at generated code (`-E`). Hopefully, the [code generation semantics] is formally defined so normally you will not see something unexpected.
### Q: What about IDE support?
![Suggestion](images/suggestion.png)
A: VS Code automatically enables suggestions of generated types but, of course, it does not support macro syntax highlighting. The sad part is that `VCALL` and its friends break go-to definitions and do not highlight function signatures, so we do intentionally [trade some IDE support for syntax conciseness](#vcall_-1).
### Q: Which compilers are tested?
A: Interface99 is known to work on these compilers:
- GCC
- Clang
- MSVC
- TCC