strings
June 29, 2026 · View on GitHub
A small C++20 toolkit of general string utilities: std::format-style
formatting, list joining, splitting, padding, trimming, case conversion,
prefix/suffix tests, and human-readable byte/time formatters. Extracted from
Xapiand.
What it is
One namespace, strings, with the everyday helpers you reach for when working
with text. The header holds the inline and template utilities:
format(fmt, args...)— a thin wrapper overstd::vformat. On a bad format string it falls back silently to the raw, unformatted string instead of throwing.join(values, delim[, last_delim][, pred])— join astd::vectorinto a string, optionally with a distinct final delimiter ("a, b and c") and an optional predicate that drops elements before joining.split(value, sep)— split a string into astd::vectorof pieces.indent(str, fill, level[, indent_first])— prefix each line withlevelcopies of a fill character.left/center/right— width-based padding.startswith/endswith(string orchar),hasupper— predicates.upper/lower/inplace_upper/inplace_lower— case conversion.replace/inplace_replace— substitute every occurrence.ltrim/rtrim/trim— strip leading/trailing whitespace (returns astd::string_viewinto the input).
The compiled side (strings.cc) adds the humanizers: from_bytes,
from_time, from_small_time, and from_delta, which render a number with a
human-readable unit (2KiB, 5s, 2ms, ...) and an optional ANSI color.
Install
This is not header-only. It ships a compiled translation unit, strings.cc,
plus the header strings.hh. You build and link the .cc; the header holds the
inline/template utilities. It requires C++20 (it uses std::format).
It has five sibling dependencies, all part of the same family and all pulled in for you by CMake:
- char-classify —
chars::toupper/chars::tolowerfor the case helpers. - split —
Split<>, used bysplit()andindent(). - static-string — the
to_string()overload forstatic_string. - repr — kept from the original header.
- term-color — the
rgb()/CLEAR_COLORpalette the humanizers color with.
With CMake FetchContent:
include(FetchContent)
FetchContent_Declare(
strings
GIT_REPOSITORY https://github.com/Kronuz/strings.git
GIT_TAG main
)
FetchContent_MakeAvailable(strings)
target_link_libraries(your_target PRIVATE strings::strings)
CMakeLists.txt requests cxx_std_20 PUBLIC and links the five deps PUBLIC,
so every header strings.hh includes resolves on the include path with no extra
wiring on your side. Then:
#include "strings.hh"
The header keeps its original filename, so a codebase that already
#include "strings.hh" just needs this repo on its include path.
Usage
#include "strings.hh"
// std::format-style formatting; bad format strings fall back to the raw text.
strings::format("{} + {} = {}", 2, 3, 5); // "2 + 3 = 5"
// Join with a delimiter, or a distinct last delimiter.
std::vector<std::string> w{"a", "b", "c"};
strings::join(w, ", "); // "a, b, c"
strings::join(w, ", ", " and "); // "a, b and c"
// Split, then case / trim / predicates.
auto parts = strings::split(std::string("a,b,c"), ','); // {"a","b","c"}
strings::upper(std::string_view("MixEd")); // "MIXED"
strings::trim(" hi "); // "hi"
strings::startswith("hello", "he"); // true
strings::endswith("hello", 'o'); // true
// Padding and indentation.
strings::right("hi", 5); // " hi"
strings::indent("a\nb", ' ', 2); // " a\n b"
// Humanizers (optionally colored).
strings::from_bytes(2048); // "2KiB"
strings::from_time(5); // "5s"
strings::from_delta(std::chrono::nanoseconds(1500000000));
Build & test
cmake -B build -DCMAKE_CXX_COMPILER=$(brew --prefix llvm)/bin/clang++
cmake --build build
ctest --test-dir build
The first configure fetches the five sibling libraries over the network. The
test exercises format (substitution, padding, and the silent fallback),
join (delimiter / last-delimiter / numeric / predicate forms), split,
indent, the padders, the prefix/suffix/case predicates, the case helpers
(value, rvalue, in-place), replace, the trim trio, and the from_bytes /
from_time / from_small_time / from_delta humanizers. It prints
all strings tests passed and exits 0.
Provenance
Extracted from Xapiand. strings.hh /
strings.cc were copied with one decoupling change: the original header
depended on Xapiand's logging (#include "log.h" and an L_EXC(...) call in the
format error path). A standalone string library must not log, so both were
removed; the format catch now keeps only the silent fallback (return the raw,
unformatted string). The five local includes were wired to their standalone
sibling libraries through CMake. See ARCHITECTURE.md for the
design and AGENTS.md for the repo map and invariants.
License
MIT, Copyright (c) 2015-2019 Dubalu LLC. See LICENSE.