Skip to content

Instantly share code, notes, and snippets.

@foonathan
Last active February 24, 2021 00:31
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save foonathan/14e163b76804b6775d780eabcbaa6a51 to your computer and use it in GitHub Desktop.
Save foonathan/14e163b76804b6775d780eabcbaa6a51 to your computer and use it in GitHub Desktop.
Sample documentation output for Standardese prototyp

Header file allocator_storage.hpp

#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;
    }
}

Class template allocator_storage<StoragePolicy, Mutex>

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.

Constructor allocator_storage<StoragePolicy, Mutex>

allocator_storage<StoragePolicy, Mutex>() = default;

Effects: Creates it by default-constructing the StoragePolicy.

Requires: The StoragePolicy must be default-constructible.

Function template allocator_storage<StoragePolicy, Mutex><OtherPolicy>

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.

Constructor allocator_storage<StoragePolicy, Mutex>

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.

Function operator=

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.

Constructor allocator_storage<StoragePolicy, Mutex>

allocator_storage<StoragePolicy, Mutex>(const allocator_storage &) = default;

Effects: Copies the allocator_storage object.

Requires: The StoragePolicy must be copyable.

Function operator=

allocator_storage & operator=(const allocator_storage &) = default;

Effects: Copies the allocator_storage object.

Requires: The StoragePolicy must be copyable.

Function allocate_node

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.

Function allocate_array

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.

Function deallocate_node

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.

Function deallocate_array

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.

Function max_node_size

std::size_t max_node_size() const;

Effects: Calls the function on the stored allocator. The Mutex will be locked during the operation.

Function max_array_size

std::size_t max_array_size() const;

Effects: Calls the function on the stored allocator. The Mutex will be locked during the operation.

Function max_alignment

std::size_t max_alignment() const;

Effects: Calls the function on the stored allocator. The Mutex will be locked during the operation.

Function get_allocator

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.

Function get_allocator

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.

Function lock

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.


Class any_allocator

struct any_allocator{};

Tag type that enables type-erasure in reference_storage.

It can be used everywhere a allocator_reference is used internally.


Class template direct_storage<RawAllocator>

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.

Constructor direct_storage<RawAllocator>

direct_storage<RawAllocator>() = default;

Effects: Creates it by default-constructing the allocator.

Requires: The RawAllcoator must be default constructible.

Constructor direct_storage<RawAllocator>

direct_storage<RawAllocator>(direct_storage && other) noexcept;

Effects: Moves the direct_storage object. This will move the stored allocator.

Function operator=

direct_storage & operator=(direct_storage && other) noexcept;

Effects: Moves the direct_storage object. This will move the stored allocator.

Function get_allocator

allocator_type & get_allocator() noexcept;

Returns: A reference to the stored allocator.


Function template make_allocator_adapter<RawAllocator>

template <typename RawAllocator>
allocator_adapter make_allocator_adapter() noexcept;

Returns: A new allocator_adapter object created by forwarding to the constructor.

Function template make_thread_safe_allocator<Mutex, RawAllocator>

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.

Class template is_shared_allocator<RawAllocator>

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.


Class template reference_storage<RawAllocator>

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.

Constructor reference_storage<RawAllocator>

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.

Constructor reference_storage<RawAllocator>

reference_storage<RawAllocator>(const reference_storage &) noexcept = default;

Effects: Copies the allocator_reference object. Only copies the pointer to it in the stateful case.

Function operator=

reference_storage & operator=(const reference_storage &) noexcept = default;

Effects: Copies the allocator_reference object. Only copies the pointer to it in the stateful case.

Function get_allocator

allocator_type & get_allocator() const noexcept;

Returns: Returns a reference to the allocator.


Class template reference_storage<any_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.

Function template reference_storage<RawAllocator>

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.

Constructor reference_storage

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.

Constructor reference_storage

reference_storage(const reference_storage & other) noexcept;

Effects: Copies the reference_storage object. It only copies the pointer to the allocator.

Function operator=

reference_storage & operator=(const reference_storage & other) noexcept;

Effects: Copies the reference_storage object. It only copies the pointer to the allocator.

Function get_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.


Function template make_allocator_reference<RawAllocator>

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.

Type alias any_reference_storage

using any_reference_storage = reference_storage<any_allocator>;

An alias for the reference_storage specialization using type-erasure.

Function template make_any_allocator_reference<RawAllocator>

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.

@germandiagogomez
Copy link

germandiagogomez commented Jun 13, 2016

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?

@foonathan
Copy link
Author

Sorry, apparently I do not get notifications for those comments here.
I ping you, so you might see the anwser: @germandiagogomez

Thanks for bringing it up, I'll think about adding a special case for those.
There isn't because in the internal hierachy there are just member functions and I select the name based on the type.

@foonathan
Copy link
Author

@germandiagogomez btw, that's implemented

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment