api – C++-ähnliche Lesbarkeit in C

Zusammenfassung

api ist eine header-basierte Makro-Bibliothek, die C C++-ähnliche Vorteile verschafft – zumindest was die Lesbarkeit angeht. private/protected-Sichtbarkeit und überschreibbare callbacks machen klassen-typische Kapselung und virtuelle Hooks ohne OOP-Overhead direkt im Quelltext ausdrückbar. Keine Laufzeit, kein Overhead, rein C.

Ziel

C hat keine Sprachebenen-Sichtbarkeit wie C++ (private/protected) und keine Features wie static virtuelle Hooks. api übersetzt genau diese Lesbarkeits-Vorteile in Makros: Die Absicht („nur hier intern” vs. „bibliotheksintern” vs. „Sicht für die Anwendung”) steht im Code, statt in Konventionen versteckt zu sein. Es ist bewusst keine OOP-Schicht – nur Marker, die der C-Preprozessor in bereits legalen C-Ausdruck übersetzt.

Makros

MakroExpansionBedeutung
privatestaticnur in der eigenen Übersetzungseinheit (this.c) sichtbar
protected(leer)Kennzeichnung „bibliotheksintern” in der Definition (*.c)
protected_import(type, function)extern type functionImport einer geschützten Funktion in die Übersetzungseinheit
callback(leer)virtueller Callback, den die Anwendung liefert
callback_declaration(type, function)extern type function __attribute__((weak))voreinstellbare (weak) Callback-Deklaration des Moduls (*.h) – überschreibbar

Nutzung

#include <api/api.h>
 
private void helfer(void) { /* nur in dieser .c */ }
protected void kern(void) { /* bibliotheksintern */ }
 
callback void app_hook(void);          /* von der Anwendung definiert */
callback_declaration(void, app_hook);  /* weak-Default im Modul */

Der Aufrufer liest die Sichtbarkeit direkt aus dem Quelltext – derselbe Lesekomfort wie private:/protected: in C++, ohne Klassen-OOP.

Nutzung im gui-Projekt (real)

gui (GTK-Widget-Bibliothek) nutzt alle drei Makros produktiv:

  • callback_declaration – jedes Widget-Modul deklariert eine weak überschreibbare Hook:
    callback_declaration(void, gui_gl(gui_gl_t core, gui_event_t e));
    callback_declaration(bool, gui_application(gui_event_type_t event, gui_application_t core));
    Die Bibliothek ruft sie nur bei if (gui_gl != NULL) / if (gui_application != NULL). Ohne starke Definition der Anwendung ist das weak-Symbol NULL; mit Definition gewinnt die App. Effekt: „virtuell” wie in C++ – das Bibliotheks-Skelett liefert den Ablauf (startup/activate/shutdown, render/realize, click/toggle), die Anwendung liefert nur die Logik.
  • private – GTK-Signalhandler und interne Helfer sind private (_gui_gl_render, _gui_button_clicked, _widgets_list_head, …): sofort lesbar, was nur übersetzungseinheiten-intern ist.
  • protected_import – Widget-Module importieren Core-interne Helfer:
    protected_import(void*, _gui_get_core(GtkWidget*));
    protected_import(void, _gui_add_widget_to_internal_list(GtkWidget*));

Einordnung & Abhängigkeiten

  • Header-only (die api.c-Übersetzungseinheit importiert nur den Header); keine weiteren Projekt-Bibliotheken.
  • Library-Build: cmake -S . -B build && cmake --build build; Einbindung über Include-Pfad <api/api.h>.

Quellen

Verwandte Artikel