libcruft-util/alloc/easy.hpp

301 lines
8.8 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2018-2020, Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "traits.hpp"
#include "../buffer/traits.hpp"
#include "../concepts.hpp"
#include "../std.hpp"
#include "../view.hpp"
#include <utility>
#include <memory>
namespace cruft::alloc::easy {
/// Provides an interface suitable for allocating and constructing typed
/// objects using a reference to an existing allocator.
template <typename AllocatorT>
class passthrough {
public:
explicit passthrough (AllocatorT &_allocator)
: m_allocator (_allocator)
{ ; }
/// Allocate, construct, and return a type `U` using memory from the
/// underlying allocator with the default alignment.
template <typename U, typename ...Args>
U*
acquire (Args&&...args)&
{
auto memory = m_allocator.allocate (sizeof (U), alignof (U));
try {
return new (memory.data ()) U (std::forward<Args> (args)...);
} catch (...) {
m_allocator.deallocate (memory.data (), sizeof (U), alignof (U));
throw;
}
}
/// Destruct and deallocate an object which has previously been
/// allocated through this interface.
template <typename U>
void release (U *ptr)
{
ptr->~U ();
m_allocator.deallocate (ptr, sizeof (U), alignof (U));
}
/// Create an object of type `U` using `acquire` and wrap the result
/// in a std::unique_ptr that will call release on this interface at d
/// struction time.
template <typename U, typename ...Args>
auto unique (Args &&...args)&
{
struct deleter {
passthrough &m_owner;
void operator() (U *ptr)
{ return m_owner.release (ptr); }
};
return std::unique_ptr (
acquire (std::forward<Args> (args)...),
deleter {*this}
);
}
template <typename T>
decltype (auto)
offset (T const *ptr)
{
return m_allocator.offset (ptr);
}
decltype(auto) data (void) { return m_allocator.data (); }
decltype(auto) begin (void) { return m_allocator.begin (); }
decltype(auto) end (void) { return m_allocator.end (); }
decltype(auto) remain (void) { return m_allocator.remain (); }
decltype(auto) offset (void const *ptr) { return m_allocator.offset (ptr); }
private:
AllocatorT &m_allocator;
};
/// An interfaces that manages an allocator and a backing store, and
/// provides a simple interface for constructing arbitrary objects using
/// the child objects.
template <
typename AllocatorT,
typename = std::enable_if_t<is_allocator_v<AllocatorT>>
>
class backed {
public:
template <typename BufferT, typename ...Args>
explicit backed (BufferT &_buffer, Args&&...args)
: m_allocator (_buffer, std::forward<Args> (args)...)
{ ; }
/// Allocate, construct, and return a type `U` using memory from the
/// underlying allocator with the default alignment.
template <typename U, typename ...Args>
U*
acquire (Args&&...args)&
{
auto memory = m_allocator.allocate (sizeof (U), alignof (U));
try {
return new (memory.data ()) U (std::forward<Args> (args)...);
} catch (...) {
m_allocator.deallocate (memory.data (), memory.size (), alignof (U));
throw;
}
}
/// Destruct and deallocate an object which has previously been
/// allocated through this interface.
template <typename U>
void release (U *ptr)
{
ptr->~U ();
m_allocator.deallocate (
reinterpret_cast<u08*> (ptr),
sizeof (U),
alignof (U)
);
}
template <typename T>
cruft::view<T*>
array (std::size_t count)
{
auto mem = m_allocator.allocate (count * sizeof (T), alignof (T)).template cast<T*> ();
try {
new (mem.data ()) T[count];
return mem;
} catch (...) {
m_allocator.deallocate (mem.data (), count * sizeof (T), alignof (T));
throw;
}
}
/// Allocates storage for an array of ValueT, moves the values from
/// ContainerT into this storage, and returns a view over the memory.
template <
concepts::iterable ContainerT
>
auto
array (ContainerT &&container)
{
using IteratorT = decltype (std::begin (container));
using ValueT = typename std::iterator_traits<IteratorT>::value_type;
auto store = array<ValueT> (std::size (container));
std::copy (
std::move_iterator (std::begin (container)),
std::move_iterator (std::end (container)),
std::begin (store)
);
return store;
}
/// Create an object of type `U` using `acquire` and wrap the result
/// in a std::unique_ptr that will call release on this interface at d
/// struction time.
template <typename U, typename ...Args>
auto unique (Args &&...args)&
{
struct deleter {
backed &m_owner;
void operator() (U *ptr)
{ return m_owner.release (ptr); }
};
return std::unique_ptr<U,deleter> (
acquire<U> (std::forward<Args> (args)...),
deleter {*this}
);
}
decltype(auto) data (void) { return m_allocator.data (); }
decltype(auto) begin (void) { return m_allocator.begin (); }
decltype(auto) end (void) { return m_allocator.end (); }
decltype(auto) used (void) { return m_allocator.used (); }
decltype(auto) remain (void) { return m_allocator.remain (); }
decltype(auto) offset (void const *ptr) { return m_allocator.offset (ptr); }
template <typename T>
decltype(auto)
offset (cruft::view<T*> ptr)
{
return offset (ptr.data ());
}
private:
AllocatorT m_allocator;
};
/// An interfaces that manages an allocator and a backing store, and
/// provides a simple interface for constructing arbitrary objects using
/// the child objects.
template <
typename AllocatorT,
typename BufferT,
typename = std::enable_if_t<true
//&& memory::buffer::is_buffer_v<BufferT>
&& is_allocator_v<AllocatorT>
>
>
class owned {
public:
explicit owned (BufferT &&_buffer)
: m_buffer (std::move (_buffer))
, m_allocator (m_buffer)
{ ; }
/// Allocate, construct, and return a type `U` using memory from the
/// underlying allocator with the default alignment.
template <typename U, typename ...Args>
U*
acquire (Args&&...args)&
{
auto memory = m_allocator.allocate (sizeof (U), alignof (U));
try {
return new (memory.data ()) U (std::forward<Args> (args)...);
} catch (...) {
m_allocator.deallocate (memory.data (), memory.size (), alignof (U));
throw;
}
}
/// Destruct and deallocate an object which has previously been
/// allocated through this interface.
template <typename U>
void release (U *ptr)
{
ptr->~U ();
m_allocator.deallocate (
reinterpret_cast<u08*> (ptr),
sizeof (U),
alignof (U)
);
}
/// Create an object of type `U` using `acquire` and wrap the result
/// in a std::unique_ptr that will call release on this interface at d
/// struction time.
template <typename U, typename ...Args>
auto unique (Args &&...args)&
{
struct deleter {
owned &m_owner;
void operator() (U *ptr)
{ return m_owner.release (ptr); }
};
return std::unique_ptr<U,deleter> (
acquire<U> (std::forward<Args> (args)...),
deleter {*this}
);
}
private:
BufferT m_buffer;
AllocatorT m_allocator;
};
};