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, an IOrigin
provenance envelope, and a strong-typed Id<Tag, Repr> identifier. Every type
carries optional nlohmann/json serialization that turns on by itself when the
dependency is available; Id<Tag, ulid::Ulid> lights up the same way when
cpp-ulid is on the include path. The namespace is comms; headers live under
<commons/...>.
- 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, andIconare literal types usable inconstexprandstatic_assertcontexts and as non-type template parameters. - Not ideal for large, hot containers:
PrioritizedSetandFlagSetare 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.
#include <commons/commons.hpp>
#include <iostream>
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.
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.
The most reliable option: drop the repository into your tree (a submodule or copy) and add it.
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.
include(FetchContent)
FetchContent_Declare(
commons
URL https://github.com/aurimasniekis/cpp-commons/archive/refs/tags/v0.1.5.tar.gz
URL_HASH SHA256=1c98ee66a8ac5bfd8b8580ddf775e5d34018ce21670c9e14f9cc2179901f521f
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_MakeAvailable(commons)
target_link_libraries(example PRIVATE commons::commons)By default no optional dependency is fetched: the JSON hooks and the ULID
Id repr both auto-detect their backing headers. To force an integration on
— which additionally fetches the dependency and hard-defines the gate macro —
configure with -DCOMMONS_WITH_NLOHMANN_JSON=ON (fetches nlohmann/json 3.12.0
or newer) and/or -DCOMMONS_WITH_ULID=ON (fetches cpp-ulid 1.0.0).
After cmake --install, the package is consumable via find_package:
find_package(commons 0.1 REQUIRED)
target_link_libraries(my_app PRIVATE commons::commons)Install rules are automatically disabled when nlohmann/json or cpp-ulid was
fetched (a fetched dependency cannot be re-exported). For a clean install,
leave the gate options OFF (the default) or provide the dependencies through
system packages.
commons_dep = dependency('commons', version: '>=0.1.0',
fallback: ['commons', 'commons_dep'])Meson options mirror the CMake ones: -Djson=true|false,
-Dulid=true|false, -Dtests=true|false, -Dexamples=true|false. A
pkg-config file is generated on install.
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.
- C++23. The library uses structural non-type template parameters,
constexprstd::string_view,std::format, and concepts. CMake enforces this withtarget_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 dependencies: nlohmann/json
3.12.0 or newer enables
<commons/json.hpp>; cpp-ulid 1.0.0 enablescomms::Id<Tag, ulid::Ulid>.
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.
#include <commons/fixed_string.hpp>
#include <iostream>
template <comms::FixedString Name>
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<M> (different sizes simply compare unequal).
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.
#include <commons/literals.hpp> // brings in color.hpp and the _color literal
#include <iostream>
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";
}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.
#include <commons/literals.hpp> // brings in icon.hpp and the _icon literal
#include <iostream>
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";
}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.
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.
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<IOrigin>). 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.
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.
A Semantic Versioning 2.0.0 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).
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.
A strong-typed identifier: a Repr value tagged with a phantom Tag so ids
of different kinds cannot be mixed even when the underlying representation is
identical. The allowed reprs are deliberately narrow — the unsigned
fixed-width ints (std::uint8_t / 16 / 32 / 64), std::string, and —
gated by COMMONS_WITH_ULID — ulid::Ulid. Aliases Uint8Id<Tag> …
Uint64Id<Tag>, StringId<Tag>, and UlidId<Tag> save typing; the
COMMONS_DEFINE_UINT{8,16,32,64}_ID, COMMONS_DEFINE_STRING_ID, and
COMMONS_DEFINE_ULID_ID macros emit both a Name##Tag (with a
static constexpr std::string_view name) and the matching using alias in
one shot. to_string delegates to the underlying repr; display_string
prefixes it with Tag::name for named tags; and the std::formatter<Id>
specialization inherits from std::formatter<Repr> so any spec the wrapped
type accepts (e.g. "{:#x}" for the uint reprs) works transparently.
#include <commons/commons.hpp>
#include <format>
#include <iostream>
#include <optional>
int main() {
using comms::Color;
// Parsing returns std::optional — always check before dereferencing.
if (std::optional<Color> 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 only50, 100, 200, …, 900, andaccent(shade)only100, 200, 400, 700. Any other value throwsstd::out_of_range. The flat aliases (red_500,red_a200) cannot be misindexed, so prefer them for fixed shades.
#include <commons/icons.hpp> // opt-in predefined catalogs
#include <iostream>
int main() {
// Predefined Material Design Icons (only via <commons/icons.hpp>).
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<comms::Icon> 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.hppdoes not include it. Add#include <commons/icons.hpp>in the translation units that needcomms::Icons::mdi::....
There are two ways to attach DisplayInfo, and a concept to detect it.
#include <commons/display_info.hpp>
#include <iostream>
// 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<Severity> {
static const DisplayInfo& display_info() {
static const DisplayInfo info{.name = "Severity",
.color = Colors::css::orange};
return info;
}
};
template <typename T>
requires comms::Displayable<T>
void show(std::string_view label) {
const auto& d = comms::display_info<T>();
std::cout << label << ": " << d.name.value_or("(none)") << "\n";
}
int main() {
show<Widget>("intrusive");
show<Severity>("trait");
static_assert(!comms::Displayable<struct Plain>); // no metadata → not Displayable
}comms::display_info<T>() dispatches to whichever mechanism is present.
comms::Displayable<T> reports whether either exists, so you can constrain
templates on it. Calling display_info<T>() on a type that has neither is a
compile error, by design.
#include <commons/flag.hpp>
#include <iostream>
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<Config, Network> {};
} // namespace
int main() {
comms::FlagSet set;
set.insert<Verbose>();
set.insert<Ipv6>();
set.insert<Ipv6>(); // 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<Ipv6>().set_flag<KeepAlive>(true); // fluent, returns Config&
// cfg.flag<Verbose>(); // will not compile: Verbose is not in Network
const comms::IHasFlags& view = cfg; // read polymorphically
std::cout << "has ipv6? " << view.has_flag<Ipv6>() << "\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.
#include <commons/prioritized.hpp>
#include <iostream>
#include <string>
// Carry a mutable priority via the CRTP builder mixin.
struct Adapter : comms::PrioritizedBuilder<Adapter> {
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<int>
std::cout << *level << " @ " << level.priority() << "\n";
// A set that iterates in (priority asc, insertion-order asc) order.
comms::PrioritizedSet<std::string> 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<T> order std::shared_ptrs for use as the comparator
of a std::set.
Pitfall —
insertnever updates an existing priority. Likestd::set, re-inserting an equal value is a no-op; the originally stored priority stays. Useset_priority(value, p)to change it. Also notePrioritizedSet'sinsert/find/erase(value)are O(n) — it targets config-sized collections, not large data sets.
#include <commons/semver.hpp>
#include <commons/version_constraint.hpp>
#include <algorithm>
#include <iostream>
#include <vector>
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<comms::SemVer> 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.
#include <commons/id.hpp>
#include <format>
#include <iostream>
#include <string>
// Both macros emit a UserIdTag/OrderIdTag with a `name` member and a `using`
// alias. They have to be invoked at namespace (or class) scope, since they
// declare a struct with a static member.
COMMONS_DEFINE_UINT64_ID(UserId, "user");
COMMONS_DEFINE_STRING_ID(OrderId, "order");
int main() {
UserId u{1234567u};
OrderId o{std::string{"o-abc-1"}};
// display_string prefixes with the tag name; to_string is the bare repr.
std::cout << comms::display_string(u) << "\n"; // user/1234567
std::cout << comms::display_string(o) << "\n"; // order/o-abc-1
// The std::formatter inherits from std::formatter<Repr>, so any spec the
// underlying type accepts works on the Id directly.
std::cout << std::format("{:#x}", u) << "\n"; // 0x12d687
// Different tags are unrelated types — won't compile:
// bool same = (u == UserId{0u}); // OK
// bool same = (u == OrderId{"o-abc-1"}); // type mismatch
}With nlohmann/json available, every public type gains to_json/from_json.
// Build with -DCOMMONS_WITH_NLOHMANN_JSON=ON, or simply have nlohmann/json
// on the include path.
#include <commons/commons.hpp>
#include <commons/json.hpp>
#include <nlohmann/json.hpp>
#include <iostream>
int main() {
using json = nlohmann::json;
comms::FixedString id{"order.created"};
json j = id; // -> "order.created"
auto back = j.get<comms::FixedString<14>>(); // 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<T> ⇄ {"priority":N,"value":<T>} and
PrioritizedSet<T> ⇄ a sorted array — both only when T is itself
JSON-serializable; Id<Tag, Repr> ⇄ the inner Repr's natural JSON (a number
for the uint reprs, a string for std::string, the ULID string when ULID is
also enabled).
The library uses three distinct strategies, by type:
std::optionalfor parsing.Color::parse,Color::parse_hex, andIcon::parsereturnstd::nullopton malformed input. Check before dereferencing.- Exceptions for hard failures.
Icon::fromthrowsstd::invalid_argument(malformed) orstd::length_error(too long).Color's MUI shade accessors (operator[],accent) throwstd::out_of_range. Thestd::formatspecializations throwstd::format_erroron a bad spec. The JSONfrom_jsonhooks throw nlohmann's exception type when a value is invalid (an unparseable color, a string too long for aFixedString, an unregistered flag name, …). - Compile-time errors. The
_colorand_iconuser-defined literals areconsteval, so a malformed literal fails to compile.Icon::fromin aconstexprcontext turns its throws into compile errors. Callingdisplay_info<T>()on a non-Displayabletype is a compile error.
#include <commons/color.hpp>
#include <commons/icon.hpp>
#include <iostream>
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";
}
}FixedStringsize counts the null terminator.FixedString{"hi"}hasN == 3andsize() == 2. When you need to name the type explicitly for JSON, use the size with the terminator: a 13-character string round-trips throughFixedString<14>.FixedStringJSON overflow throws. Deserializing a string longer than the fixed capacity is an error, not a silent truncation.- Unchecked
parsedereference is undefined behavior.Color::parse(...)andIcon::parse(...)returnstd::optional; calling->on anulloptresult 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/u128are only defined when the compiler provides 128-bit integers. Guard their use with#if defined(COMMONS_HAS_INT128). They have no defaultoperator<<; in JSON they travel as decimal strings to avoid lossy narrowing. PrioritizedSetsnapshots priority at insert. Mutating an element's own priority afterward does not reorder the set; useset_priority.clear()does not reset the internal insertion-order counter.PrioritizedSetfrom_jsonneeds recoverable priority. Reading a set back works only whenTderives fromPrioritizedor is otherwisePrioritizable; for a plainT, the set is serialize-only.WithPriority<T>flavor depends onT. For a non-final class it inheritsT(a true is-aT); for final classes and fundamentals it composes, exposing the value throughvalue()/operator*/operator->. Either way, the constructor's first argument is the priority.- Flag registration order. The
GlobalFlagRegistryis populated at static initialization; do not query it beforemain.
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.
| 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<N> — 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<T> trait, free comms::display_info<T>(), and the comms::Displayable<T> 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<FixedString, Derived> CRTP base, built-in Core/Internal/External/Unknown origins, GlobalOriginRegistry, COMMONS_REGISTER_ORIGIN. |
commons/prioritized.hpp |
comms::Prioritized, get_priority, the comparators, PrioritizedSet<T>, PrioritizedBuilder<Derived>, and WithPriority<T> / 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/id.hpp |
comms::Id<Tag, Repr> — strong-typed identifier; Uint{8,16,32,64}Id/StringId/UlidId aliases, to_string/display_string, and the COMMONS_DEFINE_*_ID macros. |
commons/config.hpp |
The COMMONS_WITH_* feature-gate macros. |
commons/json.hpp |
Optional nlohmann/json hooks (inert unless the dependency is present). |
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/id.cpp |
The Id<Tag, Repr> macros, display_string, inherited formatter specs, and the ULID repr. |
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. |
The test suite uses GoogleTest. With the bundled Makefile:
make test # base library: configure + build + run ctest
make integrations # same, with nlohmann/json and cpp-ulid forced on
make examples # build and run every exampleEquivalently, with raw CMake:
cmake -S . -B build
cmake --build build
ctest --test-dir buildThe JSON tests (tests/test_json.cpp) are compiled only when the integration is
enabled. With Meson:
meson setup build-meson -Dtests=true -Dexamples=true
meson test -C build-mesonAdd -Djson=true and/or -Dulid=true to force the respective integration
under Meson.
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 <commons/icons.hpp>; the predefined catalogs are intentionally left
out of the umbrella header.
How do I get the JSON hooks? Include <commons/json.hpp> (or the umbrella,
which includes it) and make sure <nlohmann/json.hpp> is reachable. To force the
dependency to be fetched and linked, configure with
-DCOMMONS_WITH_NLOHMANN_JSON=ON (CMake) or -Djson=true (Meson).
How do I get the ULID Id repr? comms::Id<Tag, ulid::Ulid> (and its
UlidId<Tag> / COMMONS_DEFINE_ULID_ID shortcuts) light up when <ulid/ulid.h>
is on the include path. To force the dependency to be fetched and linked,
configure with -DCOMMONS_WITH_ULID=ON (CMake) or -Dulid=true (Meson).
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.
This project is licensed under the MIT License. See the LICENSE file for details.