Use the most recent tagged release.
The include and src directories report 100% line and branch coverage, with a
few exemptions made.
People say that you should not micro-optimize. But if what you love is micro-optimization... that's what you should do. - Linus Torvalds
libhatchet is a fast-compiling, lightweight, bespoke C17/C++23 alternative to
the C++ standard library, designed for cross-compilation to resource-constrained
targets such as DSPs, FPGAs, ASICs and WebAssembly. When a modern toolchain is
unavailable it falls back to a C++11 compiler and C99 libraries, and it never
depends on the C++ standard library. If you have a low-level mindset, the
developer experience is better than with the standard library. Template
instantiation errors are easier to read, hxassertmsg formats your assert
message before setting a breakpoint for you, and there is nothing unnecessary to
step through in the debugger.
Expect very fast compile times with ninja, ccache, clang and a C++ module.
The implementation stays clean under every sensible warning flag and under the GCC and Clang sanitizers, and asserts are used generously. The process heap is unused except when initializing system allocators.
-
Library hardening and asserts are controlled by
HX_HARDENING_MODE, for example-DHX_HARDENING_MODE=HX_HARDENING_MODE_STANDARD. See<hx/libhatchet.h>for the kinds of asserts available. Null pointer checks only happen at the debug level of hardening.HX_HARDENING_MODE_NONEomits library hardening and disables every assert.HX_HARDENING_MODE_STANDARDhardens while saving space by omitting verbose output. Meets the requirements of the C++ standard.HX_HARDENING_MODE_VERBOSEadds verbose messages and suits internal releases. Meets the requirements of the C++ standard.HX_HARDENING_MODE_DEBUGprovides comprehensive asserts and verbose output.
-
Performance Focus: This is systems code. Everything should be optimized and cache-coherent without causing code bloat. Exceptions and RTTI are avoided for efficiency. If exceptions are enabled and one gets that far, the test driver and the console will catch it.
__restrictis used extensively, and inlining attributes flatten the inner loops, as is standard practice for standard libraries. -
Portability: libhatchet runs on top of any old embedded C99 library. musl libc is recommended for embedded Linux and is widely packaged: https://musl.libc.org/. No other C++ runtime or C++ code is required. Threading uses pthreads or C11's
<threads.h>, both widely implemented standards. See.clang-tidyfor a discussion of linting rules and portability concerns. Every public symbol other than methods and fields starts withHXorhx, and every non-public symbol in the headers ends with an underscore. A subset of the Google Test macros is also optionally provided. -
Containers: A set of containers designed for environments where reallocation is not allowed. Reach for them in low-level work where the standard container libraries cause code bloat, memory fragmentation and poor cache coherence. If those are not your concerns, consider using additional libraries. No tree data structures will be provided. Exceptions are not supported for efficiency reasons, so
noexceptis applied widely to defend against data loss from the unexpected use of exceptions.There are two array classes, and both support two allocation modes:
compile time capacity > 0 hxallocator_dynamic_capacityhxarrayCompile-time fixed size, inline storage Variable initial size, non-resizable, non-reallocating heap storage [1] hxvectorResizable, fixed capacity, inline storage Resizable, variable initial capacity, non-reallocating heap storage [1] Not provided by the standard.
See the headers at include/hx/ for the remaining containers. They generally use the same names as the standard.
-
Algorithms: The implementation uses the
__restrictkeyword wherever appropriate. See<hx/hxalgorithm.hpp>for the standard algorithms and<hx/hxsort.hpp>for comparison based sorting and lookup. Prefer radix sorting with<hx/hxradix_sort.hpp>when you want Ξ(n) sorting. Less common functions have been omitted to reduce compile time. Random access iterators are required for a lot of things, but the only relational operator used is<. This codebase tries not to give an AI rope to hang itself with. Showing inner-loop assembly to an AI is also advised. -
Memory Management: Fast and deterministic. Several allocation semantics are supported, which matters most when crashing from memory fragmentation is unacceptable. If you make a lot of temporary allocations, expect roughly 30% memory and 30% performance improvements overall from adding just a few RAII scopes. Complex applications can use an array of variable sized stacks. To use tools like heaptrack, disable the memory manager with
-DHX_USE_MEMORY_MANAGER=0. Leak tracking is provided. -
Testing Framework: A non-allocating, lighter, debuggable reimplementation of the core Google Test functionality.
-
Fast builds: Lightweight headers result in very fast builds when used with
ninjaandccache. The module loads quickly with Clang and MSVC. -
Pretty Printers: GDB-compatible pretty printers let debuggers and most code editors display container contents in a human-readable format.
-
C99 Compatibility: Logging, asserts and memory management are available in plain C99 through
<hx/libhatchet.h>. -
AI Friendly: Anything with the same name as the standard generally works the same way as the standard, so an AI can apply its existing knowledge of standard C++ directly. It also already knows how to use the test macros when writing tests. Using tabs instead of spaces reduces token use.
-
Console: An embedded command processor that binds C++ functions automatically using templates. Use it for interactive target debugging without recompilation, for config files, or for configuration from the command line. The syntax is just
verb [arg ...]. Note: This is the only line based file reader provided. -
Profiling System: Samples processor cycles to build a hierarchical timeline capture compatible with Chrome's
chrome://tracingviewer. Navigate the capture with the W, A, S and D keys. Uncommon hardware may need one line of assembly. -
Task Queue: An unopinionated task queue with priorities and a worker pool. An execution graph is also available as a layer on top.
-
Smart Pointers: Because the focus is non-dynamic allocation, shared and weak pointers are not provided. Use
hxhandle_tablefor weak references instead.hxptrimplementsstd::unique_ptr.hxoptionalimplementsstd::optional, giving the semantics of an optional temporary allocation with safe access and without the allocation itself.hxrefis a non-owning pointer wrapper with the same safe access semantics. -
64-bit Ready: Designed for both 32-bit and 64-bit targets.
hxsize_tis signed because that enables important optimizations. It is equivalent toptrdiff_t, but could be made 32-bit on a 64-bit platform if desired. -
constexpr Ready: C++11
constexpris used where possible. Asserts, the algorithms,hxconstexpr_list,hxbitsetandhxrandomsupportconstevalin C++23.
The documentation is at https://whatchamacallem.github.io/libhatchet.
Run doxygen with no arguments to generate docs/index.html. The markdown
source for the documentation is in the header files at include/hx/ and reads
well as-is. The stylesheet was tested with Doxygen 1.15.0.
Read <hx/hxsettings.h>. A number of important things can be configured on the
compiler command line, such as whether the library is wrapped in a namespace.
Almost every reasonable GCC and Clang warning flag should be safe to enable, and
Clang-Tidy is in use. Every release is tested against glibc and musl on Ubuntu
26.04 LTS using g++-16 and clang-22. g++-10 and clang-11 are tested periodically
with C++11. The latest MSVC 2022 should work with most warnings enabled,
although the MSVC static analyzer is not being tested. Run debian_packages.sh
to install the packages the test scripts need on a Debian based distribution
such as Ubuntu.
The pico directory contains a port to the Raspberry Pi Pico 2. There is almost
nothing to do when porting because libc provides almost all of the device
abstraction required. Some devices require you to call the C++ global
constructors yourself before calling main(). Check the sample code that came
with your board.
The scripted builds exercise the following toolchains, language modes and
HX_HARDENING_MODE combinations:
| Script | Toolchain | Modes | Hardening | Notes |
|---|---|---|---|---|
debugbuild.sh |
clang |
C17, C++23 | 3 | 32-bit debug build with ccache and no exceptions/RTTI. |
testcmake.sh |
cmake |
C17, C++23 | 3 | Uses the default compiler and the real Google Test and runs hxtest and Clang-Tidy. |
testcoverage.sh |
gcc |
C99, C++23 | 3 | Builds with --coverage, enables HX_TEST_ERROR_HANDLING=1 and emits coverage_details.html. |
testmatrix.sh |
gcc, clang |
C99, C17, C++11, C++23 | 0-3 | Sweeps optimization levels and ASan/UBSan/TSan/MSan and sets HX_USE_THREADS=1/11. |
teststrip.sh |
musl-gcc |
C17, C++11-23 | 0 | Size-focused static build with allocator/library stripping. |
testwasm.sh |
emcc |
defaults | 3 | WebAssembly build with the memory manager disabled and pthreads enabled. Emscripten defaults are Clang-based C/C++. |
testall.sh runs all of the above and also enforces certain naming conventions.
testmsvc.bat tests 32 and 64-bit debug and release configurations on Windows
and automatically discovers the installed version of MSVC.
debugbuild.sh and teststrip.sh detect when C++23 is missing and fall back to
C++20 to test g++-10 and clang-11.
The test scripts are at the top level for easy access. Meson and CMake scripts are also available.
- π
.vscode- The vscode configuration files. - π
example- Simple program showing usage. - π
gdb- GDB pretty printers, one per container, loaded by.gdbinit. - π
include- Add this directory to your include path.- π
hx- The public<hx/hx*>headers, one class per header.- π
detail- Internal implementation headers.
- π
- π
- π
src- The runtime implementation. Add these files to your build. - π
test- Optional GoogleTest-style test suites.
libhatchet.his the entry point. It is also the header to use from C code. It provides the core macros, the assert family, memory management and feature detection, and it pulls inhxsettings.handhxmemory_manager.h, which cannot be included directly.- Choose an
HX_HARDENING_MODEon the compile line.hxassertandhxassertmsgare active only atDEBUG,hxassert_hardatSTANDARDand above, andhxassert_alwaysin every mode. Null pointer checks only happen inDEBUG. - Log through
hxlog,hxlog_warning,hxlog_releaseandhxlog_console. They all route throughhxlog_handler, which can be replaced. hxsettings.hhandles compiler detection and polyfills and documents the default for every compile-line option. SetHX_USE_NAMESPACE=<identifier>to wrap the whole library in a namespace of your choosing.hxmemory_manager.hallocates by allocator ID.hxsystem_allocator_heapis the normal heap,hxsystem_allocator_permanentis never freed, andhxsystem_allocator_stack_0 + nselects a temporary stack that resets when the enclosing RAIIhxsystem_allocator_scopecloses.hxmalloc_exttakes an explicit ID, while plainhxmallocuses the current scope.hxutility.hprovides the metaprogramming basics:hxmove,hxforwardand the type traits.hxinitializer_list.hppeither providesstd::initializer_listor wraps the system version, depending onHX_USE_LIBCXX.
Differences from the standard versions are listed.
hxallocatoris the static-or-dynamic capacity backing used by the containers. Prefer static capacities where allocation is a concern.hxarrayis the fixed-sizestd::arrayanalog.hxvectorcovers bothstd::vectorandstd::inplace_vector. Both have the algorithms as methods:find,find_if,all_of,any_of,for_each,binary_search,sortandinsertion_sort. Both provideget(index)returninghxnullwhen out of range,size_bytes,memcpy/memsetfills and construction and assignment from C-style arrays.hxvectoraddsfull, Python-styleoperator+=append and concatenate,generate_n, Ξ(1)erase_unorderedanderase_if_unordered, andmake_heap/push_heap/pop_heap/erase_if_heap, which replacestd::priority_queue. Anhxvectoralso works directly as an output iterator, replacingstd::back_insert_iterator.hxdequeis a fixed-capacity ring buffer with a power-of-two capacity. Every operation, includingoperator[], is Ξ(1), andfullis provided.hxbitsetis a fixed-size bit set stored insize_twords with no heap use. Unlikestd::bitsetit exposes the underlying words viadataandbytesand loads raw bytes withload. Shifts and bitwise operators are provided.hxlistis an intrusive doubly linked XOR list at half the size of a conventional one.hxconstexpr_listhas the same interface but uses normal pointers so it works in constexpr code. Nodes are owned through a configurable deleter, and subclasses of the node type may be stored heterogeneously.extract,pop_frontandpop_backreturn an owninghxptr,release_allabandons ownership, andremove_if,spliceandreverseare provided.hxflat_mapandhxflat_setare sorted parallel-array replacements for map/multimap and set/multiset. Expect O(log2(n)) lookups and O(n) insertions and removals.findreturns a pointer to the mapped value orhxnullinstead of an iterator, and elements are addressable by position withoperator[]andget.hxhash_tableis a fixed-size hash table with singly linked buckets embedded in the nodes. Additional node types are inhxhash_table_nodes.hpp.find(key, previous)walks duplicate keys withoutequal_range,replaceswaps a node in with a single lookup,extractreturns an owninghxptr, andrelease_allandrelease_keyunlink nodes without deleting them.load_factorandload_maxreport bucket usage.hxhandle_tablemaps handles to pointers andhxhandle_mapmaps handles to values. Both use 64-bit generational handles, so use them when stale references need to be detected. Both provideerase_ifandextract.hxhandle_mapis a slot map keeping its values contiguous for iteration.hxfree_listis a fixed-capacity free-list allocator built onhxallocator.allocateandtry_allocateconstruct aTand return anhxptrwhose deleter returns the slot to the pool.is_allocatortests whether a pointer came from the pool.hxoptionalimplementsstd::optional<T>,hxrefimplementsstd::optional<T&>, which the standard does not provide before C++26, andhxptris a unique owning pointer with a monadicand_thenand a deleter that may decline deletion, e.g.hxdo_not_delete.- Every keyed container and sort compares keys through the free functions
hxkey_equal,hxkey_lessandhxkey_hashinhxkey.hpp. Overload them for custom key types. The defaults require only==and<. - Containers with dynamic capacity move and
hxswapin Ξ(1) without touching elements.
Differences from the standard versions are listed.
hxalgorithm.hppprovides searching and set utilities taking callables:hxall_of,hxany_of,hxcount_if,hxexchange,hxfind_if,hxfor_each,hxmerge,hxminmax,hxset_difference,hxset_intersection,hxset_unionandhxunique.hxmergeand the set operations move-assign out of their inputs and accept anhxvectorreference directly as the output iterator, appending to it withoutstd::back_inserter, e.g.hxmerge<const int*, hxvector<int>&>(...).hxminmaxreturns a result withminandmaxiterator fields in a single pass.hxsort.hppprovides the comparison sorts and lookup.hxsortis an introsort, andhxinsertion_sortandhxheapsortare also available.hxbinary_search,hxlower_boundandhxupper_boundare provided, and unlikestd::binary_search,hxbinary_searchreturns an iterator to the first match instead ofbool.- Every comparison routes through the
hxkey_lessandhxkey_equalfree functions or an explicit callable, so custom key types overload two free functions once instead of passing comparators everywhere. - For scalar keys up to 32 bits,
hxradix_sortsorts in Ξ(n) time. Its implementation is insrc/hxradix_sort.cpp. It sortshxradix_sort_key<key_t, value_t>pairs and handles signed andfloatkeys with bit manipulation.hxradix_sort11uses 11-bit digits for large workloads, and the temporary buffers come from a caller-selected memory manager allocator ID. hxrandomis a 64-bit LCG intended for generating test data. Beyondoperator()it providesu8/u16/u32/u64,f01/d01in[0..1),range(base, size)overloads that use a floating point multiply instead of integer modulo,readto fill a buffer with random bytes, and independently seeded streams. It works inconstexprcode, so random test data can be built at compile time.
hxfileis RAII file I/O.HX_USE_FILE_IOselects the backend:1is the libc backend insrc/hxfile_c.cppand2is the POSIX backend insrc/hxfile_posix.cpp.hxconsoleis a debug and remote command console that also parses config files such asexample.cfg. Register bindings at file scope withhxconsole_command()andhxconsole_variable(), or use the_namedvariants to choose the name yourself.hxprofileris a cycle-sampling scope profiler that emitschrome://tracingcaptures. Mark a scope withhxprofile_scope("label")and control capture withhxprofiler_start(),hxprofiler_stop()andhxprofiler_log(). It compiles away entirely unlessHX_USE_PROFILER=1.hxtaskandhxtask_queueform a worker-pool priority queue.hxtask_dag_nodelayers DAG dependencies on top.hxthreadwraps pthreads or C11<threads.h>withhxmutex,hxcondition_variable,hxunique_lock,hxthreadandhxthread_local.hxtestis the GoogleTest-compatible subset that the test suite itself runs on. SetHX_USE_GOOGLE_TEST=1to run the same tests against the real Google Test.
This project was started for the author's own personal use, and it tries to be complete enough for ordinary C++ programmers. It predates a lot of similar functionality in the standard. If you find something missing, odds are your favorite AI already knows how to add it, or it was omitted because the C library was deemed sufficient.
That said, some functionality of the C++ standard library is not worth reimplementing here. If you need these things, use the standard library shipped with your compiler.
- Atomics. The C version of
<stdatomic.h>is incompatible with g++. - The iterators library. This codebase intentionally deemphasizes iterators.
- The ranges library. This would be a large and pointless rewrite.
- The strings library. Strings are allocation intensive. See the
{fmt}project.
- musl libc - The recommended C library for using libhatchet in a freestanding environment.
- {fmt} - A micro-optimized version of
std::format, with nice extras like console colors and a fastprintf.
Β© 2017-2026 Adrian Johnston. This project is licensed under the terms of the MIT
license found in the LICENSE.md file.
πͺπͺπͺ
