What A View Means
A view is a pointer plus a size. It does not allocate memory, it does not copy the data, and it does not keep the original object alive. The caller is responsible for making sure the referenced storage still exists while the view is used.
a::u8 bytes[128]{};
a::view<a::u8> writable{bytes};
a::view<const a::u8> first_packet = writable.first(32);
a::view<T> stores the element count at runtime. It is useful
when the buffer size is known only by the code path that passes
it in.
a::view<T>
Use a::view<T> when a function needs to read or write a
continuous range without owning it.
void clear(a::view<a::u8> out) noexcept {
for (a::usize i = 0; i < out.size(); ++i) {
out[i] = 0;
}
}
a::u8 packet[64]{};
clear(a::view<a::u8>{packet});
Useful operations include size(), empty(), data(),
first(n), last(n), drop(n),
slice(pos, count), and bytes(). Slicing clamps to the available range instead of
allocating a new object.
A view of const char from a string literal excludes the final
zero terminator from its logical size.
constexpr a::view<const char> text{"fall"};
static_assert(text.size() == 4);
a::view_n<T, N>
a::view_n<T, N> is also non-owning, but the element count is
part of the type. Use it when the exact size is part of the
contract.
a::u8 block_storage[32]{};
a::view_n<a::u8, 32> block{block_storage};
block.at<0>() = 0xff;
at<I>() checks the index at compile time. Constructing
view_n from a dynamic view is fail-safe: the result is valid
only when the dynamic view has exactly N elements.
a::view<a::u8> dynamic{block_storage, 16};
a::view_n<a::u8, 32> exact{dynamic};
if (!exact.dynamic_view()) {
// dynamic.size() was not 32
}
a::zview<T>
a::zview<T> is a borrowed zero-terminated sequence. Its
logical size excludes the terminator, and explicit pointer plus
length construction accepts only storage where
data()[size()] == T{}.
constexpr a::zview<const char> name{"config.bin"};
static_assert(name.size() == 10);
const char raw[] = {'o', 'k', '\0'};
a::zview<const char> valid{raw, 2};
Use this when the backend needs a C-style zero-terminated string but application code still wants an explicit length-aware type.
a::zview_n<T, N>
a::zview_n<T, N> combines both ideas: the sequence is
zero-terminated, and the non-terminator count is encoded in the
type.
constexpr a::zview_n<const char, 4> word{"fall"};
static_assert(word.len() == 4);
a::view<const char> without_terminator = word.unterminated_view();
This is useful for small protocol names, fixed file names, or compile-time checked strings passed to code that must still see the zero terminator.
View Rules
- Views never own memory.
- Views never extend object lifetime.
viewandview_ndo not require zero termination.zviewandzview_nrequire zero termination.- Use
constelement types for read-only data. - Use fixed-size views when the exact size is part of the API.
Why Bundles Exist
Embedded and freestanding code often cannot rely on a heap.
a::bundle lets an application declare the storage it needs at
compile time and materialize it as one object with normal C++
layout and alignment.
A bundle is not a map, registry, allocator, or runtime object store. It is typed storage chosen by the application.
Declaring A Bundle
A bundle specification contains a permanent resource list.
Each resource has a type and, for arrays and buffers, a size.
Tags give resources compile-time names.
struct PeerState final {
a::u64 id;
a::u32 last_seen_tick;
};
struct NetworkState final {
a::usize connected_peers;
};
struct peer_table final {};
struct receive_packet final {};
struct send_packet final {};
struct NetworkBundle final {
using permanent = a::resources<
a::array<PeerState, 128, peer_table>,
a::object<NetworkState>,
a::buffer<receive_packet, 64 * 1024>,
a::buffer<send_packet, 64 * 1024>
>;
};
static a::bundle<NetworkBundle> network{};
Using A Bundle
Resources are retrieved by tag and kind. Arrays and buffers return fixed-size views. Objects return references.
a::view_n<PeerState, 128> peers = network.array<peer_table>();
a::view_n<a::u8, 64 * 1024> receive = network.buffer<receive_packet>();
NetworkState& state = network.object<NetworkState>();
peers.at<0>().id = 42;
receive.at<0>() = 0xff;
state.connected_peers = 1;
If you request an unknown tag or ask for a buffer as an object, the program fails at compile time. Duplicate tags in one bundle region are also rejected.
Bundle Benefits
- No heap allocation is required.
- Storage size is visible before the program runs.
- Resources are typed and tagged at compile time.
- The compiler owns alignment and object layout.
- The API makes large buffers explicit in application code.
static_assert(a::bundle<NetworkBundle>::size_bytes() >= 128 * sizeof(PeerState));
static_assert(a::bundle<NetworkBundle>::alignment_bytes() >= alignof(NetworkState));
Bundle Costs
- The size is fixed at compile time.
- The bundle cannot be copied or moved.
- Every stored type must be nothrow default-constructible and nothrow destructible.
- Large bundles increase static storage or stack usage wherever they are placed.
- It is not a replacement for a dynamic allocator when runtime-sized ownership is actually required.