No hidden heap
Containers and storage helpers keep data in bounded objects. Capacity becomes a compile-time decision instead of an invisible runtime dependency.
CASTLE is a lightweight, header-only template library for firmware where memory, timing and failure behavior need to be visible parts of the design.
#include "castle/container/vector.h"
#include "castle/utility/optional.h"
int main()
{
castle::container::vector<uint16_t, 16> samples;
if (samples.push_back(42U) != castle::status::ok)
{
// capacity failure is explicit
return 1;
}
castle::optional<uint16_t> value = samples[0];
return value.has_value() ? 0 : 2;
}
CASTLE 2.0 favors simple, inspectable building blocks over runtime machinery that can make embedded behavior harder to bound or reason about.
Containers and storage helpers keep data in bounded objects. Capacity becomes a compile-time decision instead of an invisible runtime dependency.
Full and empty states are ordinary control flow. Status values and boolean results make failure visible without requiring exception-based APIs.
The public headers do not depend on the C++ STL. The library is built around compiler features and small low-level building blocks.
Type traits, fixed sizes, configuration and many utility operations can be expressed or evaluated at compile time, keeping firmware intent close to the type system.
CASTLE 2.0 currently spans the low-level primitives, containers, algorithms and event-oriented utilities that form a practical embedded foundation.
Atomic values and memory ordering primitives.
Browse module ↗Low-level bit manipulation and bit math helpers.
Browse module ↗Fixed-storage callbacks and callback policies.
Browse module ↗Durations, time points, clocks and literals.
Browse module ↗Array, vector, ring buffer, stack, strings and associative containers.
Browse module ↗Types, compiler abstraction, traits, configuration and assertions.
Browse module ↗Small embedded-oriented observer, singleton and visitor helpers.
Browse module ↗Status and error values for explicit control flow.
Browse module ↗Dispatchers, signals/slots, IPC events and tick timers.
Browse module ↗Iterator tags, traits, bounded, circular and reverse iterators.
Browse module ↗Integer math, ratios, square root, mean, logarithms and more.
Browse module ↗Placement construction, destruction, storage, addresses and lifetimes.
Browse module ↗Mutex and scoped locking helpers for synchronized firmware paths.
Browse module ↗Move, forward, swap, optional, variant, pair, tuple, bitset and safe casts.
Browse module ↗Reusable algorithmic building blocks for the library ecosystem.
Browse module ↗CASTLE is header-only. Use the include directory directly, or consume the repository as a CMake subdirectory through its interface target.
add_subdirectory(third_party/castle)
target_link_libraries(my_app
PRIVATE castle
)
Keep CASTLE anywhere under your firmware source tree or dependency directory.
The project exposes the castle target and propagates its include directory and C++17 requirement.
#include "castle/container/array.h" #include "castle/container/vector.h" #include "castle/utility/optional.h" #include "castle/events/tick_timer.h" // Add include/ to your compiler include path.
A typical configuration may use -std=c++17 -fno-exceptions -fno-rtti -nostdinc++ where supported by the toolchain.
The documentation mirrors include/castle, one module guide at a time.
The point is not to ban every runtime mechanism. It is to keep the core data structures statically shaped, explicit and inspectable.
Capacity, storage and configuration are visible at the API boundary. That makes it easier to budget memory and reason about normal failure modes.
vector<T, N>ok, full and emptytemplate<typename T, size_t N>
class vector
{
public:
static constexpr size_type
static_capacity = N;
bool empty() const noexcept;
bool full() const noexcept;
size_type size() const noexcept;
size_type capacity() const noexcept;
status push_back(const T& value);
};
CASTLE uses recognizable C++ vocabulary—containers, optionals, variants, iterators, tuples and chrono-style types—adapted to embedded constraints.
Keep buffers, state and helper objects bounded and close to the hardware-facing code.
Use predictable containers and explicit results in control loops and supervisory logic.
Model packets, lookup structures and communication buffers without making allocation the default.
Use fixed-capacity queues and value wrappers where throughput and bounded storage matter.
Compose callbacks, event dispatchers, signals and tick timers from the existing event family.
Make constraints visible in APIs and leave room for your project’s MISRA, AUTOSAR and process requirements.
These are the project’s stated next areas after the current prototype 2.0 building blocks.
Explicit state transitions and embedded-friendly configuration.
Input/output support plus encoder and decoder algorithms.
CRC-8/16/32, Fletcher, Adler and related utilities.
Additional basic math and geometry calculations.
More expressive compile-time configuration for event topologies.
AUTOSAR / MISRA C++ guidance per module as the library matures.
Explore the source, module guides and tests on the prototype 2.0 branch.