Zum Hauptinhalt springen

C++-Implementierung

Eine eigenständige C++17-Implementierung des Open Streaming Format — idiomatisches modernes C++ ohne externe Laufzeitabhängigkeiten. Sie liest .osf- und .osfz-Dateien und schreibt OSF5. Die Bibliothek ist komplett selbstständig nutzbar und distributierbar; ihr Verhalten ist allein durch die OSF-Format-Spezifikation definiert.

Entwickler-Handbuch

Diese Seite ist der Überblick. Die ausführliche Entwicklerdokumentation steht im Unterkapitel C++ im Detail:

SeiteInhalt
ArchitekturSchichtenmodell, Module, Datenmodell, Designentscheidungen, Thread-Sicherheit
LesenDataManager, DataChannel, Segmente, BlockReader, ReaderStats, transparentes OSFZ
SchreibenStreamingWriter, BlockWriter, StaleValueGuard, ChannelDef, Metadaten-Defaults, Round-Trip
FehlerbehandlungResult<T>, vollständiger Fehlercode-Katalog, osf::throwing
C-ABIosf-c — Ownership-Regeln, Funktionskatalog, C- und P/Invoke-Beispiele
Bauen & EinbindenCMake-Optionen, Targets, add_subdirectory/FetchContent, Doxygen, CI
Kochbuchkopierfertige Rezepte von Inspektion bis Embedded-Schleife
InternaEncoder, Chunking-Mathematik, Builder-Zustandsmaschine — für Mitwirkende

Funktionsumfang

Die Implementierung ist funktional vollständig. Lese- und Schreibpfad sind durch eine GoogleTest-/ctest-Suite abgedeckt (0 Warnungen unter MSVC /W4 /permissive-), und CI baut und testet auf Linux, macOS und Windows.

Lesepfad

  • Magic-Header-Parser; OSF5-JSON- und OSF4-XML-Metablock-Parser
  • Block-Stream-Reader und der typisierte DataManager (einheitlicher In-Memory-Reader mit typisierten Kanälen)
  • Transparente OSFZ-Dekompression (gzip/zlib) — .osf und .osfz werden über dieselbe API gelesen
  • Best-Effort: abgeschnittene Dateien (Stromausfall) liefern alle vollständig lesbaren Blöcke; unbekannte zukünftige Datentypen werden übersprungen statt das Laden abzubrechen

Schreibpfad (OSF5)

  • StreamingWriter — eingebettet, Sample für Sample, fsync pro Block (ausfallsicher bei Stromverlust), konstanter Speicherbedarf
  • BlockWriter — analystenfreundlich, sammelt im Speicher und schreibt die komplette Datei am Ende; passt sizeOfLengthValue bei Bedarf automatisch von 2 → 4 an
  • StaleValueGuard — optionale Frische-Schicht, die den letzten Wert inaktiver Kanäle erneut ausgibt
  • Automatische Metadaten-Defaults: created_utc wird beim Schreiben gestempelt; creator/tag erhalten Fallbacks, wenn nicht gesetzt

Komfort und Anbindung

  • Eine werfende Komfort-Schicht (osf::throwing) über dem Result<T>-Kern für Aufrufer, die Exceptions bevorzugen
  • Die C-ABI-Bibliothek osf-c (osf/capi.h) — eine reine C99-Schicht für die sprachübergreifende Nutzung (DLL/Shared Object)

Architektur im Überblick

Zwei API-Ebenen liegen auf einem gemeinsamen, exception-freien Kern (osf::Result<T>). Die Lese-Seite stellt aus dem Block-Stream typisierte Kanäle zusammen; die Schreib-Seite bietet zwei Writer-Klassen für unterschiedliche Einsatzprofile; und ein C-ABI macht das Ganze für Nicht-C++-Konsumenten zugänglich. Vertiefung: Architektur.

Lesepfad

DataManager::loadFromFile() steuert diese gesamte Pipeline; OSFZ wird automatisch erkannt und dekomprimiert, sodass .osf und .osfz denselben Aufruf verwenden.

Schreibpfad (OSF5)

Schichten & C-ABI

Welche Klasse wofür

Ich möchte …KlasseHinweise
Datei lesen, typisierte Kanäle erhaltenosf::DataManagerZentraler Einstiegspunkt — loadFromFile(), channel("name"). Liest .osf und .osfz. → Lesen
Den rohen Block-Stream iterierenosf::BlockReaderNiedrigere Ebene; für sehr große Dateien und Streaming-Konsumenten. → Lesen
Samples eines Kanals haltenosf::DataChannelVariante über Equidistant / Timestamped / Variable; typisierte Flat-Accessoren. → Lesen
Auf einem Embedded-Gerät aufzeichnenosf::StreamingWriterfsync pro Block, konstanter Speicher, ausfallsicher bei Stromverlust. → Schreiben
Komplette Datei in einem Schritt schreibenosf::BlockWriterSammelt im Speicher, schreibt bei writeToFile(); passt sizeOfLengthValue automatisch an. → Schreiben
Inaktive Kanäle „frisch" haltenosf::StaleValueGuardGibt den letzten Wert von Kanälen erneut aus, die einen Schwellwert überschritten haben. → Schreiben
Round-Trip / OSF4 → OSF5freie Funkt. osf::writeToFile(mgr, …)Lädt einen DataManager in einen BlockWriter und schreibt OSF5. → Kochbuch
Exceptions statt Result<T> verwendenosf::throwingOpt-in-Header; nicht in den Kern einkompiliert. → Fehlerbehandlung
Aus C, C#, OCX … aufrufenosf-c (osf/capi.h)Reines C99-ABI; mit -D OSF_BUILD_C_API=ON bauen. → C-ABI

