Skip to content

Commit

Permalink
Add unique_base_ptr to allow unique_ptr<Base>
Browse files Browse the repository at this point in the history
Fixes #33.
  • Loading branch information
foonathan committed Apr 18, 2018
1 parent 813a6a8 commit d18f214
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 5 deletions.
25 changes: 23 additions & 2 deletions example/using_allocators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,27 @@ int main()
memory::allocate_unique<int, memory::no_mutex>(pool, *std::next(list.begin()));
std::cout << *ptr2 << '\n';

struct base
{
virtual ~base() = default;

virtual const char* name() const = 0;
};

struct derived : base
{
const char* name() const override
{
return "derived";
}
};

// instead of using memory::unique_ptr<base, ...>, you have to use memory::unique_base_ptr<base, ...>,
// because the deleter has to remember the size of the derived type
memory::unique_base_ptr<base, memory::memory_pool<>> base_ptr =
memory::allocate_unique<derived>(pool);
std::cout << base_ptr->name() << '\n';

// static storage of size 4KiB
memory::static_allocator_storage<4_KiB> storage;

Expand All @@ -73,11 +94,11 @@ int main()
static_pool_t static_pool(memory::unordered_set_node_size<int>::value, 4_KiB, storage);

// again, just an alias for std::unordered_set<int, std::hash<int>, std::equal_to<int>, memory::std_allocator<int, static_pool_t>
// see why I wrote these? :D
// see why I wrote these?
// now we have a hash set that lives on the stack!
memory::unordered_set<int, static_pool_t>
set(13, std::hash<int>{}, std::equal_to<int>{},
static_pool); // GCC 4.7 is missing the allocator-only ctor, breaks travis :(
static_pool); // (GCC 4.7 is missing the allocator-only ctor, breaks travis)

set.insert(3);
set.insert(2);
Expand Down
111 changes: 108 additions & 3 deletions include/foonathan/memory/deleter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
/// \file
/// \c Deleter classes using a \concept{concept_rawallocator,RawAllocator}.

#include <type_traits>

