Welcome to CSalt++ documentation!
The csalt++ project is a modern C++ library designed for safety-critical and high-performance applications requiring MISRA C++ compliance. It provides comprehensive error handling and efficient numerical computing with matrices and vectors, with optional compile-time exclusion of dynamic allocation for embedded and safety-critical systems.
This library builds upon the ideas developed in the original C version of CSalt, enhancing them with templates, operator overloading, and runtime format adaptation, while maintaining compliance with MISRA C++ guidelines through careful design choices including optional static-only compilation, fixed-size buffers, and predictable behavior.
CSalt++ targets performance-critical applications such as automotive systems, aerospace software, medical devices, scientific simulations, and engineering solvers where both safety and performance are paramount. It provides SIMD-accelerated numerical operations with support for x86 instruction sets (AVX2, AVX-512, SSE2, SSE3, SSE4.1) and ARM architectures (NEON, SVE, SVE2).
Why CSalt++
C++ offers many improvements over C, but working with numerical data still involves challenges:
On the fly dynamic memory allocation which drives time complexity
Standard exception handling violates MISRA C++ rules (dynamic allocation, non-deterministic behavior)
Most C++ libraries cannot disable dynamic allocation for safety-critical use
CSalt++ addresses these issues by offering:
Custom allocators for dynamic memory management
MISRA C++ compliant error hierarchy with zero dynamic allocation
Expected<T> for deterministic, exception-free error handling
STATIC_ONLY flag to exclude all dynamic allocation at compile time
Dual-mode design: full features with dynamic allocation OR static-only for certification
Predictable, auditable behavior suitable for certification
Core Features
Error Classes
STL-independent exception hierarchy - Alternative to standard library exceptions with no dynamic allocation
Three-level taxonomy - Base Error → 9 categories → 40+ specific types (e.g., Error → ArgumentError → NullPointerError)
Fixed-size messages - All errors use 256-byte stack buffers, ensuring predictable memory usage
Default messages - Each error type has a predefined message that can be overridden via constructor
Message composition - Helper methods for prepending/appending context to standard messages
Dual-use design - Works with traditional
throw/catchor modernExpected<T>patternMISRA compliant - Available in both standard and
STATIC_ONLYmodesType-safe handling - Catch specific errors (
DivByZeroError), categories (MathError), or base (Error)
Expected Class
Errors as values - Type-safe representation of computations that may succeed (
T) or fail (Error)Explicit error handling - Forces callers to check for errors before accessing values, preventing forgotten checks
Zero overhead - No exception unwinding, no dynamic allocation, deterministic performance
Stack-based storage -
sizeof(Expected<T>) = sizeof(bool) + sizeof(T) + sizeof(Error)Simple API -
setValue()/setError()to construct,hasValue()/hasError()to query,value()/error()to accessSafe defaults -
valueOr()method provides fallback values without checkingBool conversion - Use in conditionals:
if (result) { /* success */ }MISRA compliant - Fully compliant in both standard and
STATIC_ONLYmodesRecommended pattern - Preferred over exceptions for safety-critical and real-time code
Smart Pointers
RAII ownership -
UniquePtr<T, Deleter>provides deterministic single-owner lifetime management with custom deletersShared ownership -
SharedPtr<T>provides reference-counted shared ownership with allocator-aware control blocksWeak references -
WeakPtr<T>enables non-owning observation of shared resources without extending lifetimeAllocator-aware - All pointer types work with the full allocator hierarchy (heap, arena, buddy, slab)
No standard library dependency - Independent implementation compatible with
STATIC_ONLYmodeCustom deleters - Full support for user-defined cleanup logic via the deleter template parameter
Type-safe - Strongly typed ownership semantics prevent common dangling pointer and double-free bugs
Array
Generic container -
Array<T>supports any element type via template parameterTAllocator-backed - Both the container struct and its element buffer are allocated through the provided allocator
Factory pattern - Creation via
Array<T>::init()returningExpected<Array<T>*>prevents uninitialised instancesTiered growth - Automatic buffer growth uses a five-tier strategy to avoid runaway allocation at large sizes
Bounds-checked access -
operator[]returnsExpected<T>for safe reads;set()returnsExpected<bool>for safe writesIn-place algorithms -
sort(),reverse(),clear(), andconcat()operate without additional allocationSIMD-accelerated -
reverse()andmin()/max()dispatch to the best available SIMD back-end at compile timeSearch -
contains(),binary_search(), andbracketed_binary_search()provide O(1)–O(log n) lookupTransformations -
slice()andcumulative()produce new arrays from existing data
Dictionary
Generic hash map -
Dict<K, V>maps keys of any trivially copyable typeKto values of typeVAllocator-backed - Both the
Dictstruct and every internal node are allocated through the provided allocatorFactory pattern - Creation via
Dict<K,V>::init()returningExpected<Dict<K,V>*>prevents uninitialised instancesMurmurHash3 - High-quality hash function operating on raw key bytes with configurable seed
Chained collision resolution - Singly-linked bucket chains handle hash collisions without probing
Optional growth - Automatic resize when load factor exceeds 0.75; can be disabled for fixed-capacity use
String key support -
StringKey<N>provides a trivially copyable fixed-size wrapper for string keysTemplated iteration -
foreach()accepts any callable with signaturevoid(const K&, const V&)Copy and merge -
copy()andmerge()factory methods build new dictionaries from existing ones
List
Generic singly linked container -
SList<T>stores elements of any typeTusing a node-based template designAllocator-backed - The list struct, slab storage, and optional overflow nodes are all allocated through the provided allocator
Factory pattern - Creation via
SList<T>::init()returningExpected<SList<T>*>prevents uninitialised instancesHybrid node storage - Initial nodes are drawn from a contiguous slab for improved locality, with optional overflow allocation beyond slab capacity
Recycled slab nodes - Removed slab-backed nodes are tracked internally and reused by future push operations
Configurable overflow - Overflow allocation can be enabled for dynamic growth or disabled for deterministic fixed-capacity behavior
Safe list operations -
push_*(),pop_*(),get(), and peek functions return explicit success or error state where appropriateList-oriented algorithms -
reverse(),clear(),concat(),copy(),contains(), andforeach()provide common linked-list functionalityRAII cleanup -
SListDeleter<T>works withUniquePtrto automatically reclaim all list-owned memory
Heap
Generic binary heap -
Heap<T, Compare>provides priority-ordered access to any element typeTwith compile-time comparatorCompareArray-backed storage - All element storage, tiered growth, and buffer management are delegated to an owned
Array<T>instanceFactory pattern - Creation via
Heap<T,Compare>::init()returningExpected<Heap<T,Compare>*>prevents uninitialised instancesFlexible ordering - Pass
std::less<T>for a max-heap,std::greater<T>for a min-heap, or any lambda/functor for custom priority orderingNo operator< requirement - When a lambda or custom functor is used as the comparator, the element type needs no comparison operators
Optional growth - Automatic buffer growth using Array’s tiered strategy; can be disabled for deterministic fixed-capacity use
O(log n) push and pop - Sift-up and sift-down maintain the heap property on every mutation
Templated iteration -
foreach()accepts any callable with signaturevoid(const T&); for ordered traversal, copy the heap and pop in a loopRAII cleanup -
HeapDeleter<T, Compare>works withUniquePtrto automatically reclaim the heap struct and its backing array
AVL Tree
Generic balanced binary search tree -
AVLTree<T, Compare>provides ordered storage and lookup for any element typeTusing a compile-time comparatorCompareAllocator-backed - The tree struct, node slab, and optional overflow nodes are all allocated through the provided allocator
Factory pattern - Creation via
AVLTree<T,Compare>::init()returningExpected<AVLTree<T,Compare>*>prevents uninitialised instancesSelf-balancing structure - Maintains AVL height invariants through rotations, ensuring O(log n) insertion, removal, and lookup
Hybrid node storage - Initial nodes are allocated from a contiguous slab for improved locality, with optional overflow allocation beyond slab capacity
Recycled slab nodes - Removed slab-backed nodes are returned to an internal free list and reused before allocating new nodes
Configurable overflow - Overflow allocation can be enabled for dynamic growth or disabled for deterministic fixed-capacity behavior
Comparator-driven ordering - Ordering is defined by a three-way comparator, allowing custom sort behavior without requiring
operator<Duplicate policy control - Duplicate insertion can be enabled (multiset behavior) or disabled (set behavior) at initialization
Safe tree operations -
insert(),remove(),find(),min(),max(), and traversal methods return explicit success or error state where appropriateOrdered traversal -
foreach()provides in-order traversal;foreach_range()supports efficient range queries with subtree pruningRAII cleanup -
AVLTreeDeleter<T, Compare>works withUniquePtrto automatically reclaim the tree, slab, and any overflow nodes
Typical Use Cases
Embedded software
Engineering calculations (FEM, CFD, PDEs)
Adaptive data structures for large numerical grids
Real-time simulation or optimization
Modules:
Indices and tables
Getting Started
Clone the repository:
git clone https://github.com/Jon-Webb-79/csaltcpp.git
cd csalt++
CMake Build Instructions
Debug build (with tests):
cd scripts/bash
./debug.sh
Static build (no tests):
cd scripts/bash
./static.sh
Install system-wide (optional):
sudo ./install.sh
Run Unit Tests
cd build/debug
./unit_tests
You may optionally run under valgrind (Linux only):
valgrind ./unit_tests
Dependencies
Required:
C++ compiler supporting C++17 (tested with GCC 14.2.1 and Clang 16.0.6)
CMake ≥ 3.31.3
CMocka (for unit tests)
Optional:
valgrind (memory leak detection)
Python 3.10+ and Sphinx (for documentation)
Work Forward
Complete development of List, BTree, Queue, and Matrix containers
Refactor SIMD fast paths into classes to simplify type based ingestion
Test on a wider array of platforms and compilers to exercise all SIMD instruction sets
Add basic matrix solvers for Dense and Sparse (i.e. COO, CSR, CSC) matrices in
lin_alg.hppfileAdd root solvers, numerical integration, and numerical differentiation in
numerical.hppfile.Add more advancec matrix solvers such as JFNK method.
Development & Contribution
This library is modular and extensible. Contributions are welcome!
Fork the repo and create a branch
Write or update code
Add tests in the test directory
Ensure tests pass under debug mode
Update or add Sphinx docstrings
Submit a pull request
Documentation
Build the documentation using Sphinx:
cd docs/doxygen
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
make html
Documentation is also hosted online:
TBD
License
CSalt++ is provided under the MIT License. See the LICENSE file for details.