Lauffähige Beispiele

implementations/cpp/examples/ enthält vier kleine Programme über <osf/osf.h>inspect (Header / Metadaten / Kanäle, transparentes OSFZ), dump (Sample-Werte), write (OSF5 synthetisieren und schreiben) und copy (Round-Trip). Sie werden mit -D OSF_BUILD_EXAMPLES=ON gebaut (standardmäßig aktiv). Ausgearbeitete Code-Rezepte: Kochbuch.

Bauen — Schnellstart

cmake -B build
cmake --build build
ctest --test-dir build

Plattform­spezifische Hinweise, CMake-Optionen und FAQ stehen in der mitgelieferten BUILD.md der Bibliothek und auf der Seite Bauen & Einbinden.

CMake-Optionen

OptionStandardWirkung
OSF_BUILD_TESTSONGoogleTest/ctest-Suite bauen
OSF_BUILD_EXAMPLESONdie lauffähigen Beispielprogramme unter examples/ bauen
OSF_BUILD_DOCSOFFDoxygen-API-Referenz erzeugen (Ziel osf-docs; erfordert Doxygen)
OSF_BUILD_C_APIOFFdie C-ABI-Bibliothek osf-c (+ C-Test) mitbauen
OSF_USE_SYSTEM_ZLIBOFFSystem-zlib statt FetchContent verwenden
OSF_WARNINGS_AS_ERRORSOFFWarnungen als Fehler (/WX bzw. -Werror); in CI ON
BUILD_SHARED_LIBSOFFdie Kern-Bibliothek als Shared Library bauen

C++17 ist die fest definierte Sprachbaseline der Bibliothek. Der Wechsel auf C++20 oder höher ist ein bewusstes Library-Upgrade, keine Build-Option. Drittanbieter-Code (tl::expected, nlohmann/json, pugixml) ist im Repository unter third_party/ mitgeliefert; zlib kommt per FetchContent oder System.

Einbinden

Die Bibliothek exportiert zwei CMake-Ziele:

  • osf::osf — die Kern-Bibliothek (Standard statisch; Dateiname libosf.a / osf.lib)
  • osf::headers — ein INTERFACE-Ziel mit den öffentlichen Include-Pfaden

Einbindung per add_subdirectory oder FetchContent — Beispiel-Snippets auf Bauen & Einbinden.

API im Überblick

Der Kern ist exception-frei: Operationen, die scheitern können, geben osf::Result<T> zurück (ein tl::expected<T, osf::Error>). Der vollständige Fehlercode-Katalog steht unter Fehlerbehandlung.

Lesen

#include <osf/manager.h>

auto result = osf::DataManager::loadFromFile("messung.osf"); // auch .osfz
if (!result) {
// result.error().message — strukturierter Fehler, keine Exception
return;
}
osf::DataManager const& mgr = *result;

// Kanal über den Namen ansprechen (primäre Zugriffsform)
if (osf::DataChannel const* ch = mgr.channel("Sensor.Temperatur")) {
auto werte = osf::asDoublesFlat(
std::get<osf::TimestampedChannel>(*ch)); // typisierter Zugriff
}

Wer lieber mit Exceptions arbeitet, nutzt die opt-in-Schicht:

#include <osf/throwing.h>

auto mgr = osf::throwing::load("messung.osf"); // wirft osf::Exception bei Fehler

Schreiben (OSF5)

#include <osf/blockwriter.h>

osf::BlockWriter writer;
writer.setCreator("mein-tool/1.0");

osf::ChannelDef def;
def.name = "signale.sinus";
def.dataType = osf::DataType::Double;
def.channelType = osf::ChannelType::Scalar;

auto idx = writer.addChannel(def); // Result<uint16_t>
// … Samples zu *idx hinzufügen (addTimestampedSample, addEquidistantSegment, …)
writer.writeToFile("ausgabe.osf");

Für eingebettetes, ausfallsicheres Schreiben gibt es stattdessen den StreamingWriter (fsync pro Block). Ein geladener DataManager lässt sich mit der freien Funktion osf::writeToFile(mgr, pfad) direkt als OSF5 zurückschreiben (Round-Trip / OSF4 → OSF5). Alle Details und die Wahl des richtigen sizeOfLengthValue: Schreiben.

C-ABI (osf-c)

Mit -D OSF_BUILD_C_API=ON entsteht zusätzlich die Shared Library osf-c mit einer reinen C99-Schnittstelle (osf/capi.h): opake Handles (osf_manager, osf_channel), osf_status-Codes, eine thread-lokale osf_last_error_message() und Copy-out-Reader für Zeitstempel und Werte — plus osf_write_to_file für den Round-Trip-Schreibpfad. Keine C++-Exception überschreitet die ABI-Grenze. Gedacht für die Anbindung aus C, C#/P-Invoke, ActiveX/OCX und künftige Sprach-Bindings. Funktionskatalog und Beispiele: C-ABI.

Hinweise

  • Nur OSF5 wird geschrieben — auch wenn die Quelle eine OSF4-Datei war.
  • OSFZ beim Schreiben ist ein nachgelagerter Schritt: Die Writer komprimieren nie inline; OSFZ (gzip) entsteht nach dem Abschluss der .osf-Datei — durch einen zukünftigen Post-Close-Kompressor (Hintergrund-Thread) oder ein eigenständiges Compress-CLI. OSFZ wird gelesen transparent.
  • Best-Effort beim Lesen: abgeschnittene Dateien liefern alle Daten bis zum letzten vollständig lesbaren Block, ohne Absturz.
  • Die Bibliothek ist Qt-neutral; ein Qt-naher Zusatz kann später als eigener integrations/-Eintrag folgen.

Quellcode und weiterführende Informationen