pax_global_header00006660000000000000000000000064144031335610014512gustar00rootroot0000000000000052 comment=c81db7f76c70cd4a7fb1a55ba783dde47ecd93a1 interface99-1.0.1/000077500000000000000000000000001440313356100136335ustar00rootroot00000000000000interface99-1.0.1/.clang-format000066400000000000000000000006031440313356100162050ustar00rootroot00000000000000Language: Cpp BasedOnStyle: LLVM IndentWidth: 4 ContinuationIndentWidth: 4 ColumnLimit: 100 AllowShortFunctionsOnASingleLine: Empty AllowAllArgumentsOnNextLine: false BinPackArguments: false AllowAllParametersOfDeclarationOnNextLine: true BinPackParameters: true AlignConsecutiveMacros: true AlignAfterOpenBracket: AlwaysBreak StatementMacros: ["ML99_EVAL", "vfunc", "vfuncDefault"] interface99-1.0.1/.github/000077500000000000000000000000001440313356100151735ustar00rootroot00000000000000interface99-1.0.1/.github/workflows/000077500000000000000000000000001440313356100172305ustar00rootroot00000000000000interface99-1.0.1/.github/workflows/c-cpp.yml000066400000000000000000000017411440313356100207600ustar00rootroot00000000000000name: C/C++ CI on: push: branches: [ master ] pull_request: branches: [ master ] jobs: test: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] include: - os: ubuntu-latest compiler: gcc - os: macos-latest compiler: clang - os: windows-latest compiler: msvc runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Test run: ./scripts/test-all.sh test-tcc: runs-on: ubuntu-latest env: CC: tcc steps: - uses: actions/checkout@v2 - name: Install TCC run: sudo apt install tcc - name: Test run: ./scripts/test-all.sh check-fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Download run-clang-format run: git submodule update --init run-clang-format - name: Check code formatting run: ./scripts/check-fmt.sh interface99-1.0.1/.gitignore000066400000000000000000000007121440313356100156230ustar00rootroot00000000000000# Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb # Kernel Module Compile Results *.mod* *.cmd .tmp_versions/ modules.order Module.symvers Mkfile.old dkms.conf # CMake build files build/ interface99-1.0.1/.gitmodules000066400000000000000000000001531440313356100160070ustar00rootroot00000000000000[submodule "run-clang-format"] path = run-clang-format url = https://github.com/Sarcasm/run-clang-format interface99-1.0.1/CHANGELOG.md000066400000000000000000000071311440313356100154460ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## unreleased ## 1.0.1 - 2023-03-11 ### Fixed - Fix the `DOWNLOAD_EXTRACT_TIMESTAMP` CMake warning (see [datatype99/issues/15](https://github.com/Hirrolot/datatype99/issues/15)). ## 1.0.0 - 2022-05-15 ### Added - `IFACE99_VERSION_COMPATIBLE` to check for a SemVer-compatible version. - `IFACE99_VERSION_EQ` to check for an exact version. ## 0.8.4 - 2022-03-17 ### Added - `DYN_LIT` that constructs interface objects out of compound literals. ## 0.8.3 - 2021-12-09 ### Fixed - Specify `C` as the project language in `CMakeLists.txt`. Previously, CMake detected C++ and required a C++ compiler to compile the project. - Require CMake v3.11.4. ## 0.8.2 - 2021-12-01 ### Added - Add the root `CMakeLists.txt` to be able to use CMake with [`FetchContent`] or [`add_subdirectory`]. [`FetchContent`]: https://cmake.org/cmake/help/latest/module/FetchContent.html [`add_subdirectory`]: https://cmake.org/cmake/help/latest/command/add_subdirectory.html ## 0.8.1 - 2021-11-23 ### Fixed - Programmatically require Metalang99 version compliant with 1.12.0. ## 0.8.0 - 2021-11-09 ### Added - `VCALL`, `VCALL_OBJ`, `VCALL_SUPER`, `VCALL_SUPER_OBJ` as a convenient syntax sugar to call virtual functions. - `VSelf` and `VSELF(T)` to imitate typed `self` parameters. ### Changed - `iMethod` => `vfunc` (consistent with `VTABLE`, `vptr`) [**BC**]. - `defaultIMethod` => `vfuncDefault` [**BC**]. - `externImpl` => `implExtern` [**BC**]. - `externDeclImpl` => `declImplExtern` [**BC**]. - `self` (`VSelf`) parameters are now `restrict`. ## 0.7.0 - 2021-10-06 ### Changed - `method` => `iMethod`, `defaultMethod` => `defaultIMethod` to avoid name clashes [**BC**]. ### Fixed - Previously, shortcuts referring to functional macros were object-like. To avoid nasty compilation errors, now they are function-like too: - `interface(iface)` - `impl(iface, implementer)` - `externImpl(iface, implementer)` - `declImpl(iface, implementer)` - `externDeclImpl(iface, implementer)` - `iMethod(ret_ty, name, ...)` - `defaultIMethod(ret_ty, name, ...)` - `DYN(implementer, iface, ...)` - `VTABLE(implementer, iface)` ## 0.6.0 - 2021-10-02 ### Added - Default method implementations. ### Changed - Use `method` and `defaultMethod` instead of [X-Macro] for the sake of conciseness [**BC**]. - `_INTERFACE` => `_IFACE` [**BC**]. ## 0.5.0 - 2021-09-16 ### Changed - Remove `implPrimary`, `externImplPrimary` [**BC**]. ## 0.4.0 - 2021-09-14 ### Changed - Use a variation of the [X-Macro] pattern instead of `iFn`. This change makes the implementation easier to reason about [**BC**]. [X-Macro]: https://en.wikipedia.org/wiki/X_Macro ## 0.3.0 - 2021-08-13 ### Changed - Generate virtual tables with static linkage in `impl(Primary)` & `declImpl` [**BC**]. - `dyn` => `DYN` to know where is a function and where is a macro. ### Added - Counterparts with external linkage: `externImpl(Primary)` & `externDeclImpl`. ## 0.2.0 - 2021-07-01 ### Added - Generate `typedef struct VTable VTable;` and `typedef struct ;` prior to their definitions to allow accepting `` and `` as interface function parameters. ### Changed - Generate `char dummy;` only for an empty virtual table (i.e., a marker interface without superinterfaces) [**BC**]. ## 0.1.0 - 2021-06-23 ### Added - This excellent project. interface99-1.0.1/CMakeLists.txt000066400000000000000000000011411440313356100163700ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.11.4) project(interface99 LANGUAGES C) # Fix the warnings about `DOWNLOAD_EXTRACT_TIMESTAMP` in newer CMake versions. if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") cmake_policy(SET CMP0135 NEW) endif() include(FetchContent) FetchContent_Declare( metalang99 URL https://github.com/Hirrolot/metalang99/archive/refs/tags/v1.13.2.tar.gz ) FetchContent_MakeAvailable(metalang99) add_library(${PROJECT_NAME} INTERFACE) target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(${PROJECT_NAME} INTERFACE metalang99) interface99-1.0.1/LICENSE000066400000000000000000000020561440313356100146430ustar00rootroot00000000000000MIT License Copyright (c) 2021-2023 Hirrolot Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. interface99-1.0.1/README.md000066400000000000000000000757371440313356100151350ustar00rootroot00000000000000

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); ```
Rectangle
Triangle
```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-limit If 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/) by Hirrolot. - [_What’s the Point of the C Preprocessor, Actually?_](https://hirrolot.github.io/posts/whats-the-point-of-the-c-preprocessor-actually.html) by Hirrolot. - [_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) by Hirrolot. - [_Extend Your Language, Don’t Alter It_](https://hirrolot.github.io/posts/extend-your-language-dont-alter-it.html) by Hirrolot. ## 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 interface99-1.0.1/examples/000077500000000000000000000000001440313356100154515ustar00rootroot00000000000000interface99-1.0.1/examples/.gitignore000066400000000000000000000000071440313356100174360ustar00rootroot00000000000000build/ interface99-1.0.1/examples/CMakeLists.txt000066400000000000000000000026071440313356100202160ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) project(examples LANGUAGES C) if(CMAKE_C_COMPILER_ID STREQUAL "GNU") add_compile_options(-Wall -Wextra -pedantic -ftrack-macro-expansion=0 -fsanitize=address) add_link_options(-fsanitize=address) elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang") add_compile_options(-fmacro-backtrace-limit=1 -fsanitize=address) add_link_options(-fsanitize=address) elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC") # Enable a standard-conforming C99/C11 preprocessor. add_compile_options("/std:c11") elseif(CMAKE_C_COMPILER_ID STREQUAL "TinyCC") add_compile_definitions(ML99_ALLOW_POOR_DIAGNOSTICS) endif() add_subdirectory(opaque_type) add_executable(shape shape.c) add_executable(tracing_vehicle tracing_vehicle.c) add_executable(airplane airplane.c) add_executable(read_write read_write.c) add_executable(read_write_both read_write_both.c) add_executable(marker marker.c) add_executable(default_impl default_impl.c) add_subdirectory(.. build) get_property( EXAMPLES DIRECTORY . PROPERTY BUILDSYSTEM_TARGETS) get_property( OPAQUE_TYPE_EXAMPLE DIRECTORY opaque_type PROPERTY BUILDSYSTEM_TARGETS) foreach(TARGET ${EXAMPLES};${OPAQUE_TYPE_EXAMPLE}) target_link_libraries(${TARGET} interface99) set_target_properties(${TARGET} PROPERTIES C_STANDARD 99 C_STANDARD_REQUIRED ON) endforeach() interface99-1.0.1/examples/airplane.c000066400000000000000000000037271440313356100174210ustar00rootroot00000000000000// This example demonstrates the usage of superinterfaces/interface requirements. #include #include #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); typedef struct { int x, y; } MyAirplane; void MyAirplane_log(MyAirplane *self, const char *command, int distance) { printf("%s(%d): x = %d, y = %d\n", command, distance, self->x, self->y); } void MyAirplane_move_forward(VSelf, int distance) { VSELF(MyAirplane); self->x += distance; MyAirplane_log(self, "move_forward", distance); } void MyAirplane_move_back(VSelf, int distance) { VSELF(MyAirplane); self->x -= distance; MyAirplane_log(self, "move_back", distance); } impl(Vehicle, MyAirplane); void MyAirplane_move_up(VSelf, int distance) { VSELF(MyAirplane); self->y += distance; MyAirplane_log(self, "move_up", distance); } void MyAirplane_move_down(VSelf, int distance) { VSELF(MyAirplane); self->y -= distance; MyAirplane_log(self, "move_down", distance); } impl(Airplane, MyAirplane); /* * Output: * move_forward(10): x = 10, y = 0 * move_back(3): x = 7, y = 0 * move_up(7): x = 7, y = 7 * move_down(11): x = 7, y = -4 */ int main(void) { 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); VCALL(my_airplane, move_up, 7); VCALL(my_airplane, move_down, 11); return 0; } interface99-1.0.1/examples/default_impl.c000066400000000000000000000031761440313356100202710ustar00rootroot00000000000000// This examples demonstrates default method implementations. #include #include #define Droid_IFACE \ vfunc(const char *, name, void) \ vfuncDefault(void, turn_on, Droid droid) interface(Droid); // The default implementation of `Droid::turn_on`. void Droid_turn_on(Droid droid) { // A default implementation can access other methods through an interface object. printf("Turning on %s...\n", droid.vptr->name()); } // C-3PO implementation // ============================================================ typedef struct { char dummy; } C_3PO; const char *C_3PO_name(void) { return "C-3PO"; } // Use the default implementation of `Droid::turn_on` automatically. impl(Droid, C_3PO); // R2-D2 implementation // ============================================================ typedef struct { char dummy; } R2_D2; const char *R2_D2_name(void) { return "R2-D2"; } void R2_D2_turn_on(Droid droid) { // A default implementation can be called from a custom one. Droid_turn_on(droid); puts("Waaaaoow!"); } // Use a custom implementation for `Droid::turn_on`. #define R2_D2_turn_on_CUSTOM () impl(Droid, R2_D2); // Test // ============================================================ /* * Output: * Turning on C-3PO... * Turning on R2-D2... * Waaaaoow! */ int main(void) { Droid c_3po = DYN_LIT(C_3PO, Droid, {0}); VCALL_OBJ(c_3po, turn_on); Droid r2_d2 = DYN_LIT(R2_D2, Droid, {0}); VCALL_OBJ(r2_d2, turn_on); return 0; } interface99-1.0.1/examples/marker.c000066400000000000000000000021251440313356100170760ustar00rootroot00000000000000// This examples demonstrates marker interfaces. #include #include #include #include #include // A marker interface whose implementers can be duplicated by simply copying bits. #define Copy_IFACE interface(Copy); typedef struct { unsigned health, victories, defeats; } PlayerStats; impl(Copy, PlayerStats); // This is not `Copy`able because of `.name`. typedef struct { const char *name; int id; PlayerStats stats; } Player; void test(Copy src, Copy dst, size_t size) { memcpy(src.self, dst.self, size); } int main(void) { const PlayerStats stats1 = {.health = 100, .victories = 5, .defeats = 2}; PlayerStats stats2; test(DYN(PlayerStats, Copy, &stats1), DYN(PlayerStats, Copy, &stats2), sizeof(PlayerStats)); assert(memcmp(&stats1, &stats2, sizeof(PlayerStats)) == 0); const Player p1 = {.name = "John", .id = 123, .stats = stats1}; Player p2; // COMPILE-TIME ERROR: // test(DYN(Player, Copy, &p1), DYN(Player, Copy, &p2), sizeof(Player)); (void)p1; (void)p2; return 0; } interface99-1.0.1/examples/opaque_type/000077500000000000000000000000001440313356100200045ustar00rootroot00000000000000interface99-1.0.1/examples/opaque_type/CMakeLists.txt000066400000000000000000000000621440313356100225420ustar00rootroot00000000000000add_executable(frog main.c frog.h frog.c croak.h) interface99-1.0.1/examples/opaque_type/croak.h000066400000000000000000000002631440313356100212550ustar00rootroot00000000000000#ifndef OPAQUE_TYPE_CROAK_H #define OPAQUE_TYPE_CROAK_H #include #define Croak_IFACE vfunc(void, croak, VSelf) interface(Croak); #endif // OPAQUE_TYPE_CROAK_H interface99-1.0.1/examples/opaque_type/frog.c000066400000000000000000000007641440313356100211140ustar00rootroot00000000000000#include "frog.h" #include #include #include struct Frog { const char *name; int ncroaks; }; Frog *Frog_new(const char *name) { Frog *self = malloc(sizeof *self); assert(self); self->name = name; self->ncroaks = 0; return self; } void Frog_free(Frog *self) { assert(self); free(self); } static void Frog_croak(VSelf) { VSELF(Frog); printf("%s: croak!\n", self->name); self->ncroaks++; } implExtern(Croak, Frog); interface99-1.0.1/examples/opaque_type/frog.h000066400000000000000000000003721440313356100211140ustar00rootroot00000000000000#ifndef OPAQUE_TYPE_FROG_H #define OPAQUE_TYPE_FROG_H #include #include "croak.h" typedef struct Frog Frog; Frog *Frog_new(const char *name); void Frog_free(Frog *self); declImplExtern(Croak, Frog); #endif // OPAQUE_TYPE_FROG_H interface99-1.0.1/examples/opaque_type/main.c000066400000000000000000000007741440313356100211040ustar00rootroot00000000000000// This example demonstrates how to implement interfaces for opaque types. // Note: you can export interface implementations outside of a TU for non-opaque types in the same // way. #include "croak.h" #include "frog.h" /* * Output: * Paul: croak! * Steve: croak! */ int main(void) { Frog *paul = Frog_new("Paul"); Frog *steve = Frog_new("Steve"); VCALL(DYN(Frog, Croak, paul), croak); VCALL(DYN(Frog, Croak, steve), croak); Frog_free(paul); Frog_free(steve); return 0; } interface99-1.0.1/examples/read_write.c000066400000000000000000000022061440313356100177420ustar00rootroot00000000000000// This examples demonstrates multiple interface inheritance. #include #include #include #include #define Reader_IFACE vfunc(size_t, read, VSelf, char *dest, size_t bytes_to_read) interface(Reader); #define Writer_IFACE vfunc(size_t, write, VSelf, const char *src, size_t bytes_to_write) interface(Writer); typedef struct { FILE *fp; } File; size_t File_read(VSelf, char *dest, size_t bytes_to_read) { VSELF(File); return fread(dest, 1, bytes_to_read, self->fp); } impl(Reader, File); size_t File_write(VSelf, const char *src, size_t bytes_to_write) { VSELF(File); return fwrite(src, 1, bytes_to_write, self->fp); } impl(Writer, File); /* * Output: * We have read: 'hello world' */ int main(void) { FILE *fp = tmpfile(); assert(fp); Writer w = DYN_LIT(File, Writer, {fp}); VCALL(w, write, "hello world", strlen("hello world")); rewind(fp); Reader r = DYN_LIT(File, Reader, {fp}); char hello_world[16] = {0}; VCALL(r, read, hello_world, strlen("hello world")); printf("We have read: '%s'\n", hello_world); fclose(fp); return 0; } interface99-1.0.1/examples/read_write_both.c000066400000000000000000000024621440313356100207620ustar00rootroot00000000000000// This examples demonstrates dynamic objects of multiple interfaces. #include #include #include #include #define Reader_IFACE vfunc(size_t, read, VSelf, char *dest, size_t bytes_to_read) interface(Reader); #define Writer_IFACE vfunc(size_t, write, VSelf, const char *src, size_t bytes_to_write) interface(Writer); #define ReadWriter_IFACE #define ReadWriter_EXTENDS (Reader, Writer) interface(ReadWriter); typedef struct { FILE *fp; } File; size_t File_read(VSelf, char *dest, size_t bytes_to_read) { VSELF(File); return fread(dest, 1, bytes_to_read, self->fp); } impl(Reader, File); size_t File_write(VSelf, const char *src, size_t bytes_to_write) { VSELF(File); return fwrite(src, 1, bytes_to_write, self->fp); } impl(Writer, File); // `Reader` and `Writer` are already implemented. impl(ReadWriter, File); /* * Output: * We have read: 'hello world' */ int main(void) { FILE *fp = tmpfile(); assert(fp); ReadWriter rw = DYN_LIT(File, ReadWriter, {fp}); VCALL_SUPER(rw, Writer, write, "hello world", strlen("hello world")); rewind(fp); char hello_world[16] = {0}; VCALL_SUPER(rw, Reader, read, hello_world, strlen("hello world")); printf("We have read: '%s'\n", hello_world); fclose(fp); return 0; } interface99-1.0.1/examples/shape.c000066400000000000000000000030061440313356100167140ustar00rootroot00000000000000// This examples demonstrates the basic usage of Interface99. #include #include // clang-format off #define Shape_IFACE \ vfunc( int, perim, const VSelf) \ vfunc(void, scale, VSelf, int factor) // clang-format on interface(Shape); // Rectangle implementation // ============================================================ 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); // Triangle implementation // ============================================================ 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 // ============================================================ void test(Shape shape) { printf("perim = %d\n", VCALL(shape, perim)); VCALL(shape, scale, 5); printf("perim = %d\n", VCALL(shape, perim)); } /* * Output: * perim = 24 * perim = 120 * perim = 60 * perim = 300 */ int main(void) { Shape r = DYN_LIT(Rectangle, Shape, {5, 7}); Shape t = DYN_LIT(Triangle, Shape, {10, 20, 30}); test(r); test(t); return 0; } interface99-1.0.1/examples/tracing_vehicle.c000066400000000000000000000027741440313356100207550ustar00rootroot00000000000000// This example demonstrates how to write interface decorators. #include #include #define Vehicle_IFACE \ vfunc(void, refuel, VSelf, int fuel) \ vfunc(void, drive, VSelf) interface(Vehicle); // Car implementation // ============================================================ typedef struct { int fuel; } Car; void Car_refuel(VSelf, int fuel) { VSELF(Car); self->fuel += fuel; } void Car_drive(VSelf) { VSELF(Car); self->fuel--; } impl(Vehicle, Car); // TracingVehicle implementation // ============================================================ typedef struct { Vehicle inner; } TracingVehicle; void TracingVehicle_refuel(VSelf, int fuel) { VSELF(TracingVehicle); printf("Vehicle.refuel(%d)\n", fuel); VCALL(self->inner, refuel, fuel); } void TracingVehicle_drive(VSelf) { VSELF(TracingVehicle); printf("Vehicle.drive()\n"); VCALL(self->inner, drive); } impl(Vehicle, TracingVehicle); // Test // ============================================================ void test(Vehicle vehicle) { VCALL(vehicle, refuel, 15); VCALL(vehicle, drive); } /* * Output: * Vehicle.refuel(15) * Vehicle.drive() */ int main(void) { Vehicle car = DYN_LIT(Car, Vehicle, {.fuel = 0}); Vehicle tracing_car = DYN_LIT(TracingVehicle, Vehicle, {.inner = car}); test(tracing_car); return 0; } interface99-1.0.1/images/000077500000000000000000000000001440313356100151005ustar00rootroot00000000000000interface99-1.0.1/images/suggestion.png000066400000000000000000000606111440313356100200010ustar00rootroot00000000000000PNG  IHDRX}-9bKGD pHYs  tIME 3%( IDATxy|\}43,ZFXeYe[d^ JIM I4MzS$Mo'ioHӤi4$!0`˶$}4},z5|Ķ^o_vB!U jd?[lܱeIu-q1\ɚ\fUM6M8WBwdB@I0ÅBT$/ψ@*6ZMk3$6Zt] P_JB &_ @Q~|>F35Mx''P))͠07#Aw^#'E:p:pB!Bɻ|ɻB B!>T]!B,!B B!$B!` !BH%B!B!}HIlUQ4*tZ5CTpyh襭_2O!K@ ?<,㰅>q.g P!`)Q,gs8os?[d&#B!MJI&6l״HNKBFE@Djw,4H^,^˔$.&Q.6u#'B,0oK6\nfgE0EyY8#ra+]}.K0Y8]n@!b*3A pp&(M$ټm4K ! @Eh8W( C.P!XhkXQtR*Yg!"<N 0Jb\ )\lB!$3ga8Slr87%yY(p<6u$܅BGz !B\cҀG!B,!B B!$B!` !BH%B!Sןu(Ri_gs%{,ӟɽi;}e_X1J^6v~ ~ ~s,I(S+ő?ׅ 4*60Tc*QiyOYC+yБy77䠏Sⷙu5Rw(~ܴ3f߿[ Vw6(QH/]z|x}'YAntBHucHIlUQ4ኪijlTJ`t~6Us V\MrӛTXCDo~@SABRI6:51 >U\4vy!V(溤SZyjccwA|* grdw0_=xImQnGtdkq$/t KJq"g8W5ơBJN>}қȥ(RLBZj|8{U:oYh:S%9h>lG9jlK)ܳtʱuktw[Y7ƁnC/Z܆xKG3ӵ?5wr)2O //h5:JաVSGq vʒL23U|}.G!VJIܹu=JRB%/;%>zz壷ޡ6zFtdDM,XIIeE +}SǓr`2{u5Ϳb{Yi?z.#c4>;%xbتsT#}Ct:Ű:Ԥ9>J]RֳdgeiwW:w\dkdo%iVt^lf 44\H]ks~,')S9Pw^C=1~clIXCJ~*> Đ2ߴ0@9kFQvz>7\$:ؖ*5jqkI!5k.Y>F~>,JAvKcN[^`fmD,gjsJT63b4@t^Μ8Ϋ}[OBtD/qz߮ƻh,&61mZ6ٰyj8V-,)ZS{ٓ)!ĵ.F Š\lv RɒL<#^zpC{ɍxl]=IO_kcPڹvquX%NE~v4S24cǩ?wM.nZL J*:ٴR=j 0T>Oձ9VZtB0tƔ*b ,Q(kB?aKF%'aبiht͸Ef\߁F8X>&طB=dnv?@} ^`g?72?T8#+x4]+Xϲ*_y{TjW~-` !M`aɥ9/MC[d 10}DoIZԁk]dFakVpz| o??} S|&5ݼWB Ȥy)5SZ@SbLTn"gߜG*B!BkLz !BH%B!B!XB!B,!B B!>d"#%UE!Ӭv'+PQmô|ð(-yiV@!ba(ψ{&re,VZudB!:ǤSDpܒYB!#U Ċ\lv RɒL<#^zH!Bds(9ict\oB!$3ga8Sӄƻ*IgF(XN*^hI ! LҢQɆ8='Lf!C&m>򢴔JF!`kd$B!$d !m&Y +ο)$H B!XB+!B,q#WpwhjjIEwO}t5\2[n.e5K_Tn 4Zʶ[su,Fj390YAJU H4'O7Y呓M!>sK, )* f;9\Q5 /%#%"ũFvIt9~_nWyk*oBRgaμpqQ|r- r !X S5ρ@ dKiorӨ @)d%joOŸ|<á^bn*&> m|8>/}{1TrGGR\ãEn51bc^VJ!:7)ߓEAfj5ycUQ2n[g3Mפ_ c7Q](+Т:=Pˁe.)n6C'{?u@Atn޶}C/q\M/cK؇WxM]wz}:t[$ey9+`}XlF*N7{aA6XrR(M]̆/Mu=f!P,ƪW/z{xu|1e+h>Mp!+rl7/'N_[Y~G]D/j_HG_Dmm#͓ũ|y.z$P{86-,7=o9>ϑ:ڒlăMAmW,`mIL.6nOOno$*ZI%=46ЕeAWu(P*G@NZ!Z@"TJʷέTZLnur| DvoYǮװlq u|+3]o g\78L^^fA1@G/_Åxj2s([-^`J#CJ$a2՛!K /#BwPa J*; {C"B9Pw 8:i>Pq V$_epd r !l b3VȈgy ^,^ud$Q5 zNGA\j^h'|Qo X@9Ʃuݓ+bk&By>wJS:F(*7[]JHOfqX`7o4>ō,U1՟hB1XLV;= [#B$35il0Pid;h>|Uaܿs<)}#=WQ5ÈFnCutdIBrW_۠8oJFJ|Pxmir !X1/.ZXuwć .tD~ R*Q| jrӕ ;k(դ0|O nƫ oo<;NuR {2}8Fl&ХMzߤ:zMϊlACMbJNT!%BD6h7( "#&njZM$.Gw وCBX•k|0o /CPQ'"-ަIeբ;1Y%w&?ـ7oHڼ%fNTu^$ıjq$=u\(i~|>PC!f/?,XVbs;]Je30ַpȌtYuMՑe!4/~US6_9G羪S\ȡtOüiKP|#s_A6񿾐4N)c-.}T8|&+D=HWNVei )+*-s%#}A%w;3-Sε%'/}>7V 6tf>OX!EE ҂lrғQGD0Zl4cwNe|cx}tQԁ? +Ox˺`g?>׫|y4rF-7v6:O1RK!K&-[sn_CDZig&wjs ?tȈߞMflG_毟o/` !O5` !D(i*B!B!&>ƖlȱkK#{i{ު$!DJB!$B!K1W%|;A@ Un):l*::>Qp/@)ħX{#BAl9>?ʗ NQ~VκK'?UJſn)Dw2qe_|KoTilNU߼oLZ_ >na=ϙCؚW9>{?YY6|c?>5aon0AŗYu=4o a'r=v^RޕΉ V! //;] mXUK}dosJr( @ J|즫; @ڐD}\,T(;K%P;5~|ieob߆~Sx+5~h 6FEqE*rr&DS)Ԥyۼ{ρ}^yA 7٦%s^۸MvP'^C$, ?5N &"$xy˪8՗hl 5uߐy5JAxJ!~3wNx4p>,!Z8T?-ҫϙ贍GZn)l9l2+ Лh`ޑĸ뀁i/H/g׃J.=;L+IJT0qt[dR9`T ,̊ЧPLFSlGz*+BTtY IDAT={ s0^M"{]$r}bt75 U$Y¢dʱtx^5_MzWl[cFnf+IHOs趾W~inKKjA *㻯Ruobq[-\S"wNr\_?)ɄK%$W~"3fq8\ q1Di}cfzJ8g2f>O )MXggjFAZbVkA akLte[=UC~4Y|c%W!İ #s8ϿIs wx|28E~|7ꩨ $`Qw'Qyl@ >2u-5OIݬ|Vvx"{_x|J lh^gʐd2Rw\T/dMunH\DyO}bW\hs[h QjˁAB KH%fRF3ǫ.a\|q=F6 [&YٙAl q$hk`hkAk>ޞ"uHZI~r3/{Ra~trX#OU d(;/Eq4 G ~:rHJzߺ8nd±P,"(q\|k){usO~ roGo :XIJNa-xϭˍX0_|;ƃț ZPzy[_6O6TܼUkQRO`^:GIok0IIN KH%;\;UlC`ܳ}#ϼt/dYRAAnNyՃeQ6EᵜȠ(DMmkH,MKgcXCyc+-%+ꟸӆJ]w)3w!HE$Mw_!4[1_Z+)X^"4UN*i"ҫ)򨨨!MXdrֿe4oL sld>5Hi9$Zkx]Gx% .:zF&q]^% c@b2K͝IG[s>)͎ilfo!]e ]qdGǑO4AG_OHI*&|}b_⤳KN|N)Y,U_t[pu%]"^v^zwCdJ˶f1-= ǖiQ^^,:r&6}Jb)S TE^3ύ͢/R$t)Yd޹,c/eˈ!ffz< ;Tk9=opW(+W7bb K\wRE@l\]lutbܞZ:BK1 [fmxqҲak<9nz~z_͸?B,c?=]$OXUZf3jȉb#Gpw>VOxxYC'%X FNW7^qψ㫒YcCo&hWȅ"DA̠ZP"%~c_YR$%P((m߾x0bcb ߅XBlxG7xRd^ 擜jmr R,?NXXQQd BX)))ebCWw799Z4!lbgyu \q^×KF#۷{o[cbP( #0*" e"H"K!n 477c2سgfvZ-iii$c0M(U aBnNto !ĵQAݫYs 4i=,-!/;^#-5ؽ۷p\GGG@ՐNEؾ};_?6UW u|o?IFFFB[JOO 1Bq[y o*P-<.(=Ɔdr㢉R g+ti$%jU qq|n 3UZM$Lb:ݵ3i^zKA$$B1!&)Uټ /?YF 0v@+fnlGsosu}7OnCtn5W RSѨ(` Utt.ՑW1Q֭*37wy}Hbo/7et^ PP'Z0HZDRo \l(im{olF ?v{ޱ ]WΈOxxgt{ÁJjwd%@zBHx(tMimIFA=_-xs3ΏPDbsr@ 6) ~'Jɣ܆FqMӟKvD|Ub|4_ڷ u:oW/&J͋ko^U;7fŢ\xqw\Hmp< ߐǚ{Ws'Q~ju}&lv<_.n"*N7'ٷw3%Taaag-ѹZkk#vlwX>V˰qRin2&o&EasB9xZШ#(K/,6ZÒtִ%IN¥NmZNb\opѲ(+eACVZ|h⠫og^ Ns43"ud03խ:Z?M 8עtjV.;+1vRŸ^=+/, >{S'Pi1կVO je:1!ԑ*2hjS~j-y8Nճzy.K 29uN[7rε(?GBNFy;`8x mg{v%<<e'0?FE-O;Qq,+@u`&A5kَᦶ>e'PL\f rRV%,aiA:G*.R~No[Jz`Uu0qb;I$9SJrݯiQ~[g}ffFG N?չ/8@ZQv.iD7UWH'5sb{5󙽷C0-ƴ||Nc$-Edr08d,>6HQ~禆C.}@sRP}As{?ͽ(/+̤c>vvhkz;( ^gG[ .49eެHa^}p+o-(? :BBGC$'7Rԃllupl#:5q.51h3`es%yԷQЅgΐZRgjwc88 2P*, [ic`< uav7]FZNO@>1dvVeqR-V!kt&ɉ$pXWS[?-aqmJO3խ.JcqM_?6?|~|ULR4eQyH-LeT.&n;N _{NV/ y/|6_c#^?frШU6tD.J,;dyQ?x!JKqصjjcӚB1ZV'4uL\cX.|"!?Ekojw3]P(u!758%;#KGBzuso@FZ<KzY.Ͽr"dgNjN7QږφU룉;ii\e:HO+ !,,G:Ba`Ȃ秧DVz}f212<\MkG R)CafZM$*G'Gbd~?uu6tOU}`I~: M!Šl1cdQ6+`8϶lOÁ@ rSi$5)62 uGp*B~z`&%Y|sC۽OWȲO]//cӆiobsjY.ηL-ExkKs_.5mqHf7)M̴NNzBafj\:]wC`tx;Fc;]m~|vSm]No/nfqn&'v(m$巕rB+/qK^v2 - - r\q;ۻ r8}e^tǶ՘m;xEJmn.kfjsUiRsY gvezTe'im!u~#>_[5tu[#ƫxܳ7{+Q()Mw8x\_Xcso*7Ձ!Ka,kZXۻ>`5SB^v ;e9hQ*'6=1.mIlDuId*X3_8}{32ψ9%!~ LukQ{ iiB/A ( ǫfe 59( =s* 9Wێbtttmx}4'EC&{dD"mf|W)Rg \_; 'sJhR9sDZr&ڻ ʙZmkb\op3H~N Mm}!6+$0Ԩ#j"|>ZN"TJߊ Y2-0x䁭o#0:oMxxXp|hb5X-dbeTW/__~UW Չs͜m#z*Qs*FGG`}5 !tA[ʊ(M O S]l 7}aWgn_ϊ,RcINaˆh5jXUd:Q<ֈ3!.IZVIVZ<ǪMme9DiHmB-.LW7`32*UϏ&3-!n,-qٲU$'ĐGip0622XXѬ^l_2 w-K/n!;=-R E%F9g{M5}JbXYCx6NB\TaT2f(6ۚGWUW:QZ:(+]NIkghOmC+搓D|m;sVV֬XDXX%yikYtrw5MJj^a룛]˻iv"ޣtt޺0c/t _럞y_=Gr,s0-9#LYF/{6ۻ訮ZT-HIh!{ IDAT1xi;'}Iɉg鞞L$LOw;;cK,Z.K^!(#$E>^zWU{G)*}#=/lǸv Kxeک<a s"u'&w.~v=5[{;Z:v;gk^:m up0KXdi1ccrmC;3rDuOvE9qV6e",fZ'ZӃ%ϝOVz<68\6>%5?I[S7/O6˃a Ia릱;[zTVLxh>夥&}n ͉C7| Wֶ0?y6&:|=ٸj3{6n6 r$k} Ϭ:@yU3 fgovKI?FT%r+RSz@?U)Q,L}[4{GjR4 RbyokNEDn~(no/:ՈuMc"]"" XwSj;PYUa&B[G5Lt WIetαU:"`\d3D&uc}. B:GEDD Y,_lZɚ?]}r3;-4't} ŜZ݇)戈~8(G9Urw7&يF,XdӃ||0qNG6.!-)?/N)WwԩD)<,x̜0O}SuMz']̢s(i:rRش& /t]xo=e%g7RxJiem)[VLxa3]=6^/4X&%!Dv/t{%h~<o|p߶}GKq8xMo߼{S%ZȐq}V-M%=)-~-d׾BڰCMJͼ?aI\FwfY=}43Fn }$s͜>[{p\U5AIvi28tHFzȧڏἥ7+,9D0jwRyC8ca4YHJB4t8x|o'\s=9YAEM9yrʪom܊cM` ͽ33B m"͜Jdx&bw݁*(`ݠ%DPy9 Y,Y:kr /O֮Hcvd(&nk?:V_EX?rRqs=eMX<ͤ&Da4),1q.q_sxxEmgk))oNjW~孽KhiV"˲qƱfy.(* XNLT(~ SZDAQ5O@씋shnfR<* =-,8k?gjNr\.ιf.N4|T%) $'DQTZ7<7Ds}hS]S Uu[Z ⨨iaAJ,F³u7'=yr/,`vYIK!rl"wI2sfi1mpA9Mݤ$FqoQI,Lw,=Y1a;rT-ɇa0SYsWڤ8~{~7vMG/yQBuunʡwaf4SÇ wo?LpAl50㪬ݓN.Kc$8~ ]{3+}G4V_XMdB&Vɟ9Wϙs%E|<?.{g 0Mk208BIy؅j6uS669}ܸ|}< MY>5-9N<=jd2bjIxHυ'ce6Iltȸ/tw=߾Ÿ[Uj%.b"ٺ)Vz{,{JM9VOOW݇ŃX<l\Ah?*: |hjk6Fr|NFy]&o}xxB"^Vv=6XVLTo@; 6t1OY5r10!GTն2uZ9ry{iۆ6DUm+Moh?zu$ kGUHAQ M 5#Gv:?ZJӅ.!;@\I1M&v/~NbsTz|}\ӹ&#8/}aYV!AxZk]7" gݜ6:vl}ӓ9Vʦ54v;0E$}^ 6e?vđQ?}uOnYΟ@k{t&0?^G?KG4T"NE4^nwji*~׵ʵV|=eVxXHXVvmi&#mι& ;DLT SX4om?{\w=?Oڰ>{Q"}!  rHeXŇcuYl\=Ӟp.:+=Ə.{W>zKom?Ӈ(`J6Db6٩x`pfʪan\Ąew8&tY!&2䚵`ǥ!ޜQ4vSeڐ 8gfiZs Cu+;G"fV0g+/tࢩnΔҳ  Bu݁>5OB/Աj׎dbVX5kUG=}^-4؏hS7PRȒ<-@).n "ru{U5a;x`m !%1 ,J"~v8ބ2`}Ƅed2;V^p)obf4+qB 4Aw`?w_ys# NiUvk3 (Зx"B]ibH 2<勒HKe#Tx{x"3;-7Y1`|Hᠻw`yIkG/sƚ?gYq[˷oef217JuHTD;\[X=6Zٸ*PI3Ԥ+sDB\fFfG0-f&ٻtSʼn35o(\N. /'G_`:0ë rRbx6*6e“1Li%,ɘ~䖱f.o>:SV` {a~&gRrRZ0xKi.K6ĕ*  GrqRZDPS".`eN [7eC3rxY( o/ >=TLe\.glQv"Q;BY4&:{l]4 _[hrXnK^R^L\t(OmY|]4LϾܿz![ ֒ F4I\u+Xd܁6@0~bٸz!WN4a$'s.~>t[#'OJzY y)md&le3l\Dfz<&v>U@WO \n%sp Mݺ D䞐Q|e'=ϳDĖW XYYqZ̃2pIZ %)~{OoOmsx>YFzkC#_?jה7(`D΋D\;=_D mZBK{NWMYqϛlp~%nZ|g klZ+mZ%?] @G/dq8^Ɠ[M"BYgç S/ XRem ką] "2F kxn[.!=GIyfeX<k?q x̀UTZϚeGvNԺky&'bD#;>:"_౉0OKn%DPy9 Oױ۝7w%"NW_XC86э0RSo?D}Ig?Ib,m&3-bZ揟^˧Jx_008BFl^xbuȨ*^QBgxNltu{@D)N%u.̧{J()odr\Q;)>Hu]/<`ra ?0Db,' <߬_g\s{3ѡ44Jƙz`&>;X6ډUԶau=`nw044=H$~'[8rL`if!5?^_1oK3 #)>Mk2b2;6DTDS&}.D2;2?/̦+gx~k.IQŋOS73c~FF=~d2*{şla׾"MxY8鴴#Է_u{Cãܗ7tq߼{p:+0ܗ'}wwzzw7dMxgΓ^Os} GewsMw`r#_uy,HeiV"_WHW䞖Ń3q8][ H)QA^JCs'{Oڤ'"wOןiU "ʪ[J-9“)l7xK'ID뫉 $;EDf²zΜk inAZ;cSD& #(f]"rOr\k:rbuϡaMd*""" X_=}) "3ZM(`".":u DDDDn. s+"2tDn`(`(`(`(`(`(`(`(`(`(`]&({Y~<;De%#"""yПy1p8^d4Tɋj609:{l_FKDDDnć{ (( /?o\e>7@_:m?RGJu[7e|9;FeD22j|Cgqa|DEǻ;s޽9,^0`_zz` NU+a6ji#vO !h4rx(>Wϛq+%͙.%.&a;*aQy_SB[`^,ؚb2fGb`vts@xȨie͊|(ͭݜ(YI|Քghxb[g^zv<a1v۶o?k¾FooR~a|=E ""N={g͝6^+xc^ql{Y̌: ˱S_DyM >$ cod`p0  p/ўwY)jb"V`0ji o|:mlSX}&k^N*=ohl颼+DDD[H ۧ_O}O2nYh?!/6MWo=` 't˛[;NFFx{yȚ7\ <&sZZǽ^]S;^xY ̟_` ߇̈́Qml׻@DDDkz&W˨~lO/?~9)<2,Ot/v{p)ɱ7 ?θufQRg:0n޾k˲(`|!z tpl{XfSR@Y'/(jڦ|Ԅhp D?VlT(=6F:BmfhMܯs_/=>>,Oɇ7Ėek0݁L&#}zsb1˃Z>aHn!,؏9lZɾce sda= }}+WD6 rnK,,*KDdS,C5X"""" X"""" X"""" X""""rN}IDATʑiSDDDdQQQ,,,QQQ,,,,QQQ,,,QQQ,,,,QQQ,,,QQQ1em2`ɞIENDB`interface99-1.0.1/interface99.h000066400000000000000000000354661440313356100161440ustar00rootroot00000000000000/* MIT License Copyright (c) 2021-2023 Hirrolot Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // The official repository: . #ifndef INTERFACE99_H #define INTERFACE99_H #include #if !ML99_VERSION_COMPATIBLE(1, 13, 2) #error Please, update Metalang99 to v1.13.2 or later. #endif #ifndef IFACE99_NO_ALIASES #define interface(iface) interface99(iface) #define impl(iface, implementer) impl99(iface, implementer) #define implExtern(iface, implementer) implExtern99(iface, implementer) #define declImpl(iface, implementer) declImpl99(iface, implementer) #define declImplExtern(iface, implementer) declImplExtern99(iface, implementer) #define vfunc(ret_ty, name, ...) vfunc99(ret_ty, name, __VA_ARGS__) #define vfuncDefault(ret_ty, name, ...) vfuncDefault99(ret_ty, name, __VA_ARGS__) #define VCALL(obj, ...) VCALL99(obj, __VA_ARGS__) #define VCALL_OBJ(obj, ...) VCALL_OBJ99(obj, __VA_ARGS__) #define VCALL_SUPER(obj, superiface, ...) VCALL_SUPER99(obj, superiface, __VA_ARGS__) #define VCALL_SUPER_OBJ(obj, superiface, ...) VCALL_SUPER_OBJ99(obj, superiface, __VA_ARGS__) #define DYN(implementer, iface, ...) DYN99(implementer, iface, __VA_ARGS__) #define DYN_LIT(implementer, iface, ...) DYN_LIT99(implementer, iface, __VA_ARGS__) #define VTABLE(implementer, iface) VTABLE99(implementer, iface) #define VSelf VSelf99 #define VSELF(T) VSELF99(T) #endif // IFACE99_NO_ALIASES // Public stuff { // Metalang99-compliant macros { #define IFACE99_interface(iface) ML99_call(IFACE99_interface, iface) #define IFACE99_impl(iface, implementer) ML99_call(IFACE99_impl, iface, implementer) #define IFACE99_implExtern(iface, implementer) ML99_call(IFACE99_implExtern, iface, implementer) #define interface99(iface) ML99_EVAL(IFACE99_interface_IMPL(iface)) #define impl99(iface, implementer) ML99_EVAL(IFACE99_impl_IMPL(iface, implementer)) #define implExtern99(iface, implementer) ML99_EVAL(IFACE99_implExtern_IMPL(iface, implementer)) // } (Metalang99-compliant macros) #define vfunc99(ret_ty, name, ...) ML99_CHOICE(vfunc, ret_ty, name, __VA_ARGS__) #define vfuncDefault99(ret_ty, name, ...) ML99_CHOICE(vfuncDefault, ret_ty, name, __VA_ARGS__) #define DYN99(implementer, iface, ...) \ ((iface){.self = (void *)(__VA_ARGS__), .vptr = &VTABLE99(implementer, iface)}) #define DYN_LIT99(implementer, iface, ...) DYN99(implementer, iface, &(implementer)__VA_ARGS__) #define VTABLE99(implementer, iface) ML99_CAT4(implementer, _, iface, _impl) #define VSelf99 void *restrict iface99_self // clang-format off #define VSELF99(T) T *restrict self = (T *restrict)(iface99_self) // clang-format on #define IFACE99_MAJOR 1 #define IFACE99_MINOR 0 #define IFACE99_PATCH 1 #define IFACE99_VERSION_COMPATIBLE(x, y, z) \ (IFACE99_MAJOR == (x) && \ ((IFACE99_MINOR == (y) && IFACE99_PATCH >= (z)) || (IFACE99_MINOR > (y)))) #define IFACE99_VERSION_EQ(x, y, z) \ (IFACE99_MAJOR == (x) && IFACE99_MINOR == (y) && IFACE99_PATCH == (z)) // } (Public stuff) // Interface generation { #define IFACE99_interface_IMPL(iface) \ ML99_TERMS( \ v(typedef struct iface##VTable iface##VTable;), \ v(typedef struct iface iface;), \ ML99_semicoloned(ML99_struct(v(iface##VTable), IFACE99_PRIV_genVTableFields(iface))), \ v(struct iface { \ void *self; \ const iface##VTable *vptr; \ })) /* * // Only if is a marker interface without superinterfaces: * char dummy; * * 0 (*0)(0); * ... * N (*N)(N); * * const 0VTable *; * ... * const NVTable *; */ #define IFACE99_PRIV_genVTableFields(iface) \ ML99_uncomma(ML99_QUOTE( \ IFACE99_PRIV_genDummy(iface), \ ML99_IF( \ IFACE99_PRIV_IS_MARKER_IFACE(iface), \ ML99_empty(), \ IFACE99_PRIV_genFuncPtrForEach(iface)), \ ML99_IF( \ IFACE99_PRIV_IS_SUB_IFACE(iface), \ IFACE99_PRIV_genRequirementForEach(iface), \ ML99_empty()))) #define IFACE99_PRIV_genDummy(iface) \ ML99_IF(IFACE99_PRIV_IS_EMPTY_VTABLE(iface), v(char dummy;), ML99_empty()) /* * 0 (*0)(0); * ... * N (*N)(N); */ #define IFACE99_PRIV_genFuncPtrForEach(iface) \ ML99_seqForEach(v(IFACE99_PRIV_genFuncPtr), v(iface##_IFACE)) #define IFACE99_PRIV_genFuncPtr_IMPL(_tag, ret_ty, name, ...) v(ret_ty (*name)(__VA_ARGS__);) /* * const 0VTable *; * ... * const NVTable *; */ #define IFACE99_PRIV_genRequirementForEach(iface) \ ML99_tupleForEach(v(IFACE99_PRIV_genRequirement), v(iface##_EXTENDS)) #define IFACE99_PRIV_genRequirement_IMPL(requirement) v(const requirement##VTable *requirement;) // } (Interface generation) // Interface implementation generation { #define IFACE99_impl_IMPL(iface, implementer) \ IFACE99_PRIV_implCommon(IFACE99_PRIV_STORAGE_CLASS_STATIC, iface, implementer) #define IFACE99_implExtern_IMPL(iface, implementer) \ IFACE99_PRIV_implCommon(IFACE99_PRIV_STORAGE_CLASS_EXTERN, iface, implementer) #define IFACE99_PRIV_STORAGE_CLASS_STATIC static #define IFACE99_PRIV_STORAGE_CLASS_EXTERN /* If no storage-class specifier is provided, the \ default for objects is `extern` (file scope) or \ `auto` (block scope). */ #define IFACE99_PRIV_implCommon(storage_class, iface, implementer) \ ML99_assignInitializerList( \ v(storage_class const iface##VTable VTABLE99(implementer, iface)), \ IFACE99_PRIV_genImplInitList(iface, implementer)) /* * // 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), */ #define IFACE99_PRIV_genImplInitList(iface, implementer) \ ML99_uncomma(ML99_QUOTE( \ IFACE99_PRIV_genDummyInit(iface), \ ML99_IF( \ IFACE99_PRIV_IS_MARKER_IFACE(iface), \ ML99_empty(), \ IFACE99_PRIV_genImplFuncNameForEach(iface, implementer)), \ ML99_IF( \ IFACE99_PRIV_IS_SUB_IFACE(iface), \ IFACE99_PRIV_genRequirementsImplForEach(iface, implementer), \ ML99_empty()))) #define IFACE99_PRIV_genDummyInit(iface) \ ML99_IF(IFACE99_PRIV_IS_EMPTY_VTABLE(iface), v(.dummy = '\0'), ML99_empty()) /* * 0 = either _0 or _0, * ... * N = either _N or _N, */ #define IFACE99_PRIV_genImplFuncNameForEach(iface, implementer) \ ML99_seqForEach( \ ML99_appl(v(IFACE99_PRIV_genImplFuncName), v(iface, implementer)), \ v(iface##_IFACE)) #define IFACE99_PRIV_genImplFuncName_IMPL(iface, implementer, tag, _ret_ty, name, ...) \ ML99_match(ML99_choice(v(tag), v(iface, implementer, name)), v(IFACE99_PRIV_genImpl_)) #define IFACE99_PRIV_genImpl_vfunc_IMPL(_iface, implementer, name) v(.name = implementer##_##name, ) #define IFACE99_PRIV_genImpl_vfuncDefault_IMPL(iface, implementer, name) \ ML99_IF( \ IFACE99_PRIV_IS_CUSTOM(implementer, name), \ IFACE99_PRIV_genImpl_vfunc_IMPL(~, implementer, name), \ v(.name = iface##_##name, )) /* * 0 = &VTABLE(, 0), * ... * N = &VTABLE(, N), */ #define IFACE99_PRIV_genRequirementsImplForEach(iface, implementer) \ ML99_tupleForEach( \ ML99_appl(v(IFACE99_PRIV_genRequirementImpl), v(implementer)), \ v(iface##_EXTENDS)) #define IFACE99_PRIV_genRequirementImpl_IMPL(implementer, requirement) \ v(.requirement = &VTABLE99(implementer, requirement), ) // } (Interface implementation generation) // Implementation declaration { #define declImpl99(iface, implementer) static IFACE99_PRIV_DECL_IMPL_COMMON(iface, implementer) #define declImplExtern99(iface, implementer) \ extern IFACE99_PRIV_DECL_IMPL_COMMON(iface, implementer) #define IFACE99_PRIV_DECL_IMPL_COMMON(iface, implementer) \ const ML99_CAT(iface, VTable) VTABLE99(implementer, iface) // } (Implementation declaration) // Virtual calls { #define VCALL99(obj, ...) \ ((obj).vptr->IFACE99_PRIV_VCALL_OVERLOAD(__VA_ARGS__)((obj).self, __VA_ARGS__)) #define VCALL_OBJ99(obj, ...) \ ((obj).vptr->IFACE99_PRIV_VCALL_OVERLOAD(__VA_ARGS__)((obj), __VA_ARGS__)) #define VCALL_SUPER99(obj, superiface, ...) \ ((obj).vptr->superiface->IFACE99_PRIV_VCALL_OVERLOAD(__VA_ARGS__)((obj).self, __VA_ARGS__)) #define VCALL_SUPER_OBJ99(obj, superiface, ...) \ ((obj).vptr->superiface->IFACE99_PRIV_VCALL_OVERLOAD(__VA_ARGS__)( \ ((superiface){ \ .self = (obj).self, \ .vptr = (obj).vptr->superiface, \ }), \ __VA_ARGS__)) #define IFACE99_PRIV_VCALL_OVERLOAD(...) \ ML99_CAT(IFACE99_PRIV_VCALL_, ML99_VARIADICS_IS_SINGLE(__VA_ARGS__)) #define IFACE99_PRIV_VCALL_1(obj, func_name) func_name(obj) #define IFACE99_PRIV_VCALL_0(obj, func_name, ...) func_name(obj, __VA_ARGS__) // } (Virtual calls) // Various predicates { #define IFACE99_PRIV_IS_EMPTY_VTABLE(iface) \ ML99_AND(IFACE99_PRIV_IS_MARKER_IFACE(iface), ML99_NOT(IFACE99_PRIV_IS_SUB_IFACE(iface))) #define IFACE99_PRIV_IS_MARKER_IFACE(iface) ML99_SEQ_IS_EMPTY(iface##_IFACE) #define IFACE99_PRIV_IS_SUB_IFACE(iface) ML99_IS_TUPLE(iface##_EXTENDS) #define IFACE99_PRIV_IS_CUSTOM(implementer, func_name) \ ML99_IS_TUPLE(implementer##_##func_name##_CUSTOM) // } (Various predicates) // Arity specifiers { #define IFACE99_PRIV_genRequirement_ARITY 1 #define IFACE99_PRIV_genRequirementImpl_ARITY 2 #define IFACE99_PRIV_genFuncPtr_ARITY 1 #define IFACE99_PRIV_genImplFuncName_ARITY 2 // Public: #define IFACE99_interface_ARITY 1 #define IFACE99_impl_ARITY 2 #define IFACE99_implExtern_ARITY 2 // } (Arity specifiers) #endif // INTERFACE99_H interface99-1.0.1/run-clang-format/000077500000000000000000000000001440313356100170075ustar00rootroot00000000000000interface99-1.0.1/scripts/000077500000000000000000000000001440313356100153225ustar00rootroot00000000000000interface99-1.0.1/scripts/check-fmt.sh000077500000000000000000000002251440313356100175210ustar00rootroot00000000000000#!/bin/bash ./run-clang-format/run-clang-format.py \ --exclude examples/build \ --exclude tests/build \ -r interface99.h tests examples interface99-1.0.1/scripts/fmt.sh000077500000000000000000000002761440313356100164540ustar00rootroot00000000000000#!/bin/bash find tests examples \ \( -path examples/build -o -path tests/build \) -prune -false -o \ \( -iname "*.h" \) -or \( -iname "*.c" \) | xargs clang-format -i interface99.h interface99-1.0.1/scripts/test-all.sh000077500000000000000000000001021440313356100173770ustar00rootroot00000000000000#!/bin/bash set -e ./scripts/test.sh ./scripts/test-examples.sh interface99-1.0.1/scripts/test-examples.sh000077500000000000000000000006561440313356100204630ustar00rootroot00000000000000#!/bin/bash set -e mkdir -p examples/build cd examples/build cmake .. cmake --build . run_example() { echo "executing ./$1 ..." ./$1 } if [[ "$OSTYPE" == "linux-gnu" ]]; then run_example "shape" run_example "tracing_vehicle" run_example "airplane" run_example "read_write" run_example "read_write_both" run_example "marker" run_example "default_impl" run_example "opaque_type/frog" fi interface99-1.0.1/scripts/test.sh000077500000000000000000000006251440313356100166430ustar00rootroot00000000000000#!/bin/bash set -e mkdir -p tests/build cd tests/build cmake .. cmake --build . run_test() { echo "executing ./$1 ..." ./$1 } if [[ "$OSTYPE" == "linux-gnu" ]]; then run_test "basic_tests" run_test "decl_impl" run_test "default_impl" run_test "metalang99_compliant" run_test "superinterfaces" run_test "vcalls" run_test "version" run_test "extern_impl/test" fi interface99-1.0.1/tests/000077500000000000000000000000001440313356100147755ustar00rootroot00000000000000interface99-1.0.1/tests/.gitignore000066400000000000000000000000071440313356100167620ustar00rootroot00000000000000build/ interface99-1.0.1/tests/CMakeLists.txt000066400000000000000000000027201440313356100175360ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) project(tests LANGUAGES C) if(CMAKE_C_COMPILER_ID STREQUAL "GNU") add_compile_options(-Wall -Wextra -pedantic -ftrack-macro-expansion=0 -fsanitize=address) add_link_options(-fsanitize=address) elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang") add_compile_options(-fmacro-backtrace-limit=1 -fsanitize=address) add_link_options(-fsanitize=address) elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC") # Enable a standard-conforming C99/C11 preprocessor. add_compile_options("/std:c11") elseif(CMAKE_C_COMPILER_ID STREQUAL "TinyCC") add_compile_definitions(ML99_ALLOW_POOR_DIAGNOSTICS) endif() add_subdirectory(extern_impl) add_executable(basic_tests basic_tests.c common.h common.c util.h) add_executable(vcalls vcalls.c) add_executable(decl_impl decl_impl.c common.h common.c) add_executable(metalang99_compliant metalang99_compliant.c) add_executable(superinterfaces superinterfaces.c common.h common.c util.h) add_executable(default_impl default_impl.c) add_executable(version version.c) add_subdirectory(.. build) get_property( TESTS DIRECTORY . PROPERTY BUILDSYSTEM_TARGETS) get_property( EXTERN_IMPL_TESTS DIRECTORY extern_impl PROPERTY BUILDSYSTEM_TARGETS) foreach(TARGET ${TESTS};${EXTERN_IMPL_TESTS}) target_link_libraries(${TARGET} interface99) set_target_properties(${TARGET} PROPERTIES C_STANDARD 99 C_STANDARD_REQUIRED ON) endforeach() interface99-1.0.1/tests/basic_tests.c000066400000000000000000000055571440313356100174600ustar00rootroot00000000000000#include #include "common.h" #include "util.h" #include #include #include // Ensure that forward declarations are generated. #define TestForwardDecl_IFACE vfunc(void, abc, Foo self, FooVTable vtable) interface(TestForwardDecl); // Implementations { typedef struct { char dummy; } MarkerImpl; impl(Marker, MarkerImpl); typedef struct { char dummy; } FooImpl; #define FooImpl_foo foo1_impl impl(Foo, FooImpl); typedef struct { char dummy; } BarImpl1; #define BarImpl1_foo foo1_impl #define BarImpl1_bar bar1_impl impl(Bar, BarImpl1); // Ensure that an interface can be implemented by many types. typedef struct { char dummy; } BarImpl2; #define BarImpl2_foo foo2_impl #define BarImpl2_bar bar2_impl impl(Bar, BarImpl2); // Ensure that a type can implement multiple interfaces. typedef struct { char dummy; } FooBarImpl; #define FooBarImpl_foo foo1_impl impl(Foo, FooBarImpl); #undef FooBarImpl_foo #define FooBarImpl_foo foo1_impl #define FooBarImpl_bar bar1_impl impl(Bar, FooBarImpl); #undef FooBarImpl_foo #undef FooBarImpl_bar typedef struct { int x; long long d; const char *str; } TestCompoundLit; #define TestCompoundLit_foo foo1_impl impl(Foo, TestCompoundLit); // } (Implementations) int main(void) { // Ensure `interface`-generated data. { ENSURE_VTABLE_FIELD_TYPE(MarkerVTable, dummy, char); ENSURE_DYN_OBJ_TYPE(Marker); ENSURE_VTABLE_FIELD_TYPE(FooVTable, foo, FooOpType); ENSURE_DYN_OBJ_TYPE(Foo); ENSURE_VTABLE_FIELD_TYPE(BarVTable, foo, FooOpType); ENSURE_VTABLE_FIELD_TYPE(BarVTable, bar, BarOpType); ENSURE_DYN_OBJ_TYPE(Bar); } // Ensure `impl`-generated data. { assert(VTABLE(MarkerImpl, Marker).dummy == '\0'); assert(VTABLE(FooImpl, Foo).foo == foo1_impl); assert(VTABLE(BarImpl1, Bar).foo == foo1_impl); assert(VTABLE(BarImpl1, Bar).bar == bar1_impl); assert(VTABLE(BarImpl2, Bar).foo == foo2_impl); assert(VTABLE(BarImpl2, Bar).bar == bar2_impl); assert(VTABLE(FooBarImpl, Foo).foo == foo1_impl); assert(VTABLE(FooBarImpl, Bar).foo == foo1_impl); assert(VTABLE(FooBarImpl, Bar).bar == bar1_impl); } ENSURE_DYN_OBJ(MarkerImpl, Marker); ENSURE_DYN_OBJ(FooImpl, Foo); ENSURE_DYN_OBJ(BarImpl1, Bar); ENSURE_DYN_OBJ(BarImpl2, Bar); ENSURE_DYN_OBJ(FooBarImpl, Foo); ENSURE_DYN_OBJ(FooBarImpl, Bar); // Test compound literals with `DYN_LIT`. { Foo compound = DYN_LIT(TestCompoundLit, Foo, {.x = 123, .d = 15, .str = "abc"}); assert(compound.vptr == &VTABLE(TestCompoundLit, Foo)); assert(((TestCompoundLit *)compound.self)->x == 123); assert(((TestCompoundLit *)compound.self)->d == 15); assert(strcmp(((TestCompoundLit *)compound.self)->str, "abc") == 0); } return 0; } interface99-1.0.1/tests/common.c000066400000000000000000000005231440313356100164310ustar00rootroot00000000000000#include "common.h" const char *foo1_impl(int x, double *restrict y) { (void)x; (void)y; return (const char *)NULL; } float bar1_impl(long long x) { (void)x; return 12.141f; } const char *foo2_impl(int x, double *restrict y) { return foo1_impl(x, y); } float bar2_impl(long long x) { return bar1_impl(x); } interface99-1.0.1/tests/common.h000066400000000000000000000015061440313356100164400ustar00rootroot00000000000000#ifndef INTERFACE99_TESTS_COMMON_H #define INTERFACE99_TESTS_COMMON_H #include #include // Interfaces { #define Marker_IFACE #define Foo_IFACE vfunc(const char *, foo, int x, double *restrict y) #define Bar_IFACE \ vfunc(const char *, foo, int x, double *restrict y) \ vfunc(float, bar, long long x) interface(Marker); interface(Foo); interface(Bar); // } (Interfaces) const char *foo1_impl(int x, double *restrict y); float bar1_impl(long long x); const char *foo2_impl(int x, double *restrict y); float bar2_impl(long long x); typedef const char *(*FooOpType)(int x, double *restrict y); typedef float (*BarOpType)(long long x); #endif // INTERFACE99_TESTS_COMMON_H interface99-1.0.1/tests/decl_impl.c000066400000000000000000000007731440313356100171000ustar00rootroot00000000000000#include #include "common.h" #include typedef struct { char dummy; } BarImpl; declImpl(Bar, BarImpl); static void test_decl_impl(void) { assert(VTABLE(BarImpl, Bar).foo == foo1_impl); assert(VTABLE(BarImpl, Bar).bar == bar1_impl); } #define BarImpl_foo foo1_impl #define BarImpl_bar bar1_impl impl(Bar, BarImpl); // Multiple declarations should work fine. declImpl(Bar, BarImpl); declImpl(Bar, BarImpl); int main(void) { test_decl_impl(); return 0; } interface99-1.0.1/tests/default_impl.c000066400000000000000000000020121440313356100176010ustar00rootroot00000000000000#include #include #define TestDefault_IFACE vfuncDefault(void, default_op, VSelf, int x) interface(TestDefault); static void TestDefault_default_op(VSelf, int x) { VSELF(void); (void)self; (void)x; } static void custom_impl(VSelf, int x) { VSELF(void); (void)self; (void)x; } typedef struct { char dummy; } A; impl(TestDefault, A); typedef struct { char dummy; } B; #define B_default_op_CUSTOM () #define B_default_op custom_impl impl(TestDefault, B); #define TestNoOpCustom_IFACE vfunc(void, custom_op, VSelf, int x) interface(TestNoOpCustom); typedef struct { char dummy; } C; // This `*_CUSTOM` attribute must be no-op. #define C_custom_op_CUSTOM () #define C_custom_op custom_impl impl(TestNoOpCustom, C); int main(void) { assert(VTABLE(A, TestDefault).default_op == TestDefault_default_op); assert(VTABLE(B, TestDefault).default_op == custom_impl); assert(VTABLE(C, TestNoOpCustom).custom_op == custom_impl); return 0; } interface99-1.0.1/tests/extern_impl/000077500000000000000000000000001440313356100173235ustar00rootroot00000000000000interface99-1.0.1/tests/extern_impl/CMakeLists.txt000066400000000000000000000001151440313356100220600ustar00rootroot00000000000000add_executable(test test.c impl.c types.h ../common.h ../common.c ../util.h) interface99-1.0.1/tests/extern_impl/impl.c000066400000000000000000000003241440313356100204270ustar00rootroot00000000000000#include #include "../common.h" #include "types.h" #define FooImpl_foo foo1_impl implExtern(Foo, FooImpl); #define BarImpl_foo foo1_impl #define BarImpl_bar bar1_impl implExtern(Bar, BarImpl); interface99-1.0.1/tests/extern_impl/test.c000066400000000000000000000007731440313356100204550ustar00rootroot00000000000000#include #include "../common.h" #include "../util.h" #include "types.h" #include declImplExtern(Foo, FooImpl); // Multiple declarations shoud work fine. declImplExtern(Bar, BarImpl); declImplExtern(Bar, BarImpl); int main(void) { // Ensure `impl`-generated data. { assert(VTABLE(FooImpl, Foo).foo == foo1_impl); assert(VTABLE(BarImpl, Bar).foo == foo1_impl); } ENSURE_DYN_OBJ(FooImpl, Foo); ENSURE_DYN_OBJ(BarImpl, Bar); return 0; } interface99-1.0.1/tests/extern_impl/types.h000066400000000000000000000003471440313356100206440ustar00rootroot00000000000000#ifndef INTERFACE99_TESTS_EXTERN_IMPL_TYPES_H #define INTERFACE99_TESTS_EXTERN_IMPL_TYPES_H typedef struct { char dummy; } FooImpl; typedef struct { char dummy; } BarImpl; #endif // INTERFACE99_TESTS_EXTERN_IMPL_TYPES_H interface99-1.0.1/tests/metalang99_compliant.c000066400000000000000000000017201440313356100211610ustar00rootroot00000000000000#include #include "util.h" #include #include #include ML99_ASSERT_UNEVAL(IFACE99_interface_ARITY == 1); ML99_ASSERT_UNEVAL(IFACE99_impl_ARITY == 2); ML99_ASSERT_UNEVAL(IFACE99_implExtern_ARITY == 2); #define Foo_IFACE vfunc(const char *, foo, int x, double *restrict y) ML99_EVAL(IFACE99_interface(v(Foo))); // Implementations { static const char *foo_impl(int x, double *restrict y) { (void)x; (void)y; return (const char *)NULL; } typedef struct { char dummy; } A; #define A_foo foo_impl ML99_EVAL(IFACE99_impl(v(Foo), v(A))); typedef struct { char dummy; } B; #define B_foo foo_impl ML99_EVAL(IFACE99_implExtern(v(Foo), v(B))); // } (Implementations) int main(void) { // Ensure `impl`-generated data. { assert(VTABLE(A, Foo).foo == foo_impl); assert(VTABLE(B, Foo).foo == foo_impl); } ENSURE_DYN_OBJ(A, Foo); ENSURE_DYN_OBJ(B, Foo); return 0; } interface99-1.0.1/tests/superinterfaces.c000066400000000000000000000053431440313356100203500ustar00rootroot00000000000000#include #include "common.h" #include "util.h" #include // Interfaces { #define FooExtendsMarker_IFACE Foo_IFACE #define FooExtendsMarker_EXTENDS (Marker) #define FooExtendsMany_IFACE Foo_IFACE #define FooExtendsMany_EXTENDS (Foo, Bar) #define MarkerExtendsMarker_IFACE Marker_IFACE #define MarkerExtendsMarker_EXTENDS (Marker) #define MarkerExtendsMany_IFACE Marker_IFACE #define MarkerExtendsMany_EXTENDS (Foo, Bar) interface(FooExtendsMarker); interface(FooExtendsMany); interface(MarkerExtendsMarker); interface(MarkerExtendsMany); // } (Interfaces) // FooExtendsMarker { typedef struct { char dummy; } A; impl(Marker, A); #define A_foo foo1_impl impl(FooExtendsMarker, A); // } (FooExtendsMarker) // FooExtendsMany { typedef struct { char dummy; } B; #define B_foo foo1_impl impl(Foo, B); #undef B_foo #define B_foo foo1_impl #define B_bar bar1_impl impl(Bar, B); #undef B_foo #undef B_bar #define B_foo foo1_impl impl(FooExtendsMany, B); #undef B_foo // } (FooExtendsMany) // MarkerExtendsMarker { typedef struct { char dummy; } C; impl(Marker, C); impl(MarkerExtendsMarker, C); // } (MarkerExtendsMarker) // MarkerExtendsMany { typedef struct { char dummy; } D; #define D_foo foo1_impl impl(Foo, D); #undef D_foo #define D_foo foo1_impl #define D_bar bar1_impl impl(Bar, D); #undef D_foo #undef D_bar impl(MarkerExtendsMany, D); // } (MarkerExtendsMany) int main(void) { // Ensure `interface`-generated data. { ENSURE_VTABLE_FIELD_TYPE(FooExtendsMarkerVTable, foo, FooOpType); ENSURE_DYN_OBJ_TYPE(FooExtendsMarker); ENSURE_VTABLE_FIELD_TYPE(FooExtendsManyVTable, foo, FooOpType); ENSURE_DYN_OBJ_TYPE(FooExtendsMany); ENSURE_DYN_OBJ_TYPE(MarkerExtendsMarker); ENSURE_DYN_OBJ_TYPE(MarkerExtendsMany); } // Ensure `impl`-generated data. { assert(VTABLE(A, FooExtendsMarker).Marker == &VTABLE(A, Marker)); assert(VTABLE(B, FooExtendsMany).Foo == &VTABLE(B, Foo)); assert(VTABLE(B, FooExtendsMany).Bar == &VTABLE(B, Bar)); assert(VTABLE(C, MarkerExtendsMarker).Marker == &VTABLE(C, Marker)); assert(VTABLE(D, MarkerExtendsMany).Foo == &VTABLE(D, Foo)); assert(VTABLE(D, MarkerExtendsMany).Bar == &VTABLE(D, Bar)); } ENSURE_DYN_OBJ(A, FooExtendsMarker); ENSURE_DYN_OBJ(B, FooExtendsMany); ENSURE_DYN_OBJ(C, MarkerExtendsMarker); ENSURE_DYN_OBJ(D, MarkerExtendsMany); // Ensure that dynamic objects of superinterfaces still work. { ENSURE_DYN_OBJ(A, Marker); ENSURE_DYN_OBJ(B, Foo); ENSURE_DYN_OBJ(B, Bar); ENSURE_DYN_OBJ(C, Marker); ENSURE_DYN_OBJ(D, Foo); ENSURE_DYN_OBJ(D, Bar); } return 0; } interface99-1.0.1/tests/util.h000066400000000000000000000045261440313356100161320ustar00rootroot00000000000000#ifndef INTERFACE99_TESTS_UTIL_H #define INTERFACE99_TESTS_UTIL_H #define ENSURE_EXPR_TYPE(expr, type) \ do { \ type x = (expr); \ (void)x; \ } while (0) #define ENSURE_VTABLE_FIELD_TYPE(vtable_type, field, type) \ do { \ ENSURE_FIELD_TYPE(vtable_type, field, type); \ ENSURE_FIELD_TYPE(struct vtable_type, field, type); \ } while (0) #define ENSURE_FIELD_TYPE(outer_type, field, type) ENSURE_EXPR_TYPE(((outer_type){0}).field, type) #define ENSURE_DYN_OBJ_TYPE(iface) \ do { \ ENSURE_FIELD_TYPE(iface, self, void *); \ ENSURE_FIELD_TYPE(iface, vptr, const iface##VTable *); \ \ ENSURE_FIELD_TYPE(struct iface, self, void *); \ ENSURE_FIELD_TYPE(struct iface, vptr, const iface##VTable *); \ } while (0) #define ENSURE_DYN_OBJ(implementer, iface) \ do { \ implementer x = {0}; \ iface x_dyn = DYN(implementer, iface, &x); \ assert(x_dyn.self == &x); \ assert(x_dyn.vptr == &VTABLE(implementer, iface)); \ } while (0) #endif // INTERFACE99_TESTS_UTIL_H interface99-1.0.1/tests/vcalls.c000066400000000000000000000125621440313356100164330ustar00rootroot00000000000000#include #include #define METHODS(prefix, T) \ vfunc(int, prefix##_vcall, VSelf) \ vfunc(int, prefix##_vcall_args, const VSelf, int x, const char *y) \ \ vfunc(int, prefix##_vcall_obj, T self) \ vfunc(int, prefix##_vcall_obj_args, T self, int x, const char *y) #define TestSuper_IFACE METHODS(test_super, TestSuper) #define Test_IFACE METHODS(test, Test) #define Test_EXTENDS (TestSuper) interface(TestSuper); interface(Test); static const int n = 7; static const char *str = "abc"; typedef enum { TEST_VCALL, TEST_VCALL_ARGS, TEST_VCALL_OBJ, TEST_VCALL_OBJ_ARGS, } RetVal; typedef struct { char dummy; } Impl; static Impl obj = {0}; #define IMPL_METHODS(prefix, T) \ static int Impl_##prefix##_vcall(VSelf) { \ VSELF(Impl); \ assert(&obj == self); \ \ return TEST_VCALL; \ } \ \ static int Impl_##prefix##_vcall_args(const VSelf, int x, const char *y) { \ VSELF(const Impl); \ assert(&obj == self); \ assert(n == x); \ assert(str == y); \ \ return TEST_VCALL_ARGS; \ } \ \ static int Impl_##prefix##_vcall_obj(T test) { \ assert(&obj == test.self); \ assert(&VTABLE(Impl, T) == test.vptr); \ \ return TEST_VCALL_OBJ; \ } \ \ static int Impl_##prefix##_vcall_obj_args(T test, int x, const char *y) { \ assert(&obj == test.self); \ assert(&VTABLE(Impl, T) == test.vptr); \ assert(n == x); \ assert(str == y); \ \ return TEST_VCALL_OBJ_ARGS; \ } declImpl(TestSuper, Impl); declImpl(Test, Impl); // clang-format off IMPL_METHODS(test_super, TestSuper) IMPL_METHODS(test, Test) // clang-format on impl(TestSuper, Impl); impl(Test, Impl); int main(void) { #define RVALUE DYN(Impl, Test, &obj) // Test that VCALL_* can accept lvalues. { Test lvalue = RVALUE; assert(TEST_VCALL == VCALL(lvalue, test_vcall)); assert(TEST_VCALL_ARGS == VCALL(lvalue, test_vcall_args, n, str)); assert(TEST_VCALL_OBJ == VCALL_OBJ(lvalue, test_vcall_obj)); assert(TEST_VCALL_OBJ_ARGS == VCALL_OBJ(lvalue, test_vcall_obj_args, n, str)); assert(TEST_VCALL == VCALL_SUPER(lvalue, TestSuper, test_super_vcall)); assert(TEST_VCALL_ARGS == VCALL_SUPER(lvalue, TestSuper, test_super_vcall_args, n, str)); assert(TEST_VCALL_OBJ == VCALL_SUPER_OBJ(lvalue, TestSuper, test_super_vcall_obj)); assert( TEST_VCALL_OBJ_ARGS == VCALL_SUPER_OBJ(lvalue, TestSuper, test_super_vcall_obj_args, n, str)); } // Test rvalues. { (void)VCALL(RVALUE, test_vcall); (void)VCALL_OBJ(RVALUE, test_vcall_obj); (void)VCALL_SUPER(RVALUE, TestSuper, test_super_vcall); (void)VCALL_SUPER_OBJ(RVALUE, TestSuper, test_super_vcall_obj); } #undef RVALUE return 0; } interface99-1.0.1/tests/version.c000066400000000000000000000030271440313356100166300ustar00rootroot00000000000000#include #include int main(void) { #undef IFACE99_MAJOR #undef IFACE99_MINOR #undef IFACE99_PATCH #define IFACE99_MAJOR 1 #define IFACE99_MINOR 2 #define IFACE99_PATCH 3 // IFACE99_VERSION_COMPATIBLE { ML99_ASSERT_UNEVAL(IFACE99_VERSION_COMPATIBLE(1, 0, 0)); ML99_ASSERT_UNEVAL(IFACE99_VERSION_COMPATIBLE(1, 1, 0)); ML99_ASSERT_UNEVAL(IFACE99_VERSION_COMPATIBLE(1, 1, 1)); ML99_ASSERT_UNEVAL(IFACE99_VERSION_COMPATIBLE(1, 2, 0)); ML99_ASSERT_UNEVAL(IFACE99_VERSION_COMPATIBLE(1, 2, 1)); ML99_ASSERT_UNEVAL(IFACE99_VERSION_COMPATIBLE(1, 2, 2)); ML99_ASSERT_UNEVAL(IFACE99_VERSION_COMPATIBLE(1, 2, 3)); // Major-incompatible. ML99_ASSERT_UNEVAL(!IFACE99_VERSION_COMPATIBLE(2, 0, 0)); ML99_ASSERT_UNEVAL(!IFACE99_VERSION_COMPATIBLE(7, 1, 2)); // Minor-incompatible. ML99_ASSERT_UNEVAL(!IFACE99_VERSION_COMPATIBLE(1, 3, 0)); ML99_ASSERT_UNEVAL(!IFACE99_VERSION_COMPATIBLE(1, 4, 9)); // Patch-incompatible. ML99_ASSERT_UNEVAL(!IFACE99_VERSION_COMPATIBLE(1, 2, 4)); ML99_ASSERT_UNEVAL(!IFACE99_VERSION_COMPATIBLE(1, 2, 5)); } // IFACE99_VERSION_EQ { ML99_ASSERT_UNEVAL(IFACE99_VERSION_EQ(1, 2, 3)); ML99_ASSERT_UNEVAL(!IFACE99_VERSION_EQ(1, 2, 7)); ML99_ASSERT_UNEVAL(!IFACE99_VERSION_EQ(1, 7, 3)); ML99_ASSERT_UNEVAL(!IFACE99_VERSION_EQ(7, 2, 3)); } #undef IFACE99_MAJOR #undef IFACE99_MINOR #undef IFACE99_PATCH return 0; }