Files
uLib/docs/archives.md
2026-04-01 19:59:37 +00:00

4.5 KiB

Serialization and Archives Internals

This document explains the internal design of the uLib serialization system, which is built on top of Boost.Serialization. It provides custom archive implementations for various formats (XML, Text, Logging) and introduces Human Readable Pairs (HRP) for metadata-rich serialization.


Architecture Overview

The uLib archive system extends the standard boost::archive templates to add domain-specific features. The main components are:

  1. Custom Interface Layer: Extends the default Boost archive API with additional operators and utilities.
  2. Specialized Archive Implementations: Specialized classes for XML, Text, and Logging.
  3. HRP Support: First-class support for hrp (Human Readable Pair) wrappers, which carry units, ranges, and descriptions.
  4. Static Registration System: Macros and explicit instantiations to handle polymorphic types and compilation isolation.

Custom Interface Layer

All uLib archives use a custom interface defined in Archives.h via uLib_interface_iarchive and uLib_interface_oarchive. These templates add several key features:

Feature Operator/Method Description
Mapping Operator operator== Aliased to operator& (Boost's standard mapping operator).
Trace Operator operator!= Used for trace/debug output of strings during serialization.
Type Registration register_type<T>() Registers a class type with the archive's internal serializer map.
Standard IO operator<< / operator>> Standard redirect for saving and loading.

These interfaces are applied to the archives using template specialization of boost::archive::detail::interface_iarchive and interface_oarchive.


Archive Variants

XML Archives (xml_iarchive, xml_oarchive)

These inherit from boost::archive::xml_iarchive_impl and xml_oarchive_impl.

  • Internals: They override load_override and save_override to handle boost::serialization::hrp<T> specifically.
  • XML Mapping: When saving an hrp, it uses save_start(name) and save_end(name) to wrap the value in a named XML tag.

Text Archives (text_iarchive, text_oarchive)

Standard text-based archives used for compact serialization. They use StringReader to consume decorative text markers during loading.

Human Readable Text (hrt_iarchive, hrt_oarchive)

These are "naked" text archives that suppress most of Boost's internal metadata (object IDs, class IDs, versions).

  • Goal: Produce text output that is easy for humans to read and edit.
  • Internals: All overrides for Boost internal types (like object_id_type, version_type, etc.) are implemented as no-ops.

Log Archive (log_archive)

An XML-based output archive specifically for debug logging.

  • Internals: It forces every object into a Name-Value Pair (NVP) even if not provided by the user, and strips all technical metadata to keep the logs clean.

HRP (Human Readable Pair) Integration

hrp is a core uLib wrapper (defined in Serializable.h) that extends Boost's nvp:

// Example of HRP usage
ar & HRP2("Energy", m_energy, "MeV").range(0, 100);

Internal Handling in Archives

Archives in Archives.h provide specific save_override/load_override for hrp<T>:

  • XML: Maps the name() to an XML tag.
  • HRT: Formats as name: value [units]\n.
  • Log: Converts it to a standard Boost nvp for consistent XML logging.

Registration and Polymorphism

Registration Macro

The ULIB_SERIALIZATION_REGISTER_ARCHIVE(Archive) macro is crucial for polymorphic serialization. It instantiates the necessary template machinery to link the custom Archive type with any Serializable class exported via BOOST_CLASS_EXPORT.

Explicit Instantiation

To reduce compilation times and provide a single point of failure for link-time issues, uLib uses explicit instantiations in src/Core/Archives.cpp. This file includes the .ipp implementation files from Boost and instantiates the archive_serializer_map and implementation classes for all uLib archive types.


Utility: StringReader

The StringReader utility is used internally by text-based archives to parse and skip literals. For example:

  • When loading a string literal from a text archive, StringReader consumes whitespace and ensures the stream matches the expected string, failing if there is a mismatch.
  • This is vital for maintaining the structure of human-readable formats.