A C++ library for making your code generic over allocation and alignment. Inspired by the Zig stdlib.
ALLO IS STILL EXPERIMENTAL.
scratch_allocator_t- An object which allows you to allocate and to register destruction callbacks. Upon destruction, it frees all of its allocations and calls callbacks. Not threadsafe.
stack_allocator_t- Similar to the scratch allocator, but it allows you to remap and free the topmost allocation. Not threadsafe.
heap_allocator_t- An object which lets you arbitrarily alloc and free memory. Its advantages
over
mallocare that it defines exactly when it will make a syscall (by default, it never does) and that it frees all of its allocations upon destruction (as opposed tomalloc, where you must remember to free each induvidual allocation). Not threadsafe.
- An object which lets you arbitrarily alloc and free memory. Its advantages
over
block_allocator_t- An optimization of
heap_allocator_tfor when all of your allocations may be similarly sized. Not threadsafe.
- An optimization of
c_allocator_t- A rough attempt to make
mallocfit into the abstractions of this library. Its primary use is as a quick-and-easy allocator which is unbounded except by system memory/swap space, and can be passed into functions which might otherwise be intended to accept the other four. - Breaks the abstraction: you cannot remap allocations, nor register destruction callbacks. Most importantly, there is no way to free all of the allocations made by this allocator, so it does nothing upon destruction.
- A rough attempt to make
- Debugging features
- Runtime type info (rtti compiler option not required) to check if you freed an allocation with a different type than you originally allocated it with
- Allocation size tracking, to ensure that you request frees as the same size that you allocated them with.
- A
template <typename T> class threadsafe_tto allow for threadsafe variants of the heap allocators. virtual_allocator_t,virtual_stack_allocator_t,virtual_heap_allocator_t, andvirtual_threadsafe_heap_allocator_t. A class which can be extended with virtual overrides to implement your own allocator and pass it to functions which expect an allocator from this library. Probably an antipattern, as kind of the whole point of Allo is that you only ever need stack, block, and heap allocators.debug_allocator_twhich internally usesc_allocator_t, but provides options to make certain functions always fail. Effectively a limited shortcut and could be achieved with thevirtual_threadsafe_heap_allocator_t. May not be necessary.- An
allo::make_uniqueandallo::make_sharedfor use with the heap allocators. - Debugging Options
- Disable runtime allocation type info and allocation size tracking by default, allow enabling in debug mode or always. Encourage enabling both these features in debug mode, and use them in example code. However it's important to understand that these increase memory usage substantially before enabling them. Could cause bugs that only occur in release mode.
- New Debugging Features
- Optionally allow refcounting all allocations, using a global threadsafe array
of refcounts. Would require for
zl::sliceto be overridden in debug mode to implement the semantics of a shared pointer. Log error details whenever the contents of a slice is accessed after free or before initialization. Probably won't make it to first release but will require significant preparation to ensure it can be added in later releases. Functions likealloc_onewould need to be rethought, since they return aT&instead of azl::slice<T>. - Optionally allow allocator constructors to take in a logging callback.
- Optionally allow refcounting all allocations, using a global threadsafe array
of refcounts. Would require for
- Allow for allocators to request new blocks of memory from their parent allocator instead of only trying to remap their current block. Will allow for potentially unbounded amounts of memory allocation, although will require some additional memory usage for the allocator to keep track of the blocks it owns. May be implemented as separate variants of the current allocators.
- Add some sort of API to allow for saving and then restoring the state of an allocator, or easily registering a sub-allocator, so that procedures which allocate can return an error and then the caller can easily undo any allocation they may have performed.
- You know what memory is being allocated and how- the allocator is no longer global.
- The details of memory allocation are hidden from systems. For example:
- switch out a
heap_allocator_twith ablock_allocator_tto decrease memory usage for many similar sized allocations. - switch out a
c_allocator_twith astack_allocator_tfor routines which only perform allocation, so that the allocations become contiguous
- switch out a
The goal of allo is to provide a framing for programming problems where allocators handle most of the ownership within a program. This violates RAII (allocators, when destructed, do no call the destructors of items allocated within them).
Additionally, allo provides a slightly different model of allocators that the STL. Where the STL has "deleters" and the fully fledged polymorphic allocators, allo has the following inheritance hierarchy:
- Allocator
- Able to allocate new memory
- Able to register "destruction callbacks". Used for non-memory resources.
- Stack Allocator
- Allocator with the additionl ability to:
- Free the most recently allocated allocation
- Realloc the most recently allocated allocation
- Allocator with the additionl ability to:
- Heap Allocator
- Stack Allocator which is able to realloc/free any allocation, not just the most recently allocated.
- Threadsafe Heap Allocator
- A heap allocator which is additionally threadsafe and therefore provides an atomic reallocation function (as opposed to other allocators, which require you to alloc a new allocation, and then free the old one).
I belive heap allocators are conceptually compatible with polymorphic allocators for the most part. If the abstraction matches up, I plan to provide a polymorphic wrapper for these types, for use with the STL.
Allo does not use RTTI, exceptions, or virtual dispatch. To improve compile times, it attempts to minimize use of the STL. Some headers that are used are:
<type_traits>for template magic<iterator>for stdlib iteration support (can optionally be disabled)<utility>forstd::forward,std::move, andstd::in_place_t<functional>forstd::ref<memory>forstd::align(only has effect on compile times in header-only mode)<cstdint>and<cstddef>in public headers, as well as<cmath>in header-only mode.
Allo uses templates liberally, but avoids forcing user code to be templated.
TBD. I personally made this library for the control it offers over memory, regardless
of whether heap_allocator_t is slower than malloc. I'm leaving benchmarks until
someone else uses this thing and wants that.
Allo supports C++17 and up. It was originally written for C++20 but was backported for use on a game console. The C++20 elements are no longer present, but some drop-in replacement features like requires-clauses are planned to be conditionally available in the future.
TBD. I would like to add fmtlib support for sure, but right now I just want dependencies to be light so I'm leaving it for later.