{"id":15049287,"url":"https://github.com/hirrolot/interface99","last_synced_at":"2025-04-09T06:13:32.273Z","repository":{"id":50514418,"uuid":"371031412","full_name":"Hirrolot/interface99","owner":"Hirrolot","description":"Full-featured interfaces for C99","archived":false,"fork":false,"pushed_at":"2024-04-06T08:42:42.000Z","size":422,"stargazers_count":277,"open_issues_count":0,"forks_count":13,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-05-21T03:24:59.396Z","etag":null,"topics":["c99","dynamic-dispatch","generic-programming","macros","metalang99","metaprogramming","object-oriented","oop","polymorphism","type-system","typeclasses"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Hirrolot.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-26T12:47:27.000Z","updated_at":"2024-05-30T04:34:14.314Z","dependencies_parsed_at":"2024-01-13T17:10:38.952Z","dependency_job_id":"783efced-9142-4e9e-8765-0e6bcd6c0be3","html_url":"https://github.com/Hirrolot/interface99","commit_stats":{"total_commits":235,"total_committers":1,"mean_commits":235.0,"dds":0.0,"last_synced_commit":"c81db7f76c70cd4a7fb1a55ba783dde47ecd93a1"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hirrolot%2Finterface99","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hirrolot%2Finterface99/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hirrolot%2Finterface99/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hirrolot%2Finterface99/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hirrolot","download_url":"https://codeload.github.com/Hirrolot/interface99/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247987285,"owners_count":21028895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c99","dynamic-dispatch","generic-programming","macros","metalang99","metaprogramming","object-oriented","oop","polymorphism","type-system","typeclasses"],"created_at":"2024-09-24T21:19:31.032Z","updated_at":"2025-04-09T06:13:32.248Z","avatar_url":"https://github.com/Hirrolot.png","language":"C","readme":"\u003cdiv align=\"center\"\u003e\n\u003ch1\u003eInterface99\u003c/h1\u003e\n\u003ca href=\"https://github.com/hirrolot/interface99/actions\"\u003e\n\u003cimg src=\"https://github.com/hirrolot/interface99/workflows/C/C++%20CI/badge.svg\"\u003e\n\u003c/a\u003e\n\nFull-featured interfaces inspired by Rust and Golang. Multiple inheritance, superinterfaces, and default implementations supported. No external tools required, pure C99.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e\u003cb\u003eShape\u003c/b\u003e\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv align=\"left\"\u003e\n\n```c\n#include \u003cinterface99.h\u003e\n\n#include \u003cstdio.h\u003e\n\n#define Shape_IFACE                      \\\n    vfunc( int, perim, const VSelf)      \\\n    vfunc(void, scale, VSelf, int factor)\n\ninterface(Shape);\n```\n\n\u003c/div\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e\u003cb\u003eRectangle\u003c/b\u003e\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e\u003cb\u003eTriangle\u003c/b\u003e\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv align=\"left\"\u003e\n\n```c\ntypedef struct {\n    int a, b;\n} Rectangle;\n\nint Rectangle_perim(const VSelf) {\n    VSELF(const Rectangle);\n    return (self-\u003ea + self-\u003eb) * 2;\n}\n\nvoid Rectangle_scale(VSelf, int factor) {\n    VSELF(Rectangle);\n    self-\u003ea *= factor;\n    self-\u003eb *= factor;\n}\n\nimpl(Shape, Rectangle);\n```\n\n\u003c/div\u003e\n\u003c/td\u003e\n\u003ctd\u003e\n\u003cdiv align=\"left\"\u003e\n\n```c\ntypedef struct {\n    int a, b, c;\n} Triangle;\n\nint Triangle_perim(const VSelf) {\n    VSELF(const Triangle);\n    return self-\u003ea + self-\u003eb + self-\u003ec;\n}\n\nvoid Triangle_scale(VSelf, int factor) {\n    VSELF(Triangle);\n    self-\u003ea *= factor;\n    self-\u003eb *= factor;\n    self-\u003ec *= factor;\n}\n\nimpl(Shape, Triangle);\n```\n\n\u003c/div\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e\u003cb\u003eTest\u003c/b\u003e\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdiv align=\"left\"\u003e\n\n```c\nvoid test(Shape shape) {\n    printf(\"perim = %d\\n\", VCALL(shape, perim));\n    VCALL(shape, scale, 5);\n    printf(\"perim = %d\\n\", VCALL(shape, perim));\n}\n\nint main(void) {\n    Shape r = DYN_LIT(Rectangle, Shape, {5, 7});\n    Shape t = DYN_LIT(Triangle, Shape, {10, 20, 30});\n\n    test(r);\n    test(t);\n}\n```\n\n\u003c/div\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/div\u003e\n\n(Based on [`examples/shape.c`](examples/shape.c).)\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```\nperim = 24\nperim = 120\nperim = 60\nperim = 300\n```\n\n\u003c/details\u003e\n\n## Highlights\n\n - **Minimum boilerplate.** Forget about maintaining virtual tables -- just write `impl(Shape, Rectangle)` and Interface99 will do it for you!\n\n - **Portable.** Everything you need is a standard-conforming C99 compiler; neither the standard library, nor compiler/platform-specific functionality or VLA are required.\n\n - **Predictable.** Interface99 comes with formal [code generation semantics], meaning that the generated data layout is guaranteed to always be the same.\n\n - **Comprehensible errors.** Interface99 is [resilient to bad code].\n\n - **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.\n\n[code generation semantics]: #semantics\n[resilient to bad code]: #q-what-about-compile-time-errors\n[OpenIPC]: https://openipc.org/\n[RTSP 1.0 implementation]: https://github.com/OpenIPC/smolrtsp/\n\n## Features\n\n| Feature | Status | Description |\n|---------|--------|-------------|\n| [Multiple interface inheritance](examples/read_write.c) | ✅ | A type can inherit multiple interfaces at the same time. |\n| [Superinterfaces](examples/airplane.c) | ✅ | One interface can require a set of other interfaces to be implemented as well. |\n| [Marker interfaces](examples/marker.c) | ✅ | An interface with no functions. |\n| [Single/Dynamic dispatch](examples/shape.c) | ✅ | Determine a function to be called at runtime based on `self`. |\n| Multiple dispatch | ❌ | Determine a function to be called at runtime based on multiple arguments. Likely to never going to be implemented. |\n| [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`. |\n| [Default implementations](examples/default_impl.c)  | ✅ | Some interface functions may be given default implementations. A default function can call other functions and vice versa. |\n| Data and implementation separation  | ✅ | New interfaces can be implemented for existing types. |\n\n## Installation\n\nInterface99 consists of one header file `interface99.h` and one dependency [Metalang99]. To use it in your project, you need to:\n\n[Metalang99]: https://github.com/hirrolot/metalang99\n\n 1. Add `interface99` and `metalang99/include` to your include directories.\n 2. Specify [`-ftrack-macro-expansion=0`] (GCC) or [`-fmacro-backtrace-limit=1`] (Clang) to avoid useless macro expansion errors.\n\n[`-ftrack-macro-expansion=0`]: https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html\n[`-fmacro-backtrace-limit=1`]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fmacro-backtrace-limit\n\nIf you use CMake, the recommended way is [`FetchContent`]:\n\n[`FetchContent`]: https://cmake.org/cmake/help/latest/module/FetchContent.html\n\n```cmake\ninclude(FetchContent)\n\nFetchContent_Declare(\n    interface99\n    URL https://github.com/hirrolot/interface99/archive/refs/tags/vx.y.z.tar.gz # vx.y.z\n)\n\nFetchContent_MakeAvailable(interface99)\n\ntarget_link_libraries(MyProject interface99)\n\n# Disable full macro expansion backtraces for Metalang99.\nif(CMAKE_C_COMPILER_ID STREQUAL \"Clang\")\n  target_compile_options(MyProject PRIVATE -fmacro-backtrace-limit=1)\nelseif(CMAKE_C_COMPILER_ID STREQUAL \"GNU\")\n  target_compile_options(MyProject PRIVATE -ftrack-macro-expansion=0)\nendif()\n```\n\n(By default, `interface99/CMakeLists.txt` downloads Metalang99 [v1.13.5](https://github.com/hirrolot/metalang99/releases/tag/v1.13.5) from the GitHub releases; if you want to override this behaviour, you can do so by invoking [`FetchContent_Declare`] earlier.)\n\n[`FetchContent_Declare`]: https://cmake.org/cmake/help/latest/module/FetchContent.html#command:fetchcontent_declare\n\nOptionally, 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.\n\n[precompile headers]: https://en.wikipedia.org/wiki/Precompiled_header\n\nHappy hacking!\n\n## Tutorial\n\nThis section is based on a collection of well-documented [examples](examples/), each of which demonstrates one specific aspect of Interface99.\n\n### Basic usage\n\n 1. **Interface definition.**\n\nSyntax: [`interface(Shape);`](#interface)\n\nAn 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):\n\n```c\n// interface(Shape);\ntypedef struct ShapeVTable ShapeVTable;\ntypedef struct Shape Shape;\n\nstruct ShapeVTable {\n    int (*perim)(const VSelf);\n    void (*scale)(VSelf, int factor);\n};\n\nstruct Shape {\n    void *self;\n    const ShapeVTable *vptr;\n};\n```\n\nHere, `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.\n\nUsually, interface definitions go in `*.h` files.\n\n 2. **Implementation definition.**\n\n| Linkage | Syntax |\n|---------|--------|\n| Internal | [`impl(Shape, Rectangle);`](#impl) |\n| External | [`implExtern(Shape, Rectangle);`](#implExtern) |\n\nAn implementation definition expands to nothing but a virtual table instance of a particular implementer. In the case of `examples/shape.c`:\n\n```c\n// impl(Shape, Rectangle);\nstatic const ShapeVTable Rectangle_Shape_impl = {\n    .perim = Rectangle_perim,\n    .scale = Rectangle_scale,\n};\n```\n\n(If you were using [`implExtern`](#implExtern), this definition would be `extern` likewise.)\n\nNote 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):\n\n```c\nint Rectangle_perim(const VSelf) {\n    VSELF(const Rectangle);\n    return (self-\u003ea + self-\u003eb) * 2;\n}\n\nvoid Rectangle_scale(VSelf, int factor) {\n    VSELF(Rectangle);\n    self-\u003ea *= factor;\n    self-\u003eb *= factor;\n}\n```\n\n 3. **Dynamic dispatch.**\n\nOnce an interface and its implementations are both generated, it is time to instantiate an interface object and invoke some functions upon it.\n\nFirst of all, to instantiate `Shape`, use the [`DYN_LIT`](#DYN_LIT) macro:\n\n```с\nShape r = DYN_LIT(Rectangle, Shape, {5, 7});\ntest(r);\n```\n\nHere, `DYN_LIT(Rectangle, Shape, {5, 7})` creates `Shape` by assigning `Shape.self` to `\u0026(Rectangle){5, 7}` and `Shape.vptr` to the aforementioned `\u0026Rectangle_Shape_impl`. Eventually, you can accept `Shape` as a function parameter and perform dynamic dispatch through the [`VCALL`](#vcall_) macro:\n\n```c\nvoid test(Shape shape) {\n    printf(\"perim = %d\\n\", VCALL(shape, perim));\n    VCALL(shape, scale, 5);\n    printf(\"perim = %d\\n\", VCALL(shape, perim));\n}\n```\n\nFinally, just a few brief notes:\n\n - 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_).\n - 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.\n - If your virtual function does not accept `self`, you can invoke it as `obj.vptr-\u003efoo(...)`.\n - If you want to call an interface function on some concrete type, just write `VTABLE(T, Iface).foo(...)`.\n\nCongratulations, this is all you need to know to write most of the stuff!\n\n### Superinterfaces\n\nInterface99 has the feature called superinterfaces, or interface requirements. [`examples/airplane.c`](examples/airplane.c) demonstrates how to extend interfaces with new functionality:\n\n```c\n#define Vehicle_IFACE                              \\\n    vfunc(void, move_forward, VSelf, int distance) \\\n    vfunc(void,    move_back, VSelf, int distance)\n\ninterface(Vehicle);\n\n#define Airplane_IFACE                          \\\n    vfunc(void,   move_up, VSelf, int distance) \\\n    vfunc(void, move_down, VSelf, int distance)\n\n#define Airplane_EXTENDS (Vehicle)\n\ninterface(Airplane);\n```\n\n(Note that `#define Airplane_EXTENDS` must appear prior to `interface(Airplane);`.)\n\nHere, `Airplane` extends `Vehicle` with the new functions `move_up` and `move_down`. Everywhere you have `Airplane`, you can also operate `Vehicle`:\n\n```c\nAirplane my_airplane = DYN_LIT(MyAirplane, Airplane, {.x = 0, .y = 0});\n\nVCALL_SUPER(my_airplane, Vehicle, move_forward, 10);\nVCALL_SUPER(my_airplane, Vehicle, move_back, 3);\n```\n\nInternally, 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:\n\n```c\n// #define Airplane_EXTENDS (Vehicle, Repairable, Armoured)\ntypedef struct AirplaneVTable {\n    void (*move_up)(VSelf, int distance);\n    void (*move_down)(VSelf, int distance);\n    const VehicleVTable *Vehicle;\n    const RepairableVTable *Repairable;\n    const ArmouredVTable *Armoured;\n} AirplaneVTable;\n```\n\n### Default implementations\n\nSometimes we wish to define default behaviour for several implementers; this is supported by _default implementations_.\n\nTake a look at [`examples/default_impl.c`](examples/default_impl.c). In this example, we define the interface `Droid`:\n\n```c\n#define Droid_IFACE                         \\\n    vfunc(const char *, name, void)         \\\n    vfuncDefault(void, turn_on, Droid droid)\n\ninterface(Droid);\n```\n\nThe macro `vfuncDefault` tells Interface99 to use the default implementation for `turn_on` automatically. But where is it located? Here:\n\n```c\nvoid Droid_turn_on(Droid droid) {\n    printf(\"Turning on %s...\\n\", droid.vptr-\u003ename());\n}\n```\n\nAs you can see, default implementations follow a strict naming convention, `\u003ciface\u003e_\u003cdefault-func-name\u003e` , 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:\n\n```c\nstatic const DroidVTable C_3PO_Droid_impl = {\n    .name = C_3PO_name,\n    .turn_on = Droid_turn_on,\n};\n```\n\nBut for `R2_D2`, we use a custom implementation `R2_D2_turn_on`:\n\n```c\nvoid R2_D2_turn_on(Droid droid) {\n    Droid_turn_on(droid);\n    puts(\"Waaaaoow!\");\n}\n\n#define R2_D2_turn_on_CUSTOM ()\nimpl(Droid, R2_D2);\n```\n\n(`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.)\n\nAnd the virtual table would be:\n\n```c\nstatic const DroidVTable R2_D2_Droid_impl = {\n    .name = R2_D2_name,\n    .turn_on = R2_D2_turn_on,\n};\n```\n\nPlease, note that you have to specify `()` for the `*_CUSTOM` attribute; do not leave it empty.\n\n## Syntax and semantics\n\nHaving a well-defined semantics of the macros, you can write an FFI which is quite common in C.\n\n### EBNF syntax\n\n```ebnf\n\u003ciface-def\u003e       ::= \"interface(\" \u003ciface\u003e \")\" ;\n\u003ciface\u003e           ::= \u003cident\u003e ;\n\n\u003cfunc\u003e            ::= \u003cregular-func\u003e | \u003cdefault-func\u003e ;\n\u003cregular-func\u003e    ::= \"vfunc(\"        \u003cfunc-ret-ty\u003e \",\" \u003cfunc-name\u003e \",\" \u003cfunc-params\u003e \")\" ;\n\u003cdefault-func\u003e    ::= \"vfuncDefault(\" \u003cfunc-ret-ty\u003e \",\" \u003cfunc-name\u003e \",\" \u003cfunc-params\u003e \")\" ;\n\u003cfunc-ret-ty\u003e     ::= \u003ctype\u003e ;\n\u003cfunc-name\u003e       ::= \u003cident\u003e ;\n\u003cfunc-params\u003e     ::= \u003cparameter-type-list\u003e ;\n\n\u003cimpl\u003e            ::= \"impl(\"           \u003ciface\u003e \",\" \u003cimplementer\u003e \")\" ;\n\u003cimplExtern\u003e      ::= \"implExtern(\"     \u003ciface\u003e \",\" \u003cimplementer\u003e \")\" ;\n\u003cdeclImpl\u003e        ::= \"declImpl(\"       \u003ciface\u003e \",\" \u003cimplementer\u003e \")\" ;\n\u003cdeclImplExtern\u003e  ::= \"declImplExtern(\" \u003ciface\u003e \",\" \u003cimplementer\u003e \")\" ;\n\u003cimplementer\u003e     ::= \u003cident\u003e ;\n\n\u003cdyn\u003e             ::= \"DYN(\"     \u003cimplementer\u003e \",\" \u003ciface\u003e \",\" \u003cptr\u003e \")\" ;\n\u003cdyn-lit\u003e         ::= \"DYN_LIT(\" \u003cimplementer\u003e \",\" \u003ciface\u003e \",\" \"{\" \u003cinitializer-list\u003e \"}\" \")\" ;\n\u003cvtable\u003e          ::= \"VTABLE(\"  \u003cimplementer\u003e \",\" \u003ciface\u003e \")\" ;\n\n\u003cvself-params\u003e    ::= \"VSelf\" ;\n\u003cvself-cast\u003e      ::= \"VSELF(\" \u003ctype\u003e \")\" ;\n\n(* \u003cexpr\u003e must be an expression of an interface object type. *)\n\u003cvcall\u003e           ::= \"VCALL(\"           \u003cexpr\u003e \",\" \u003cfunc-name\u003e \u003cvcall-args\u003e \")\" ;\n\u003cvcall-obj\u003e       ::= \"VCALL_OBJ(\"       \u003cexpr\u003e \",\" \u003cfunc-name\u003e \u003cvcall-args\u003e \")\" ;\n\u003cvcall-super\u003e     ::= \"VCALL_SUPER(\"     \u003cexpr\u003e \",\" \u003ciface\u003e \",\" \u003cfunc-name\u003e \u003cvcall-args\u003e \")\" ;\n\u003cvcall-super-obj\u003e ::= \"VCALL_SUPER_OBJ(\" \u003cexpr\u003e \",\" \u003ciface\u003e \",\" \u003cfunc-name\u003e \u003cvcall-args\u003e \")\" ;\n\u003cvcall-args\u003e      ::= [ \",\" \u003cargument-expression-list\u003e ] ;\n\n\u003crequirement\u003e     ::= \u003ciface\u003e ;\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eNote: shortened vs. postfixed versions\u003c/summary\u003e\n\n  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`.\n\u003c/details\u003e\n\nNotes:\n\n - For every interface `\u003ciface\u003e`, the macro `\u003ciface\u003e_IFACE` must expand to `{ \u003cfunc\u003e }*`.\n - For any interface, a macro `\u003ciface\u003e_EXTENDS` can be defined, which must expand to `\"(\" \u003crequirement\u003e { \",\" \u003crequirement\u003e }* \")\"`.\n - For any interface function implementation, a macro `\u003cimplementer\u003e_\u003cfunc-name\u003e_CUSTOM` can be defined, which must expand to `\"()\"`.\n\n[Clang-Format]: https://clang.llvm.org/docs/ClangFormatStyleOptions.html\n\n### Semantics\n\n(It might be helpful to look at the [generated output](https://godbolt.org/z/Gr6f7TM83) of [`examples/shape.c`](examples/shape.c).)\n\n#### `interface`\n\nExpands to\n\n```\ntypedef struct \u003ciface\u003eVTable \u003ciface\u003eVTable;\ntypedef struct \u003ciface\u003e \u003ciface\u003e;\n\nstruct \u003ciface\u003eVTable {\n    // Only if \u003ciface\u003e is a marker interface without superinterfaces:\n    char dummy;\n\n    \u003cfunc-ret-ty\u003e0 (*\u003cfunc-name\u003e0)(\u003cfunc-params\u003e0);\n    ...\n    \u003cfunc-ret-ty\u003eN (*\u003cfunc-name\u003eN)(\u003cfunc-params\u003eN);\n\n    const \u003crequirement\u003e0VTable *\u003crequirement\u003e;\n    ...\n    const \u003crequirement\u003eNVTable *\u003crequirement\u003e;\n};\n\nstruct \u003ciface\u003e {\n    void *self;\n    const \u003ciface\u003eVTable *vptr;\n}\n```\n\n(`char dummy;` is needed for an empty `\u003ciface\u003eVTable` because a structure must have at least one member, according to C99.)\n\nI.e., this macro defines a virtual table structure for `\u003ciface\u003e`, as well as the structure `\u003ciface\u003e` that is polymorphic over `\u003ciface\u003e` implementers. This is generated in two steps:\n\n - **Function pointers**. For each `\u003cfunc-name\u003eI` specified in the macro `\u003ciface\u003e_IFACE`, the corresponding function pointer is generated.\n - **Requirements obligation.** If the macro `\u003ciface\u003e_EXTENDS` is defined, then the listed requirements are generated to obligate `\u003ciface\u003e` implementers to satisfy them.\n\n#### `impl`\n\nExpands to\n\n```\nstatic const \u003ciface\u003eVTable VTABLE(\u003cimplementer\u003e, \u003ciface\u003e) = {\n    // Only if \u003ciface\u003e is a marker interface without superinterfaces:\n    .dummy = '\\0',\n\n    \u003cfunc-name\u003e0 = either \u003cimplementer\u003e_\u003cfunc-name\u003e0 or \u003ciface\u003e_\u003cfunc-name\u003e0,\n    ...\n    \u003cfunc-name\u003eN = either \u003cimplementer\u003e_\u003cfunc-name\u003eN or \u003ciface\u003e_\u003cfunc-name\u003eN,\n\n    \u003crequirement\u003e0 = \u0026VTABLE(\u003cimplementer\u003e, \u003crequirement\u003e0),\n    ...\n    \u003crequirement\u003eN = \u0026VTABLE(\u003cimplementer\u003e, \u003crequirement\u003eN),\n}\n```\n\nI.e., this macro defines a virtual table instance of type `\u003ciface\u003eVTable` for `\u003cimplementer\u003e`. It is generated in two steps:\n\n - **Function implementations.** If `\u003cfunc-name\u003eI` is defined via `vfuncDefault` and `\u003cimplementer\u003e_\u003cfunc-name\u003eI_CUSTOM` is **not** defined, `\u003ciface\u003e_\u003cfunc-name\u003eI` is generated (default implementation). Otherwise, `\u003cimplementer\u003e_\u003cfunc-name\u003eI` is generated (custom implementation).\n - **Requirements satisfaction.** If the macro `\u003ciface\u003e_EXTENDS` is defined, then the listed requirements are generated to satisfy `\u003ciface\u003e`.\n\n#### `implExtern`\n\nThe same as [`impl`](#impl) but generates an `extern` definition instead of `static`.\n\n#### `declImpl`\n\nExpands to `static const \u003ciface\u003eVTable VTABLE(\u003cimplementer\u003e, \u003ciface\u003e)`, i.e., it declares a virtual table instance of `\u003cimplementer\u003e` of type `\u003ciface\u003eVTable`.\n\n#### `declImplExtern`\n\nThe same as [`declImpl`](#declImpl) but generates an `extern` declaration instead of `static`.\n\n#### `DYN`\n\nExpands to an expression of type `\u003ciface\u003e`, with `.self` initialised to `\u003cptr\u003e` and `.vptr` initialised to `\u0026VTABLE(\u003cimplementer\u003e, \u003ciface\u003e)`.\n\n`\u003cptr\u003e` is guaranteed to be evaluated only once.\n\n#### `DYN_LIT`\n\n`DYN_LIT(\u003cimplementer\u003e, \u003ciface\u003e, ...)` expands to `DYN(\u003cimplementer\u003e, \u003ciface\u003e, \u0026(\u003cimplementer\u003e)...)`. The `...` must take the form of an initialiser list in [compound literals].\n\n[compound literals]: https://en.cppreference.com/w/c/language/compound_literal\n\n#### `VTABLE`\n\nExpands to `\u003cimplementer\u003e_\u003ciface\u003e_impl`, i.e., a virtual table instance of `\u003cimplementer\u003e` of type `\u003ciface\u003eVTable`.\n\n#### `VSelf`/`VSELF`\n\n`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`.\n\n`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.\n\n#### `VCALL_*`\n\nThe `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.\n\nFor methods accepting `VSelf`, there exist `VCALL` and `VCALL_SUPER`:\n\n - `VCALL(obj, func)` =\u003e `obj.vptr-\u003efunc(obj.self)`.\n - `VCALL(obj, func, args...)` =\u003e `obj.vptr-\u003efunc(obj.self, args...)`.\n - `VCALL_SUPER(obj, superiface, func)` =\u003e `obj.vptr-\u003esuperiface-\u003efunc(obj.self)`.\n - `VCALL_SUPER(obj, superiface, func, args...)` =\u003e `obj.vptr-\u003esuperiface-\u003efunc(obj.self, args...)`.\n\nFor methods accepting an interface object, there are `VCALL_OBJ` and `VCALL_SUPER_OBJ`:\n\n - `VCALL_OBJ` is the same as `VCALL` except that it passes `obj` to `func` instead of `obj.self`.\n - `VCALL_SUPER_OBJ` is the same as `VCALL_SUPER` except that it passes `(superiface){obj.self, obj.vptr-\u003esuperiface}` to `func` instead of `obj.self`.\n\n## Miscellaneous\n\n - 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).\n\n - 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:\n\n| Macro | Metalang99-compliant counterpart |\n|----------|----------|\n| `interface` | `IFACE99_interface` |\n| `impl` | `IFACE99_impl` |\n| `implExtern` | `IFACE99_implExtern` |\n\n(An [arity specifier] and [desugaring macro] are provided for each of the above macros.)\n\n[arity specifier]: https://hirrolot.gitbook.io/metalang99/partial-application\n[desugaring macro]: https://metalang99.readthedocs.io/en/latest/#definitions\n\n## Guidelines\n\n - Write `impl(...)`/`implExtern(...)` right after all functions are implemented; do not gather all implementation definitions in a single place.\n - 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.\n\n## Pitfalls\n\n - 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:\n\n```c\n// `MyType_Iface1_foo` function definition here...\n\n#define Iface1_foo MyType_Iface1_foo\nimpl(Iface1, MyType);\n#undef Iface1_foo\n\n// `MyType_Iface2_foo` function definition here...\n\n#define Iface2_foo MyType_Iface2_foo\nimpl(Iface2, MyType);\n#undef Iface2_foo\n```\n\nThe same holds for custom implementations:\n\n```c\n// Use a custom implementation for `Iface1::bar`.\n#define MyType_bar_CUSTOM ()\nimpl(Iface1, MyType);\n#undef MyType_bar_CUSTOM\n\n// Use the default `Iface2::bar`.\nimpl(Iface2, MyType);\n```\n\n## Design choices\n\nThe design of Interface99 may raise some questions. In this section, you may find answers why it was designed in this way.\n\n### `VCALL_*`\n\nInstead 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:\n\n```c\nvoid Shape_scale(Shape shape, int factor) {\n    shape.vptr-\u003escale(shape.self, factor);\n}\n```\n\nBut 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.\n\n### `self` type safety\n\nSince 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).\n\nTo 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:\n\n```c\nvoid Rectangle_scale_wrapper(void *restrict self, int factor) {\n    Rectangle_scale((Rectangle * restrict)self, factor);\n}\n```\n\nBut 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.\n\nHowever, 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.\n\n## Credits\n\nThanks to Rust and Golang for their implementations of traits/interfaces.\n\n## Blog posts\n\n - [_Comparing Golang and Interface99_](https://www.reddit.com/r/C_Programming/comments/tgm5ft/comparing_golang_and_interface99/)\n - [_What’s the Point of the C Preprocessor, Actually?_](https://hirrolot.github.io/posts/whats-the-point-of-the-c-preprocessor-actually.html)\n - [_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)\n - [_Extend Your Language, Don’t Alter It_](https://hirrolot.github.io/posts/extend-your-language-dont-alter-it.html)\n\n## Release procedure\n\n 1. Update `IFACE99_MAJOR`, `IFACE99_MINOR`, and `IFACE99_PATCH` in `interface99.h`.\n 2. Update `CHANGELOG.md`.\n 3. Release the project in [GitHub Releases].\n\n[GitHub Releases]: https://github.com/hirrolot/interface99/releases\n\n## FAQ\n\n### Q: Why use C instead of Rust/Zig/whatever else?\n\nA: See [Datatype99's README \u003e\u003e](https://github.com/hirrolot/datatype99#q-why-use-c-instead-of-rustzigwhatever-else).\n\n### Q: Why not third-party code generators?\n\nA: See [Metalang99's README \u003e\u003e](https://github.com/hirrolot/metalang99#q-why-not-third-party-code-generators).\n\n### Q: How does it work?\n\nA: Interface99 is implemented upon [Metalang99], a preprocessor metaprogramming library that allows enriching pure C with some custom syntax sugar.\n\n### Q: Does it work on C++?\n\nA: Yes, C++11 and onwards is supported.\n\n### Q: How Interface99 differs from similar projects?\n\nA:\n\n - **Less boilerplate.** In particular, Interface99 deduces function implementations from the context, thus improving code maintenance. To my knowledge, no other alternative can do this.\n\n - **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].\n\n - **Depends on Metalang99.** Interface99 is built upon [Metalang99], the underlying metaprogramming framework. With Metalang99, you can also use [Datatype99].\n\nOther worth-mentioning projects:\n\n - [typeclass-interface-pattern], though it is rather a general idea than a ready-to-use implementation.\n - [OOC] -- a book about OO programming in ANSI C.\n\n[`obj.h`]: https://github.com/small-c/obj.h\n[GObject]: https://developer.gnome.org/gobject/stable/\n[COS]: http://ldeniau.web.cern.ch/ldeniau/cos.html\n[Datatype99]: https://github.com/hirrolot/datatype99\n[typeclass-interface-pattern]: https://github.com/TotallyNotChase/typeclass-interface-pattern\n[OOC]: https://www.cs.rit.edu/~ats/books/ooc.pdf\n\n### Q: What about compile-time errors?\n\n#### Error: missing interface implementation\n\n[`playground.c`]\n```c\n#define Foo_IFACE vfunc(void, foo, int x, int y)\ninterface(Foo);\n\ntypedef struct {\n    char dummy;\n} MyFoo;\n\n// Missing `void MyFoo_foo(int x, int y)`.\n\nimpl(Foo, MyFoo);\n```\n\n[`/bin/sh`]\n```\nplayground.c:12:1: error: ‘MyFoo_foo’ undeclared here (not in a function)\n   12 | impl(Foo, MyFoo);\n      | ^~~~\n```\n\n----------\n\n#### Error: improperly typed interface implementation\n\n[`playground.c`]\n```c\n#define Foo_IFACE vfunc(void, foo, int x, int y)\ninterface(Foo);\n\ntypedef struct {\n    char dummy;\n} MyFoo;\n\nvoid MyFoo_foo(const char *str) {}\n\nimpl(Foo, MyFoo);\n```\n\n[`/bin/sh`]\n```\nplayground.c:12:1: warning: initialization of ‘void (*)(int,  int)’ from incompatible pointer type ‘void (*)(const char *)’ [-Wincompatible-pointer-types]\n   12 | impl(Foo, MyFoo);\n      | ^~~~\nplayground.c:12:1: note: (near initialization for ‘MyFoo_Foo_impl.foo’)\n```\n\n----------\n\n#### Error: unsatisfied interface requirement\n\n[`playground.c`]\n```c\n#define Foo_IFACE vfunc(void, foo, int x, int y)\ninterface(Foo);\n\n#define Bar_IFACE   vfunc(void, bar, void)\n#define Bar_EXTENDS (Foo)\n\ninterface(Bar);\n\ntypedef struct {\n    char dummy;\n} MyBar;\n\nvoid MyBar_bar(void) {}\n\n// Missing `impl(Foo, MyBar)`.\n\nimpl(Bar, MyBar);\n```\n\n[`/bin/sh`]\n```\nplayground.c:19:1: error: ‘MyBar_Foo_impl’ undeclared here (not in a function); did you mean ‘MyBar_Bar_impl’?\n   19 | impl(Bar, MyBar);\n      | ^~~~\n      | MyBar_Bar_impl\n```\n\n----------\n\n#### Error: typo in `DYN`\n\n[`playground.c`]\n```c\n#define Foo_IFACE vfunc(void, foo, void)\ninterface(Foo);\n\ntypedef struct {\n    char dummy;\n} MyFoo;\n\nvoid MyFoo_foo(void) {}\n\nimpl(Foo, MyFoo);\n\nint main(void) { Foo foo = DYN(MyFoo, /* Foo */ Bar, \u0026(MyFoo){0}); }\n```\n\n[`/bin/sh`]\n```\nplayground.c: In function ‘main’:\nplayground.c:14:28: error: ‘Bar’ undeclared (first use in this function)\n   14 | int main(void) { Foo foo = DYN(MyFoo, /* Foo */ Bar, \u0026(MyFoo){0}); }\n      |                            ^~~\nplayground.c:14:28: note: each undeclared identifier is reported only once for each function it appears in\nplayground.c:14:31: error: expected ‘)’ before ‘{’ token\n   14 | int main(void) { Foo foo = DYN(MyFoo, /* Foo */ Bar, \u0026(MyFoo){0}); }\n      |                            ~~~^\n      |                               )\n```\n\n----------\n\n#### Error: typo in `VTABLE`\n\n[`playground.c`]\n```c\n#define Foo_IFACE vfunc(void, foo, void)\ninterface(Foo);\n\ntypedef struct {\n    char dummy;\n} MyFoo;\n\nvoid MyFoo_foo(void) {}\n\nimpl(Foo, MyFoo);\n\nint main(void) { FooVTable foo = VTABLE(/* MyFoo */ MyBar, Foo); }\n```\n\n[`/bin/sh`]\n```\nplayground.c: In function ‘main’:\nplayground.c:14:34: error: ‘MyBar_Foo_impl’ undeclared (first use in this function); did you mean ‘MyFoo_Foo_impl’?\n   14 | int main(void) { FooVTable foo = VTABLE(/* MyFoo */ MyBar, Foo); }\n      |                                  ^~~~~~\n      |                                  MyFoo_Foo_impl\n```\n\n----------\n\nFrom my experience, nearly 95% of errors make sense.\n\nIf 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.\n\n### Q: What about IDE support?\n\n![Suggestion](images/suggestion.png)\n\nA: 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).\n\n### Q: Which compilers are tested?\n\nA: Interface99 is known to work on these compilers:\n\n - GCC\n - Clang\n - MSVC\n - TCC\n","funding_links":[],"categories":["C language extensions"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhirrolot%2Finterface99","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhirrolot%2Finterface99","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhirrolot%2Finterface99/lists"}