#include "allocator_storage.hpp"
#include "config.hpp"
#include "threading.hpp"
Expand All @@ -17,12 +19,16 @@ namespace foonathan
namespace memory
{
/// A deleter class that deallocates the memory through a specified \concept{concept_rawallocator,RawAllocator}.
///
/// It deallocates memory for a specified type but does not call its destructors.
/// Only a reference to the \c RawAllocator is stored, access to it is synchronized by a given \c Mutex which defaults to \ref default_mutex.
/// \ingroup memory adapter
template <typename Type, class RawAllocator, class Mutex = default_mutex>
class allocator_deallocator : FOONATHAN_EBO(allocator_reference<RawAllocator, Mutex>)
{
static_assert(!std::is_abstract<Type>::value,
"use allocator_polymorphic_deallocator for storing base classes");

public:
using allocator_type =
typename allocator_reference<RawAllocator, Mutex>::allocator_type;
Expand Down Expand Up @@ -66,6 +72,8 @@ namespace foonathan
class allocator_deallocator<Type[], RawAllocator, Mutex>
: FOONATHAN_EBO(allocator_reference<RawAllocator, Mutex>)
{
static_assert(!std::is_abstract<Type>::value, "must not create polymorphic arrays");

public:
using allocator_type =
typename allocator_reference<RawAllocator, Mutex>::allocator_type;
Expand Down Expand Up @@ -116,12 +124,59 @@ namespace foonathan
std::size_t size_;
};

/// Similar to \ref allocator_deallocator but calls the destructors of the objects.
/// A deleter class that deallocates the memory of a derived type through a specified \concept{concept_rawallocator,RawAllocator}.
///
/// It can only be created from a \ref allocator_deallocator and thus must only be used for smart pointers initialized by derived-to-base conversion of the pointer.
/// \ingroup memory adapter
template <typename BaseType, class RawAllocator, class Mutex = default_mutex>
class allocator_polymorphic_deallocator
: FOONATHAN_EBO(allocator_reference<RawAllocator, Mutex>)
{
public:
using allocator_type =
typename allocator_reference<RawAllocator, Mutex>::allocator_type;
using mutex = Mutex;
using value_type = BaseType;

/// \effects Creates it from a deallocator for a derived type.
/// It will deallocate the memory as if done by the derived type.
template <typename T, FOONATHAN_REQUIRES((std::is_base_of<BaseType, T>::value))>
allocator_polymorphic_deallocator(allocator_deallocator<T, RawAllocator, Mutex> dealloc)
: allocator_reference<RawAllocator, Mutex>(dealloc.get_allocator()),
derived_size_(sizeof(T)),
derived_alignment_(FOONATHAN_ALIGNOF(T))
{
}

/// \effects Deallocates the memory given to it.
/// Calls \c deallocate_node(pointer, size, alignment) on the referenced allocator object,
/// where \c size and \c alignment are the values of the type it was created with.
void operator()(value_type* pointer) FOONATHAN_NOEXCEPT
{
this->deallocate_node(pointer, derived_size_, derived_alignment_);
}

/// \returns The reference to the allocator.
/// It has the same type as the call to \ref allocator_reference::get_allocator().
auto get_allocator() const FOONATHAN_NOEXCEPT -> decltype(
std::declval<allocator_reference<allocator_type, mutex>>().get_allocator())
{
return this->allocator_reference<allocator_type, mutex>::get_allocator();
}

private:
std::size_t derived_size_, derived_alignment_;
};

/// Similar to \ref allocator_deallocator but calls the destructors of the object.
/// Otherwise behaves the same.
/// \ingroup memory adapter
template <typename Type, class RawAllocator, class Mutex = default_mutex>
class allocator_deleter : FOONATHAN_EBO(allocator_reference<RawAllocator, Mutex>)
{
static_assert(!std::is_abstract<Type>::value,
"use allocator_polymorphic_deleter for storing base classes");

public:
using allocator_type =
typename allocator_reference<RawAllocator, Mutex>::allocator_type;
Expand Down Expand Up @@ -166,6 +221,8 @@ namespace foonathan
class allocator_deleter<Type[], RawAllocator, Mutex>
: FOONATHAN_EBO(allocator_reference<RawAllocator, Mutex>)
{
static_assert(!std::is_abstract<Type>::value, "must not create polymorphic arrays");

public:
using allocator_type =
typename allocator_reference<RawAllocator, Mutex>::allocator_type;
Expand Down Expand Up @@ -217,7 +274,55 @@ namespace foonathan
private:
std::size_t size_;
};
}
} // namespace foonathan::memory

/// Similar to \ref allocator_polymorphic_deallocator but calls the destructors of the object.
/// Otherwise behaves the same.
/// \note It has a relatively high space overhead, so only use it if you have to.
/// \ingroup memory adapter
template <typename BaseType, class RawAllocator, class Mutex = default_mutex>
class allocator_polymorphic_deleter
: FOONATHAN_EBO(allocator_reference<RawAllocator, Mutex>)
{
public:
using allocator_type =
typename allocator_reference<RawAllocator, Mutex>::allocator_type;
using mutex = Mutex;
using value_type = BaseType;

/// \effects Creates it from a deleter for a derived type.
/// It will deallocate the memory as if done by the derived type.
template <typename T, FOONATHAN_REQUIRES((std::is_base_of<BaseType, T>::value))>
allocator_polymorphic_deleter(allocator_deleter<T, RawAllocator, Mutex> deleter)
: allocator_reference<RawAllocator, Mutex>(deleter.get_allocator()),
derived_size_(sizeof(T)),
derived_alignment_(FOONATHAN_ALIGNOF(T))
{
FOONATHAN_MEMORY_ASSERT(std::size_t(derived_size_) == sizeof(T)
&& std::size_t(derived_alignment_) == FOONATHAN_ALIGNOF(T));
}

/// \effects Deallocates the memory given to it.
/// Calls \c deallocate_node(pointer, size, alignment) on the referenced allocator object,
/// where \c size and \c alignment are the values of the type it was created with.
void operator()(value_type* pointer) FOONATHAN_NOEXCEPT
{
pointer->~value_type();
this->deallocate_node(pointer, derived_size_, derived_alignment_);
}

/// \returns The reference to the allocator.
/// It has the same type as the call to \ref allocator_reference::get_allocator().
auto get_allocator() const FOONATHAN_NOEXCEPT -> decltype(
std::declval<allocator_reference<allocator_type, mutex>>().get_allocator())
{
return this->allocator_reference<allocator_type, mutex>::get_allocator();
}

private:
unsigned short derived_size_,
derived_alignment_; // use unsigned short here to save space
};
} // namespace memory
} // namespace foonathan

#endif //FOONATHAN_MEMORY_DELETER_HPP_INCLUDED
14 changes: 14 additions & 0 deletions include/foonathan/memory/smart_ptr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,26 @@ namespace foonathan
} // namespace detail

/// A \c std::unique_ptr that deletes using a \concept{concept_rawallocator,RawAllocator}.
///
/// It is an alias template using \ref allocator_deleter as \c Deleter class.
/// \ingroup memory adapter
template <typename T, class RawAllocator, class Mutex = default_mutex>
FOONATHAN_ALIAS_TEMPLATE(unique_ptr,
std::unique_ptr<T, allocator_deleter<T, RawAllocator, Mutex>>);

/// A \c std::unique_ptr that deletes using a \concept{concept_rawallocator,RawAllocator} and allows polymorphic types.
///
/// It can only be created by converting a regular unique pointer to a pointer to a derived class,
/// and is meant to be used inside containers.
/// It is an alias template using \ref allocator_polymorphic_deleter as \c Deleter class.
/// \note It has a relatively high overhead, so only use it if you have to.
/// \ingroup memory adapter
template <class BaseType, class RawAllocator, class Mutex = default_mutex>
FOONATHAN_ALIAS_TEMPLATE(
unique_base_ptr,
std::unique_ptr<BaseType,
allocator_polymorphic_deleter<BaseType, RawAllocator, Mutex>>);

/// Creates a \c std::unique_ptr using a \concept{concept_rawallocator,RawAllocator} for the allocation.
/// \effects Allocates memory for the given type using the allocator
/// and creates a new object inside it passing the given arguments to its constructor.
Expand Down

0 comments on commit d18f214

Please sign in to comment.