Custom Allocators
Custom alloctors can be implemented easily by defining a new class that satisfies the low-level allocator interface. By calling a base class the allocator user-interface is automatically generated.
Implementing a new allocator
Implementing a new allocator is easy. Given a struct
the following (lowlevel) interface should be implemented:
Finally, by calling the following base class the implementation is completed:
For an example, please have a look at the corresponding implementation of the DefaultAllocator
that uses malloc/free.
The allocator base class
The allocator base class generates and completes the implementation of myallocator
. It implements the following basic interfaces
We already covered the implementation of owns
. The implementation of __allocators_best_friend
is also straightforward. It looks like this:
The idea of wrapping the three key allocator functions in one function is inspired from Lua's lua_Alloc. See also the blog post on an allocator API for C.
Let's look at the allocate
method:
The implementation of __allocate
, __reallocate
and __deallocate
is specific to each allocator.
Use in containers
Here follows an example of a simple DynamicStack
class. A couple of interesting things are the following:
- The allocator is not passed as a template type parameter!
- A
__dtor
method need not be implemented to release the dynamic resources. One is generated automatically since__dtor
is implemented forblock
. - In
new
an opaqueblock
is automatically cast to aS = alloc.SmartBlock(T)
. - A
get
andset
method is available for SmartBlock objects of typeS
. Essentially,S = alloc.SmartBlock(T)
is a smart pointer type.
The above class can be used as follows:
Recursive datastructures
Recursive datastructures, such as linked lists can be implemented using specialized __dtor
's and keeping an array of nodes, or, directly, using smart blocks. The implementation of block
supports automatic destruction of recursive datastructures, and even cycles. Here follows an example of a cyclical double linked list:
To do:
The following things remain:
- The current implementation of
block.methods.__dtor
relies on recursion. LLVM may not be able to fully optimize the recursion to a loop, which may seriously limit the size of such datastrutures due to limits in stack-space. In the near future I will rewrite the algorithm using a while loop. - Right now only a default allocator is implemented based on
malloc
,realloc
,calloc
andaligned_alloc
. Other standard allocators need to be implemented, such as, a 'stack', 'arena', 'freelist' allocators, etc. - Functionality for composing allocators to build new ones.
- A
SmartBlock(T)
can already be cast to aSmartBlock(vector(T))
for primitive typesT
. By adding a__for
metamethod it would become possible to iterate over aSmartBlock(vector(T))
and enable 'SIMD' instructions in a range for loop.