Writing own RawAllocator classes

There are, in general, three different ways and one special case to write a RawAllocator class. See the link for the exact requirements and behavior for each function.

# 0. Write a normal Allocator class

Just go ahead and write a normal `Allocator` class. It will work just fine and can be used anywhere a RawAllocator is required. Keep in mind, though, that the `construct` or `destroy` functions will not be called and its pointer typedefs not used.

# 1. Fulfill the requirements for the default allocator_traits

This is the easiest way. The default specialization of allocator_traits will forward to member functions, if they exist, and has some fallback, if they don't. The following class overrides all the fallbacks:

struct raw_allocator
{
using is_stateful = std::integral_constant<bool, Value>;
void* allocate_node(std::size_t size, std::size_t alignment);
void deallocate_node(void *node, std::size_t size, std::size_t alignment) noexcept;
void* allocate_array(std::size_t count, std::size_t size, std::size_t alignment);
void deallocate_array(void *ptr, std::size_t count, std::size_t size, std::size_t alignment) noexcept;
std::size_t max_node_size() const;
std::size_t max_array_size() const;
std::size_t max_alignment() const;
};

There are fallbacks for every function except `allocate_node()` and `deallocate_node()`. A minimum class thus only needs to provide those two functions. The fallbacks "do the right thing", for example `allocate_array()` forwards to `allocate_node()`, `is_stateful` is determined via `std::is_empty` and `max_node_size()` returns the maximum possible value.

Keep in mind that a RawAllocator has to be nothrow moveable and be valid to be used as a non-polymorphic base class, i.e. as a `private` base to use EBO.

The full interface is provided by the allocator_storage typedefs. Other classes where this approach is used are heap_allocator or aligned_allocator. The latter also provides the full interface.

# 2. Specialize the allocator_traits

But sometimes it is not attractive to provide the full interface. An example is the library class memory_stack. Its interface consists of typical behaviors required for a stack, like unwinding, and it does not make sense to provide a `deallocate_node()` function for it since there is no direct way to do so - only via unwinding.

In this case, the allocator_traits can be specialized for your type. Keep in mind that it is in the sub-namespace `memory` of the namespace `foonathan`. It needs to provide the following interface:

template <>
class allocator_traits<raw_allocator>
{
public:
using allocator_type = raw_allocator;
using is_stateful = std::integral_constant<bool, Value>;
static void* allocate_node(allocator_type &state, std::size_t size, std::size_t alignment);
static void deallocate_node(allocator_type &state, void *node, std::size_t size, std::size_t alignment) noexcept;
static void* allocate_array(allocator_type &state, std::size_t count, std::size_t size, std::size_t alignment);
static void deallocate_array(allocator_type &state, void *array, std::size_t count, std::size_t size, std::size_t alignment) noexcept;
static std::size_t max_node_size(const allocator_type &state);
static std::size_t max_array_size(const allocator_type &state);
static std::size_t max_alignment(const allocator_type &state);
};

This approach is used in the mentioned memory_stack but also the memory_pool classes.

# 3. Forward all behavior to another class

The allocator_traits provide a typedef `allocator_type`. This type is the actual type used for the (de-)allocation and will be stored in all classes taking a RawAllocator. Its only requirement is that it is implicitly constructible from the actual type instantiated and that it is a RawAllocator.

The main use for this typedef is to support `Allocator` classes. They need to be rebound to `char` to allow byte-size allocations prior before they are actually used.

Using this technique otherwise is rather esoteric and I do not see any reason for it, but it is possible. Let there be a class `raw_allocator` that is a RawAllocator, i.e. it provides the appropriate traits interface using any of the mentioned ways. This class also provides a constructor taking the class `my_allocator` that wants to forward to it. Then you only need to write:

class my_allocator {...};
class raw_allocator
{
public:
raw_allocator(my_allocator &)
{
...
}
// provides the required interface or has a traits specialization
};
...
template <>
class allocator_traits<my_allocator>
: public allocator_traits<raw_allocator>
{};