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.
Diese Seite ist der Überblick. Die ausführliche Entwicklerdokumentation steht im Unterkapitel C++ im Detail:
| Seite | Inhalt |
|---|---|
| Architektur | Schichtenmodell, Module, Datenmodell, Designentscheidungen, Thread-Sicherheit |
| Lesen | DataManager, DataChannel, Segmente, BlockReader, ReaderStats, transparentes OSFZ |
| Schreiben | StreamingWriter, BlockWriter, StaleValueGuard, ChannelDef, Metadaten-Defaults, Round-Trip |
| Fehlerbehandlung | Result<T>, vollständiger Fehlercode-Katalog, osf::throwing |
| C-ABI | osf-c — Ownership-Regeln, Funktionskatalog, C- und P/Invoke-Beispiele |
| Bauen & Einbinden | CMake-Optionen, Targets, add_subdirectory/FetchContent, Doxygen, CI |
| Kochbuch | kopierfertige Rezepte von Inspektion bis Embedded-Schleife |
| Interna | Encoder, 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) —
.osfund.osfzwerden ü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,fsyncpro Block (ausfallsicher bei Stromverlust), konstanter SpeicherbedarfBlockWriter— analystenfreundlich, sammelt im Speicher und schreibt die komplette Datei am Ende; passtsizeOfLengthValuebei Bedarf automatisch von 2 → 4 anStaleValueGuard— optionale Frische-Schicht, die den letzten Wert inaktiver Kanäle erneut ausgibt- Automatische Metadaten-Defaults:
created_utcwird beim Schreiben gestempelt;creator/tagerhalten Fallbacks, wenn nicht gesetzt
Komfort und Anbindung
- Eine werfende Komfort-Schicht (
osf::throwing) über demResult<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 … | Klasse | Hinweise |
|---|---|---|
| Datei lesen, typisierte Kanäle erhalten | osf::DataManager | Zentraler Einstiegspunkt — loadFromFile(), channel("name"). Liest .osf und .osfz. → Lesen |
| Den rohen Block-Stream iterieren | osf::BlockReader | Niedrigere Ebene; für sehr große Dateien und Streaming-Konsumenten. → Lesen |
| Samples eines Kanals halten | osf::DataChannel | Variante über Equidistant / Timestamped / Variable; typisierte Flat-Accessoren. → Lesen |
| Auf einem Embedded-Gerät aufzeichnen | osf::StreamingWriter | fsync pro Block, konstanter Speicher, ausfallsicher bei Stromverlust. → Schreiben |
| Komplette Datei in einem Schritt schreiben | osf::BlockWriter | Sammelt im Speicher, schreibt bei writeToFile(); passt sizeOfLengthValue automatisch an. → Schreiben |
| Inaktive Kanäle „frisch" halten | osf::StaleValueGuard | Gibt den letzten Wert von Kanälen erneut aus, die einen Schwellwert überschritten haben. → Schreiben |
| Round-Trip / OSF4 → OSF5 | freie Funkt. osf::writeToFile(mgr, …) | Lädt einen DataManager in einen BlockWriter und schreibt OSF5. → Kochbuch |
Exceptions statt Result<T> verwenden | osf::throwing | Opt-in-Header; nicht in den Kern einkompiliert. → Fehlerbehandlung |
| Aus C, C#, OCX … aufrufen | osf-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
Plattformspezifische Hinweise, CMake-Optionen und FAQ stehen in der
mitgelieferten BUILD.md der Bibliothek und auf der Seite
Bauen & Einbinden.
CMake-Optionen
| Option | Standard | Wirkung |
|---|---|---|
OSF_BUILD_TESTS | ON | GoogleTest/ctest-Suite bauen |
OSF_BUILD_EXAMPLES | ON | die lauffähigen Beispielprogramme unter examples/ bauen |
OSF_BUILD_DOCS | OFF | Doxygen-API-Referenz erzeugen (Ziel osf-docs; erfordert Doxygen) |
OSF_BUILD_C_API | OFF | die C-ABI-Bibliothek osf-c (+ C-Test) mitbauen |
OSF_USE_SYSTEM_ZLIB | OFF | System-zlib statt FetchContent verwenden |
OSF_WARNINGS_AS_ERRORS | OFF | Warnungen als Fehler (/WX bzw. -Werror); in CI ON |
BUILD_SHARED_LIBS | OFF | die 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; Dateinamelibosf.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
- Quellcode: github.com/optimeas/osf,
Verzeichnis
implementations/cpp/ - Bauanleitung:
BUILD.mdim Bibliotheksverzeichnis — Zusammenfassung unter Bauen & Einbinden - API-Referenz: Mit Doxygen über
-D OSF_BUILD_DOCS=ONerzeugen (Zielosf-docs) — siehe Bauen & Einbinden - Format-Spezifikation: Kapitel OSF-Format