#define FOONATHAN_MEMORY_ALLOCATOR_STORAGE_HPP_INCLUDED
#include <new>
#include <type_traits>
#include "detail/utility.hpp"
#include "config.hpp"
#include "allocator_traits.hpp"
#include "threading.hpp"
namespace foonathan
{
namespace memory
{
namespace detail{}
template <typename StoragePolicy, typename Mutex>
class allocator_storage;
struct any_allocator;
template <typename RawAllocator>
class direct_storage;
template <typename RawAllocator>
allocator_adapter make_allocator_adapter() noexcept;
template <typename Mutex, typename RawAllocator>
thread_safe_allocator<typename std::decay<RawAllocator>::type, Mutex> make_thread_safe_allocator();
namespace detail
{
struct reference_stateful;
struct reference_stateless;
struct reference_shared;
reference_stateful reference_type(std::true_type stateful, std::false_type shared);
reference_stateless reference_type(std::false_type stateful, std::true_type shared);
reference_stateless reference_type(std::false_type stateful, std::false_type shared);
reference_shared reference_type(std::true_type stateful, std::true_type shared);
template <typename RawAllocator>
class reference_storage_impl<RawAllocator, reference_stateful>;
template <typename RawAllocator>
class reference_storage_impl<RawAllocator, reference_stateless>;
template <typename RawAllocator>
class reference_storage_impl<RawAllocator, reference_shared>;
}
template <typename RawAllocator>
struct is_shared_allocator;
template <typename RawAllocator>
class reference_storage;
template <>
class reference_storage<any_allocator>;
template <typename RawAllocator>
allocator_reference<typename std::decay<RawAllocator>::type> make_allocator_reference() noexcept;
using any_reference_storage = reference_storage<any_allocator>;
template <typename RawAllocator>
any_allocator_reference<> make_any_allocator_reference() noexcept;
}
}
template <typename StoragePolicy, typename Mutex>
class allocator_storage
{
public:
using allocator_type = typename StoragePolicy::allocator_type;
using storage_policy = StoragePolicy;
using mutex = Mutex;
allocator_storage<StoragePolicy, Mutex>() = default;
template <typename OtherPolicy>
allocator_storage<StoragePolicy, Mutex>(const allocator_storage<OtherPolicy, Mutex> & other, );
allocator_storage<StoragePolicy, Mutex>(allocator_storage && other) noexcept;
allocator_storage & operator=(allocator_storage && other) noexcept;
allocator_storage<StoragePolicy, Mutex>(const allocator_storage &) = default;
allocator_storage & operator=(const allocator_storage &) = default;
void * allocate_node(std::size_t size, std::size_t alignment);
void * allocate_array(std::size_t count, std::size_t size, std::size_t alignment);
void deallocate_node(void * ptr, std::size_t size, std::size_t alignment) noexcept;
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;
decltype(std::declval<storage_policy>().get_allocator()) get_allocator() noexcept;
decltype(std::declval<const storage_policy>().get_allocator()) get_allocator() const noexcept;
decltype(detail::lock_allocator(std::declval<storage_policy>().get_allocator(), std::declval<actual_mutex &>())) lock() noexcept;
};
A RawAllocator
that stores another allocator.
The StoragePolicy
defines the allocator type being stored and how it is stored.
The Mutex
controls synchronization of the access.
Requires: The StoragePolicy
itself must not store an instance of this class.
allocator_storage<StoragePolicy, Mutex>() = default;
Effects: Creates it by default-constructing the StoragePolicy
.
Requires: The StoragePolicy
must be default-constructible.
template <typename OtherPolicy>
allocator_storage<StoragePolicy, Mutex>(const allocator_storage<OtherPolicy, Mutex> & other, );
Effects: Creates it by passing it another allocator_storage with a different StoragePolicy
but the same Mutex
type.
Initializes it with the result of other.get_allocator()
.
Requires: The expression new storage_policy(other.get_allocator())
must be well-formed,
otherwise this constructor does not participate in overload resolution.
allocator_storage<StoragePolicy, Mutex>(allocator_storage && other) noexcept;
Effects: Moves the allocator_storage
object.
A moved-out allocator_storage
object must still store a valid allocator object.
allocator_storage & operator=(allocator_storage && other) noexcept;
Effects: Moves the allocator_storage
object.
A moved-out allocator_storage
object must still store a valid allocator object.
allocator_storage<StoragePolicy, Mutex>(const allocator_storage &) = default;
Effects: Copies the allocator_storage
object.
Requires: The StoragePolicy
must be copyable.
allocator_storage & operator=(const allocator_storage &) = default;
Effects: Copies the allocator_storage
object.
Requires: The StoragePolicy
must be copyable.
void * allocate_node(std::size_t size, std::size_t alignment);
Effects: Calls the function on the stored allocator.
The Mutex
will be locked during the operation.
void * allocate_array(std::size_t count, std::size_t size, std::size_t alignment);
Effects: Calls the function on the stored allocator.
The Mutex
will be locked during the operation.
void deallocate_node(void * ptr, std::size_t size, std::size_t alignment) noexcept;
Effects: Calls the function on the stored allocator.
The Mutex
will be locked during the operation.
void deallocate_array(void * ptr, std::size_t count, std::size_t size, std::size_t alignment) noexcept;
Effects: Calls the function on the stored allocator.
The Mutex
will be locked during the operation.
std::size_t max_node_size() const;
Effects: Calls the function on the stored allocator.
The Mutex
will be locked during the operation.
std::size_t max_array_size() const;
Effects: Calls the function on the stored allocator.
The Mutex
will be locked during the operation.
std::size_t max_alignment() const;
Effects: Calls the function on the stored allocator.
The Mutex
will be locked during the operation.
decltype(std::declval<storage_policy>().get_allocator()) get_allocator() noexcept;
Effects: Forwards to the StoragePolicy
.
Returns: Returns a reference to the stored allocator.
This does not lock the Mutex
.
decltype(std::declval<const storage_policy>().get_allocator()) get_allocator() const noexcept;
Effects: Forwards to the StoragePolicy
.
Returns: Returns a reference to the stored allocator.
This does not lock the Mutex
.
decltype(detail::lock_allocator(std::declval<storage_policy>().get_allocator(), std::declval<actual_mutex &>())) lock() noexcept;
Returns: A proxy object that acts like a pointer to the stored allocator.
It cannot be reassigned to point to another allocator object and only moving is supported, which is destructive.
As long as the proxy object lives and is not moved from, the Mutex
will be kept locked.
struct any_allocator{};
Tag type that enables type-erasure in reference_storage
.
It can be used everywhere a allocator_reference
is used internally.
template <typename RawAllocator>
class direct_storage
{
public:
direct_storage<RawAllocator>() = default;
direct_storage<RawAllocator>(direct_storage && other) noexcept;
direct_storage & operator=(direct_storage && other) noexcept;
allocator_type & get_allocator() noexcept;
protected:
~direct_storage<RawAllocator>() noexcept = default;
};
A StoragePolicy
that stores the allocator directly.
It embeds the allocator inside it, i.e. moving the storage policy will move the allocator.
direct_storage<RawAllocator>() = default;
Effects: Creates it by default-constructing the allocator.
Requires: The RawAllcoator
must be default constructible.
direct_storage<RawAllocator>(direct_storage && other) noexcept;
Effects: Moves the direct_storage
object.
This will move the stored allocator.
direct_storage & operator=(direct_storage && other) noexcept;
Effects: Moves the direct_storage
object.
This will move the stored allocator.
allocator_type & get_allocator() noexcept;
Returns: A reference to the stored allocator.
template <typename RawAllocator>
allocator_adapter make_allocator_adapter() noexcept;
Returns: A new allocator_adapter
object created by forwarding to the constructor.
template <typename Mutex, typename RawAllocator>
thread_safe_allocator<typename std::decay<RawAllocator>::type, Mutex> make_thread_safe_allocator();
Returns: A new thread_safe_allocator
object created by forwarding to the constructor,
specifying a certain mutex type.
Requires: It requires threading support from the implementation.
template <typename RawAllocator>
struct is_shared_allocator
: std::false_type
{
};
Specifies whether or not a RawAllocator
has shared semantics.
It is shared, if - like allocator_reference
- if multiple objects refer to the same internal allocator and if it can be copied.
This sharing is stateful, however, stateless allocators are not considered shared in the meaning of this traits.
If a RawAllocator
is shared, it will be directly embedded inside reference_storage
since it already provides allocator_reference
like semantics, so there is no need to add them manually.
Specialize it for your own types, if they provide sharing semantics and can be copied.
Notes: This makes no guarantess about the lifetime of the shared object, the sharing allocators can either own or refer to a shared object.
template <typename RawAllocator>
class reference_storage
{
public:
reference_storage<RawAllocator>(const allocator_type & alloc) noexcept;
reference_storage<RawAllocator>(const reference_storage &) noexcept = default;
reference_storage & operator=(const reference_storage &) noexcept = default;
allocator_type & get_allocator() const noexcept;
protected:
~reference_storage<RawAllocator>() noexcept = default;
};
A StoragePolicy
that stores a reference to an allocator.
For stateful allocators it only stores a pointer to an allocator object and copying/moving only copies the pointer.
For stateless allocators it does not store anything, an allocator will be constructed as needed.
For allocators that are already shared (determined through is_shared_allocator
) it will store the allocator type directly.
Notes: It does not take ownership over the allocator in the stateful case, the user has to ensure that the allocator object stays valid. In the other cases the lifetime does not matter.
reference_storage<RawAllocator>(const allocator_type & alloc) noexcept;
Effects: Creates it from a stateless or shared allocator. It will not store anything, only creates the allocator as needed.
Requires: The RawAllocator
is stateless or shared.
reference_storage<RawAllocator>(const reference_storage &) noexcept = default;
Effects: Copies the allocator_reference
object.
Only copies the pointer to it in the stateful case.
reference_storage & operator=(const reference_storage &) noexcept = default;
Effects: Copies the allocator_reference
object.
Only copies the pointer to it in the stateful case.
allocator_type & get_allocator() const noexcept;
Returns: Returns a reference to the allocator.
template <>
class reference_storage<any_allocator>
{
class base_allocator;
public:
template <typename RawAllocator>
reference_storage(RawAllocator & alloc) noexcept;
reference_storage(const base_allocator & alloc) noexcept;
reference_storage(const reference_storage & other) noexcept;
reference_storage & operator=(const reference_storage & other) noexcept;
allocator_type & get_allocator() const noexcept;
protected:
~reference_storage() noexcept = default;
private:
template <typename RawAllocator>
class basic_allocator;
using default_instantiation = basic_allocator<base_allocator>;
storage storage_;
};
Specialization of the class template reference_storage
that is type-erased.
It is triggered by the tag type any_allocator
.
The specialization can store a reference to any allocator type.
template <typename RawAllocator>
reference_storage(RawAllocator & alloc) noexcept;
Effects: Creates it from a reference to any stateful RawAllocator
.
It will store a pointer to this allocator object.
Notes: The user has to take care that the lifetime of the reference does not exceed the allocator lifetime.
reference_storage(const base_allocator & alloc) noexcept;
Effects: Creates it from the internal base class for the type-erasure. Has the same effect as if the actual stored allocator were passed to the other constructor overloads.
Notes: This constructor is used internally to avoid double-nesting.
reference_storage(const reference_storage & other) noexcept;
Effects: Copies the reference_storage
object.
It only copies the pointer to the allocator.
reference_storage & operator=(const reference_storage & other) noexcept;
Effects: Copies the reference_storage
object.
It only copies the pointer to the allocator.
allocator_type & get_allocator() const noexcept;
Returns: A reference to the allocator.
The actual type is implementation-defined since it is the base class used in the type-erasure,
but it provides the full RawAllocator
member functions.
Notes: There is no way to access any custom member functions of the allocator type.
template <typename RawAllocator>
allocator_reference<typename std::decay<RawAllocator>::type> make_allocator_reference() noexcept;
Returns: A new allocator_reference
object by forwarding the allocator to the constructor.
using any_reference_storage = reference_storage<any_allocator>;
An alias for the reference_storage
specialization using type-erasure.
template <typename RawAllocator>
any_allocator_reference<> make_any_allocator_reference() noexcept;
Returns: A new any_allocator_reference
object by forwarding the allocator to the constructor.
Why for operator= it says function? I think for the copy/move assignment it should not say "function", it is just a special function, such as the constructors. Will this be fixed in the future or this is the way it is meant to be?