An open API service indexing awesome lists of open source software.

https://github.com/aurimasniekis/cpp-commons

Header-only C++23 library of common/shared types for the C++ libraries
https://github.com/aurimasniekis/cpp-commons

commons cpp cpp23

Last synced: 4 days ago
JSON representation

Header-only C++23 library of common/shared types for the C++ libraries

Awesome Lists containing this project

README

          

# Commons

[![CI](https://github.com/aurimasniekis/cpp-commons/actions/workflows/ci.yml/badge.svg)](https://github.com/aurimasniekis/cpp-commons/actions/workflows/ci.yml)
[![Docs](https://github.com/aurimasniekis/cpp-commons/actions/workflows/docs.yml/badge.svg)](https://aurimasniekis.github.io/cpp-commons/)

A header-only C++23 library of small, shared building-block types — a
compile-time fixed-size string, Rust-flavoured fixed-width numeric aliases, an
RGBA `Color` with full HSL/HSV manipulation and CSS/Material-UI palettes, an
Iconify `Icon` identifier, presentation metadata (`DisplayInfo`), a compile-time
named-`Flag` system, a Spring-style `Prioritized` ordering toolkit,
`SemVer` / `VersionConstraint` semantic-version types, and an `IOrigin`
provenance envelope. Every type carries optional nlohmann/json serialization that
turns on by itself when the dependency is available. The namespace is `comms`;
headers live under ``.

## Why use this library?

- **Good for** sharing one definition of common vocabulary types across several
projects instead of re-implementing them per repository.
- **Good for** UI-adjacent backend code: colors, icons, and display metadata
that need to round-trip to JSON for a frontend.
- **Light by default.** The core depends only on the C++23 standard library.
The JSON hooks stay completely inert unless nlohmann/json is on the include
path, so you never pay for an integration you don't use.
- **Compile-time friendly.** `FixedString`, `Color`, and `Icon` are literal
types usable in `constexpr` and `static_assert` contexts and as non-type
template parameters.
- **Not ideal for** large, hot containers: `PrioritizedSet` and `FlagSet` are
designed for config-sized collections and use linear-time lookups.
- **Not ideal for** projects that cannot move to C++23 — the whole library
requires it.

## Quick example

```cpp
#include

#include

int main() {
namespace c = comms;

c::FixedString tag{"order.created"}; // compile-time string, usable as an NTTP
c::u32 count = 42;
c::f64 ratio = 1.0 / 3.0;

std::cout << tag.view() << " x" << count << " (" << ratio << ")\n";
std::cout << "commons " << c::version << "\n";
}
```

`FixedString` is built with class template argument deduction (CTAD) straight
from the literal, so you never spell its size. It is a *structural* type, which
means it can also appear directly in a template argument list, e.g.
`Event<"order.created">`. The numeric aliases (`u32`, `f64`, …) are lowercase
names for the standard fixed-width types. Including `commons/commons.hpp` pulls
in every core type plus the self-gating JSON hooks; it is always safe to include
even when nlohmann/json is absent.

## Installation

`commons` is a header-only `INTERFACE` library. There is no compiled artifact to
link — you only need the headers on your include path and C++23 enabled.

Package-manager support (vcpkg, Conan, system packages) is not provided. The
supported integration paths are CMake, Meson, and copying the headers.

### CMake — vendored subdirectory

The most reliable option: drop the repository into your tree (a submodule or
copy) and add it.

```cmake
cmake_minimum_required(VERSION 3.25)
project(example LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_subdirectory(third_party/commons)

add_executable(example main.cpp)
target_link_libraries(example PRIVATE commons::commons)
```

Linking `commons::commons` brings the include directory and the `cxx_std_23`
requirement along with it.

### CMake — FetchContent

```cmake
include(FetchContent)

FetchContent_Declare(
commons
URL https://github.com/aurimasniekis/cpp-commons/archive/refs/tags/v0.1.4.tar.gz
URL_HASH SHA256=511268a30e692e82365669738c734d093edc7ca76c898337125f032569c8f907
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)

FetchContent_MakeAvailable(commons)

target_link_libraries(example PRIVATE commons::commons)
```

By default no optional dependency is fetched: the JSON hooks auto-detect
nlohmann/json. To *force* the integration on — which additionally fetches
nlohmann/json (version 3.12.0 or newer) and hard-defines the gate macro —
configure with `-DCOMMONS_WITH_NLOHMANN_JSON=ON`.

### CMake — installed package

After `cmake --install`, the package is consumable via `find_package`:

```cmake
find_package(commons 0.1 REQUIRED)
target_link_libraries(my_app PRIVATE commons::commons)
```

Install rules are automatically disabled when nlohmann/json was fetched (a
fetched dependency cannot be re-exported). For a clean install, leave
`COMMONS_WITH_NLOHMANN_JSON=OFF` (the default) or provide nlohmann/json through a
system package.

### Meson

```meson
commons_dep = dependency('commons', version: '>=0.1.0',
fallback: ['commons', 'commons_dep'])
```

Meson options mirror the CMake ones: `-Djson=true|false`, `-Dtests=true|false`,
`-Dexamples=true|false`. A `pkg-config` file is generated on install.

### Manual / header-only

Copy `include/commons` onto your include path and compile with C++23. The only
generated header is `commons/version.hpp`: CMake and Meson produce it from
`commons/version.hpp.in` using the project version. For a pure manual copy,
either configure once with CMake/Meson and copy the generated
`commons/version.hpp` alongside the rest, or create it by hand from the template
(replace the four `@PROJECT_VERSION...@` tokens). It is only needed if you
include the umbrella `commons/commons.hpp` or `commons/version.hpp` directly.

## Requirements

- **C++23.** The library uses structural non-type template parameters,
`constexpr` `std::string_view`, `std::format`, and concepts. CMake enforces
this with `target_compile_features(commons INTERFACE cxx_std_23)`.
- **Build tooling:** CMake 3.25 or newer, or Meson 1.3.0 or newer. Neither is
required if you only copy the headers.
- **Optional dependency:** [nlohmann/json](https://github.com/nlohmann/json)
3.12.0 or newer, which enables ``.

## Core concepts

### `comms::FixedString`

A fixed-capacity string whose contents are fixed at compile time. `N` is the
buffer size *including* the trailing null terminator, so `size()` returns
`N - 1`. Because it is a structural type, it can be a non-type template
parameter.

```cpp
#include

#include

template
struct Event {
static constexpr std::string_view name = Name.view();
};

int main() {
comms::FixedString id{"login"}; // N = 6 (5 chars + null), size() == 5
std::cout << id.view() << " / " << id.size() << "\n";

static_assert(Event<"login">::name == "login");
}
```

It converts implicitly to `std::string_view`, and `operator==` compares against
any `FixedString` (different sizes simply compare unequal).

### `comms::Color`

Four `u8` channels (`r`, `g`, `b`, `a`), with almost the entire API `constexpr`:
packed-integer conversions, HSL/HSV conversion, channel and alpha tweaks, the
HSL transforms, WCAG luminance/contrast, palette generation, and parsing. Only
the `std::string`-producing methods are non-`constexpr`. The default `Color` is
opaque black.

```cpp
#include // brings in color.hpp and the _color literal

#include

int main() {
using comms::Color;
using namespace comms::literals;

constexpr Color indigo = "#6366f1"_color; // compile-time hex literal
std::cout << indigo.lighten(0.15).to_hex_string() << "\n";
std::cout << indigo.complement().to_hex_string() << "\n";
}
```

### `comms::Icon`

A value type holding an Iconify `set:name` identifier (e.g. `mdi:abacus`) inline
in a 64-byte buffer — no heap, trivially copyable, usable in `constexpr`
contexts. Construct it from a whole value or from the two parts; both validate.

```cpp
#include // brings in icon.hpp and the _icon literal

#include

int main() {
using namespace comms::literals;

constexpr comms::Icon cog = comms::Icon::from("mdi", "cog");
constexpr comms::Icon home = "mdi:home"_icon; // compile-time literal
std::cout << cog.value() << " | " << cog.set() << " | " << cog.name() << "\n";
std::cout << home.value() << "\n";
}
```

### `comms::DisplayInfo`

Optional presentation metadata — `name`, `description`, `icon`, `color`, every
field an `std::optional`. The intent is *static* data attached to a type once and
never mutated. The `icon`/`color` fields reuse `Icon`/`Color`, so they serialize
to JSON for a frontend out of the box.

### `comms::Flag` family

Compile-time named flags grouped into categories, a runtime `FlagSet` that keeps
insertion order, a program-wide `GlobalFlagRegistry`, and mixins for types that
own a flag set. Flags are *types*, declared (and optionally auto-registered) with
the `COMMONS_*_FLAG*` macros.

### `comms::IOrigin`

A polymorphic provenance envelope — *where a definition came from* — for an open
set of sources. The `kind()` discriminator is a compile-time `FixedString`
template parameter: derive from `OriginKind<"yourkind", YourType>` and you get
`kind()`, a deep `clone()`, and a `DisplayInfo`-backed `info()` (so every origin
is also `Displayable`). Built-ins are `CoreOrigin`, `InternalOrigin`,
`ExternalOrigin` (carrying a `source`), and `UnknownOrigin`; carry one as an
`OriginPtr` (`std::unique_ptr`). New kinds self-register into the
`GlobalOriginRegistry` with `COMMONS_REGISTER_ORIGIN(Type)` — mirroring the flag
registry — so a JSON `kind` can be resolved back to the right type.

### `comms::Prioritized`

Attaches integer priorities to orderable things and sorts them deterministically,
mirroring Spring's `Ordered`: **lower value sorts first** (higher precedence).
`HIGHEST_PRECEDENCE` is `INT_MIN`, `LOWEST_PRECEDENCE` is `INT_MAX`, and the
neutral `DEFAULT_PRIORITY` is `0`.

### `comms::SemVer`

A [Semantic Versioning 2.0.0](https://semver.org) value — `major.minor.patch`
plus optional prerelease and build metadata. `SemVer::parse` is non-throwing
(returns `std::optional`), accepts an optional `v` prefix, and parses partial
versions leniently (`"1"`, `"1.2"`). Ordering implements the full §11 prerelease
precedence — a prerelease ranks below its release and numeric identifiers compare
numerically, so `1.0.0-alpha.2 < 1.0.0-alpha.10 < 1.0.0-beta < 1.0.0` — while
build metadata is preserved in the text form but ignored by comparison and
equality. Because it holds `std::string` members it is a runtime type (not
`constexpr`).

### `comms::VersionConstraint`

An npm-style semver range that answers `satisfies(SemVer)`: `*`, an exact
version, the comparisons `>=`/`>`/`<=`/`<`/`!=`, caret (`^1.2.3` → `>=1.2.3
<2.0.0`) and tilde (`~1.2.3` → `>=1.2.3 <1.3.0`) ranges, and space-separated
intersections like `>=1.0.0 <2.0.0` (all must match). Unlike `SemVer::parse`,
`VersionConstraint::parse` **throws** `std::invalid_argument` on a malformed
sub-version.

## Common usage patterns

### Working with colors

```cpp
#include

#include
#include
#include

int main() {
using comms::Color;

// Parsing returns std::optional — always check before dereferencing.
if (std::optional red = Color::parse("rgb(255 0 0)")) {
std::cout << red->to_hex_string() << "\n"; // #ff0000
}

// Named colors, hex, and HSL functional notation all parse.
std::cout << Color::parse("rebeccapurple")->to_hex_string() << "\n";
std::cout << Color::parse("hsl(120, 100%, 50%)")->to_hex_string() << "\n";

// Palettes from the CSS and Material-UI sets.
std::cout << comms::Colors::css::indigo.to_hex_string() << "\n";
std::cout << comms::Colors::mui::red_500.to_hex_string() << "\n"; // flat alias
std::cout << comms::Colors::mui::red[700].to_hex_string() << "\n"; // indexed shade
std::cout << comms::Colors::mui::blue.accent(200).to_hex_string() << "\n";

// WCAG: choose readable text and report contrast.
constexpr Color bg{0x63, 0x66, 0xf1};
const Color text = bg.readable_text_color(); // black or white
std::cout << text.to_hex_string() << " contrast "
<< bg.contrast_ratio(text) << "\n";

// std::format specs: h (lowercase hex, default), H (uppercase), r (CSS rgb).
std::cout << std::format("{:H}", bg) << "\n";
std::cout << std::format("{:r}", bg.fade(0.5)) << "\n";
}
```

This covers the main paths: successful parsing (with the mandatory `optional`
check), the palette accessors, the WCAG helpers, and the formatter specs.
`fade(opacity)` takes a `[0, 1]` opacity and sets the alpha channel. Transforms
such as `lighten`/`darken`/`saturate`/`rotate_hue` clamp their results, so they
never produce an out-of-range channel.

> **Pitfall — invalid shades throw.** `mui::red[shade]` accepts only
> `50, 100, 200, …, 900`, and `accent(shade)` only `100, 200, 400, 700`. Any
> other value throws `std::out_of_range`. The flat aliases (`red_500`, `red_a200`)
> cannot be misindexed, so prefer them for fixed shades.

### Building and parsing icons

```cpp
#include // opt-in predefined catalogs

#include

int main() {
// Predefined Material Design Icons (only via ).
constexpr comms::Icon abacus = comms::Icons::mdi::abacus;
std::cout << abacus.value() << "\n";

// Keyword-named icons get a trailing underscore; the value is unchanged.
std::cout << comms::Icons::mdi::delete_.value() << "\n"; // mdi:delete

// Non-throwing validation for runtime/untrusted input.
if (std::optional icon = comms::Icon::parse("mdi:cog")) {
std::cout << "valid: " << icon->value() << "\n";
}
if (!comms::Icon::parse("not-an-icon")) {
std::cout << "rejected (no single ':')\n";
}
}
```

Use `Icon::parse` for runtime input — it returns `std::nullopt` on malformed
values. Use `Icon::from` when you want a hard failure: it throws
`std::invalid_argument` for a malformed value or `std::length_error` for one that
exceeds the 64-byte capacity. In a `constexpr` context, either failure becomes a
compile error.

> **Pitfall — predefined catalogs are not in the umbrella.** The MDI table has
> 7,447 entries, so `commons/commons.hpp` does not include it. Add
> `#include ` in the translation units that need
> `comms::Icons::mdi::...`.

### Attaching display metadata to a type

There are two ways to attach `DisplayInfo`, and a concept to detect it.

```cpp
#include

#include

// 1) Intrusive: a static member returning a reference.
struct Widget {
static const comms::DisplayInfo& display_info() {
static const comms::DisplayInfo info{
.name = "Widget",
.icon = comms::Icon::from("mdi:widgets"),
.color = comms::Colors::css::indigo,
};
return info;
}
};

// A third-party enum we cannot edit.
enum class Severity { Info, Warning, Error };

// 2) Non-intrusive: specialize the trait (in namespace comms).
template <>
struct comms::HasDisplayInfo {
static const DisplayInfo& display_info() {
static const DisplayInfo info{.name = "Severity",
.color = Colors::css::orange};
return info;
}
};

template
requires comms::Displayable
void show(std::string_view label) {
const auto& d = comms::display_info();
std::cout << label << ": " << d.name.value_or("(none)") << "\n";
}

int main() {
show("intrusive");
show("trait");

static_assert(!comms::Displayable); // no metadata → not Displayable
}
```

`comms::display_info()` dispatches to whichever mechanism is present.
`comms::Displayable` reports whether either exists, so you can constrain
templates on it. Calling `display_info()` on a type that has neither is a
compile error, by design.

### Declaring and collecting flags

```cpp
#include

#include

namespace {
COMMONS_FLAG_CATEGORY(Network, "network");
COMMONS_DEFINE_FLAG_IN(Ipv6, "ipv6", Network); // defined + auto-registered
COMMONS_DEFINE_FLAG_IN(KeepAlive, "keep-alive", Network);
COMMONS_DEFINE_FLAG(Verbose, "verbose"); // default "unset" category

// A builder that owns a FlagSet limited to Network flags and is readable
// through the IHasFlags interface.
class Config : public comms::FlagBuilderGetters {};
} // namespace

int main() {
comms::FlagSet set;
set.insert();
set.insert();
set.insert(); // duplicate by name — ignored, returns false

for (const auto& f : set) { // insertion order preserved
std::cout << f.name << " [" << f.category << "]\n";
}

Config cfg;
cfg.flag().set_flag(true); // fluent, returns Config&
// cfg.flag(); // will not compile: Verbose is not in Network

const comms::IHasFlags& view = cfg; // read polymorphically
std::cout << "has ipv6? " << view.has_flag() << "\n";

std::cout << comms::GlobalFlagRegistry::instance().flags().size()
<< " flags registered\n";
}
```

`FlagSet` deduplicates by flag name and keeps insertion order; `insert` returns
`false` when the name is already present. `group_by_category()` returns a
`std::map` from category name to the flags in it. The `COMMONS_DEFINE_FLAG*`
macros register each flag into the `GlobalFlagRegistry` automatically; the
builder mixins (`FlagBuilderMixin` for a private set, `FlagBuilderGetters` for an
observable one) constrain their typed overloads to the listed categories.

### Ordering things by priority

```cpp
#include

#include
#include

// Carry a mutable priority via the CRTP builder mixin.
struct Adapter : comms::PrioritizedBuilder {
std::string name;
explicit Adapter(std::string n) : name(std::move(n)) {}
};

int main() {
Adapter fast("fast");
fast.highest_priority(); // fluent; sets INT_MIN
std::cout << fast.name << " = " << fast.priority() << "\n";

// Attach a priority to any value. The FIRST argument is always the priority.
auto level = comms::with_priority(-5, 42); // WithPriority
std::cout << *level << " @ " << level.priority() << "\n";

// A set that iterates in (priority asc, insertion-order asc) order.
comms::PrioritizedSet pipeline;
pipeline.insert(5, "compress");
pipeline.insert(1, "auth");
pipeline.insert(5, "log"); // ties with "compress" → insertion order
for (const auto& stage : pipeline) {
std::cout << "[" << pipeline.priority_of(stage) << "] " << stage << "\n";
}
// Prints auth (1), compress (5), log (5).
}
```

`get_priority(x)` is a uniform, null-safe lookup that works on values,
references, raw pointers, and smart pointers, falling back to `DEFAULT_PRIORITY`
when no priority is discoverable. `PrioritizedCompare` and
`LenientPrioritizedCompare` order `std::shared_ptr`s for use as the comparator
of a `std::set`.

> **Pitfall — `insert` never updates an existing priority.** Like `std::set`,
> re-inserting an equal value is a no-op; the originally stored priority stays.
> Use `set_priority(value, p)` to change it. Also note `PrioritizedSet`'s
> `insert`/`find`/`erase(value)` are O(n) — it targets config-sized collections,
> not large data sets.

### Versions and constraints

```cpp
#include
#include

#include
#include
#include

int main() {
// Parsing is non-throwing; a `v` prefix and partial versions are accepted.
comms::SemVer v = comms::SemVer::parse("v1.4.0-rc.1").value();
std::cout << v << "\n"; // 1.4.0-rc.1

// Full SemVer ordering: prereleases sort below the release, and numeric
// identifiers compare numerically (alpha.2 < alpha.10).
std::vector versions;
for (const auto* s : {"1.0.0", "1.0.0-alpha.10", "1.0.0-alpha.2", "1.0.0-beta"}) {
versions.push_back(comms::SemVer::parse(s).value());
}
std::ranges::sort(versions);
// -> 1.0.0-alpha.2, 1.0.0-alpha.10, 1.0.0-beta, 1.0.0

// Range constraints answer satisfies(SemVer).
auto range = comms::VersionConstraint::parse(">=1.2.0 <2.0.0");
std::cout << std::boolalpha
<< range.satisfies(comms::SemVer::parse("1.5.0").value()) << "\n"; // true
}
```

`SemVer` works directly in `std::set`/`std::map`/`std::sort` (via its
`operator<=>`) and in `std::unordered_*` (via the `std::hash` specialization);
both `SemVer` and `VersionConstraint` also support `std::format`, `operator<<`,
and JSON.

### JSON serialization (optional)

With nlohmann/json available, every public type gains `to_json`/`from_json`.

```cpp
// Build with -DCOMMONS_WITH_NLOHMANN_JSON=ON, or simply have nlohmann/json
// on the include path.
#include
#include

#include

#include

int main() {
using json = nlohmann::json;

comms::FixedString id{"order.created"};
json j = id; // -> "order.created"
auto back = j.get>(); // round-trips

json color = comms::Colors::css::indigo; // -> "#4b0082" (hex string)
json icon = comms::Icon::from("mdi:cog"); // -> "mdi:cog"

comms::cf64 signal{0.5, -1.25};
json sig = signal; // -> [0.5, -1.25]

std::cout << color.dump() << " " << icon.dump() << " " << sig.dump() << "\n";
}
```

The mappings are: `FixedString` and `Icon` ⇄ strings; `Color` ⇄ a hex string
(`#RRGGBB`, or `#RRGGBBAA` when not opaque); `Hsl`/`Hsv` ⇄ objects; `DisplayInfo`
⇄ an object with absent fields omitted; `FlagRef` ⇄ its name and `FlagSet` ⇄ an
array of names; `SemVer` ⇄ its canonical version string and `VersionConstraint`
⇄ its raw range string; `IOrigin`/`OriginPtr` ⇄ a `{"kind", …fields}` object
(null `OriginPtr` ⇄ `null`), with the four built-in kinds round-tripping their
fields and the `kind` resolved back through the `GlobalOriginRegistry` (an
unknown kind throws; a custom kind brings its own `to_json`/`from_json`);
`i128`/`u128` ⇄ decimal strings; the complex aliases ⇄
`[real, imaginary]` arrays; `WithPriority` ⇄ `{"priority":N,"value":}` and
`PrioritizedSet` ⇄ a sorted array — both only when `T` is itself
JSON-serializable.

## Error handling

The library uses three distinct strategies, by type:

- **`std::optional` for parsing.** `Color::parse`, `Color::parse_hex`, and
`Icon::parse` return `std::nullopt` on malformed input. Check before
dereferencing.
- **Exceptions for hard failures.** `Icon::from` throws `std::invalid_argument`
(malformed) or `std::length_error` (too long). `Color`'s MUI shade accessors
(`operator[]`, `accent`) throw `std::out_of_range`. The `std::format`
specializations throw `std::format_error` on a bad spec. The JSON `from_json`
hooks throw nlohmann's exception type when a value is invalid (an unparseable
color, a string too long for a `FixedString`, an unregistered flag name, …).
- **Compile-time errors.** The `_color` and `_icon` user-defined literals are
`consteval`, so a malformed literal fails to compile. `Icon::from` in a
`constexpr` context turns its throws into compile errors. Calling
`display_info()` on a non-`Displayable` type is a compile error.

```cpp
#include
#include

#include

int main() {
// optional path
if (auto c = comms::Color::parse("#zzzzzz"); !c) {
std::cout << "bad color\n";
}

// exception path
try {
(void)comms::Icon::from("missing-colon");
} catch (const std::invalid_argument& e) {
std::cout << "rejected: " << e.what() << "\n";
}
}
```

## Edge cases and pitfalls

- **`FixedString` size counts the null terminator.** `FixedString{"hi"}` has
`N == 3` and `size() == 2`. When you need to name the type explicitly for JSON,
use the size *with* the terminator: a 13-character string round-trips through
`FixedString<14>`.
- **`FixedString` JSON overflow throws.** Deserializing a string longer than the
fixed capacity is an error, not a silent truncation.
- **Unchecked `parse` dereference is undefined behavior.** `Color::parse(...)`
and `Icon::parse(...)` return `std::optional`; calling `->` on a `nullopt`
result is UB. Always check.
- **MUI shade accessors throw on bad shades.** See the color section above —
prefer the flat aliases (`red_500`) for compile-time-fixed shades.
- **128-bit aliases may be absent.** `i128`/`u128` are only defined when the
compiler provides 128-bit integers. Guard their use with
`#if defined(COMMONS_HAS_INT128)`. They have no default `operator<<`; in JSON
they travel as decimal strings to avoid lossy narrowing.
- **`PrioritizedSet` snapshots priority at insert.** Mutating an element's own
priority afterward does not reorder the set; use `set_priority`. `clear()` does
not reset the internal insertion-order counter.
- **`PrioritizedSet` `from_json` needs recoverable priority.** Reading a set back
works only when `T` derives from `Prioritized` or is otherwise
`Prioritizable`; for a plain `T`, the set is serialize-only.
- **`WithPriority` flavor depends on `T`.** For a non-final class it inherits
`T` (a true *is-a* `T`); for final classes and fundamentals it composes,
exposing the value through `value()` / `operator*` / `operator->`. Either way,
the constructor's first argument is the priority.
- **Flag registration order.** The `GlobalFlagRegistry` is populated at static
initialization; do not query it before `main`.

Thread safety is not documented. The value types (`FixedString`, `Color`,
`Icon`, `DisplayInfo`) are plain data and safe to read concurrently when not
mutated. `FlagSet`, `PrioritizedSet`, and the `GlobalFlagRegistry` are not
synchronized; treat concurrent mutation as unsafe.

## API overview

| Header | Provides |
|----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `commons/commons.hpp` | Umbrella header (all core types + JSON hooks). |
| `commons/version.hpp` | Generated from `version.hpp.in` by the build: `COMMONS_VERSION_MAJOR/MINOR/PATCH/STRING` macros and the `comms::version` / `version_major` / `version_minor` / `version_patch` constants. |
| `commons/types.hpp` | `i8`…`u64`, `f32`/`f64`, `usize`/`isize`, complex aliases (`cs8`…`cs64`, `cu8`…`cu64`, `cf32`/`cf64`), and `i128`/`u128` (gated by `COMMONS_HAS_INT128`). |
| `commons/fixed_string.hpp` | `comms::FixedString` — structural, NTTP-friendly fixed string. |
| `commons/color.hpp` | `comms::Color`, `comms::Hsl`/`comms::Hsv`, and `comms::Colors::css` / `comms::Colors::mui` palettes. |
| `commons/icon.hpp` | `comms::Icon` — an Iconify `set:name` identifier; `Icon::from` / `Icon::parse`. |
| `commons/icons.hpp` | Opt-in predefined catalogs: `comms::Icons::mdi::...`. Not pulled by the umbrella. |
| `commons/literals.hpp` | The `comms::literals` user-defined literals: `"#6366f1"_color` and `"mdi:home"_icon` (both `consteval`). |
| `commons/display_info.hpp` | `comms::DisplayInfo`, the `comms::HasDisplayInfo` trait, free `comms::display_info()`, and the `comms::Displayable` concept. |
| `commons/flag.hpp` | `comms::Flag`/`FlagCategory`, `FlagRef`, `FlagSet`, `GlobalFlagRegistry`, the `IHasFlags`/`HasFlags`/`FlagBuilderMixin`/`FlagBuilderGetters` mixins, and the `COMMONS_*_FLAG*` macros. |
| `commons/origin.hpp` | `comms::IOrigin`/`OriginPtr`, the `OriginKind` CRTP base, built-in `Core`/`Internal`/`External`/`Unknown` origins, `GlobalOriginRegistry`, `COMMONS_REGISTER_ORIGIN`. |
| `commons/prioritized.hpp` | `comms::Prioritized`, `get_priority`, the comparators, `PrioritizedSet`, `PrioritizedBuilder`, and `WithPriority` / `with_priority` / `make_prioritized`. |
| `commons/semver.hpp` | `comms::SemVer` — a Semantic Versioning 2.0.0 value; non-throwing `SemVer::parse`, full §11 ordering, `std::hash`. |
| `commons/version_constraint.hpp` | `comms::VersionConstraint` — an npm-style semver range answering `satisfies(SemVer)`; `VersionConstraint::parse` throws on a malformed sub-version. |
| `commons/config.hpp` | The `COMMONS_WITH_*` feature-gate macros. |
| `commons/json.hpp` | Optional nlohmann/json hooks (inert unless the dependency is present). |

## Examples

Each example is a self-contained program under `examples/`.

| Example | Demonstrates |
|-------------------------------------|----------------------------------------------------------------------------------------------|
| `examples/hello.cpp` | `FixedString`, the numeric aliases, and `version`. |
| `examples/color.cpp` | Parsing, hex/CSS output, HSL transforms, palettes, WCAG, and the formatter specs. |
| `examples/icon.cpp` | Predefined icons, ad-hoc construction, the `set`/`name` accessors, and text output. |
| `examples/display_info.cpp` | Intrusive and non-intrusive `DisplayInfo` attachment and the `Displayable` concept. |
| `examples/flag.cpp` | `FlagSet`, the global registry, and a category-constrained builder read through `IHasFlags`. |
| `examples/origin.cpp` | `IOrigin` kinds, `clone()`, the `DisplayInfo` description, and registry resolution by kind. |
| `examples/prioritized.cpp` | The builder mixin, both `WithPriority` flavors, `PrioritizedSet`, and the comparators. |
| `examples/semver.cpp` | Parsing, full-precedence sorting, `std::format`, and `VersionConstraint` range checks. |
| `examples/json_integration.cpp` | The optional nlohmann/json round-trips (requires the integration). |
| `examples/consumers/fetch_content/` | A standalone downstream project that pulls `commons` via FetchContent. |

## Testing

The test suite uses GoogleTest. With the bundled `Makefile`:

```bash
make test # base library: configure + build + run ctest
make integrations # same, with nlohmann/json forced on (runs the JSON tests)
make examples # build and run every example
```

Equivalently, with raw CMake:

```bash
cmake -S . -B build
cmake --build build
ctest --test-dir build
```

The JSON tests (`tests/test_json.cpp`) are compiled only when the integration is
enabled. With Meson:

```bash
meson setup build-meson -Dtests=true -Dexamples=true
meson test -C build-meson
```

Add `-Djson=true` to force the JSON integration under Meson.

## FAQ

**Do I need to link a library?** No. `commons` is header-only; linking
`commons::commons` only adds the include path and the C++23 requirement.

**What happens if I give `Color::parse` or `Icon::parse` bad input?** They return
`std::nullopt`. The `from` factory on `Icon` throws instead, and the `_color`
literal fails to compile.

**Can I use it in multiple threads?** Thread safety is not documented. The plain
value types are safe to read concurrently; the registries and the mutable
collections are not synchronized.

**Does `FixedString` own its characters?** Yes — it stores them inline. `view()`
returns a `std::string_view` into that storage, so do not let the view outlive
the `FixedString`.

**Why won't `comms::Icons::mdi::...` compile?** Add
`#include `; the predefined catalogs are intentionally left
out of the umbrella header.

**How do I get the JSON hooks?** Include `` (or the umbrella,
which includes it) and make sure `` is reachable. To force the
dependency to be fetched and linked, configure with
`-DCOMMONS_WITH_NLOHMANN_JSON=ON` (CMake) or `-Djson=true` (Meson).

## Contributing

Contributions to the library are welcome! If you encounter any issues or have suggestions for
improvements,
please feel free to submit a pull request or open an issue on the project's repository.

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.