1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
|
/*!********************************************************************
Audacity: A Digital Audio Editor
@file PackedArray.h
@brief Smart pointer for a header contiguous with an array holding a
dynamically determined number of elements
Paul Licameli
**********************************************************************/
#ifndef __AUDACITY_PACKED_ARRAY__
#define __AUDACITY_PACKED_ARRAY__
#include <type_traits>
namespace PackedArray {
template<typename T> struct Traits;
namespace detail {
//! Primary template of metafunction deducing other things from Traits<T>
template<typename T, typename = void> struct ExtendedTraits : Traits<T> {
using element_type = typename Traits<T>::element_type;
using iterated_type = T;
static iterated_type *begin(T *p) { return p; }
static element_type *element_ptr(T *p) { return p; }
static size_t element_offset() { return 0; }
};
//! Partial specialization used when Traits<T>::array_member is defined
template<typename T> struct ExtendedTraits<T, std::void_t<decltype(
std::declval<T>().*(Traits<T>::array_member)
)>> : Traits<T> {
using element_type = typename Traits<T>::element_type;
using member_type = std::remove_reference_t<decltype(
std::declval<T>().*(Traits<T>::array_member)
)>;
// Check restrictions on usage. The member is expected to have dimension 1
static_assert(std::extent_v<member_type> == 1);
using iterated_type = std::remove_extent_t<member_type>;
// Check that any overlay of a type with a destructor is sensible
static_assert(sizeof(iterated_type) == sizeof(element_type));
static iterated_type *begin(T *p) {
return &(p->*(Traits<T>::array_member))[0];
}
static element_type *element_ptr(T *p) {
return reinterpret_cast<element_type*>(begin(p));
}
static size_t element_offset() {
return reinterpret_cast<size_t>(begin(0));
}
};
}
//! Primary template used in Deleter that can be specialized
/*!
Specializations that define a pointer-to-data-member of T called array_member
are treated specially
*/
template<typename T> struct Traits{
// Default assumption is that there is no header and no need to overlay
// the element with a type that performs nontrivial destruction
struct header_type {};
using element_type = T;
};
//! Deleter for an array of elements and optional contiguous header structure
/*!
@tparam Type is the type pointed to, and an explicit specialization of
Traits<Type> may redefine the nested types for a non-empty header:
- header_type, which overlays the members of Type except the last member;
may be non-empty and define a destructor
- element_type must be such that the last member of Type is Element[1]; may
define a destructor
And Traits<Type> may also define a pointer-to-data-member of T called
array_member
@tparam BaseDeleter manages the main deallocation
*/
template<typename Type,
template<typename> typename BaseDeleter = std::default_delete>
struct Deleter : BaseDeleter<Type> {
using managed_type = Type;
using base_type = BaseDeleter<managed_type>;
using traits_type = detail::ExtendedTraits<managed_type>;
using header_type = typename traits_type::header_type;
using element_type = typename traits_type::element_type;
//! Nontrivial, implicit constructor of the deleter takes a size, which
//! defaults to 0 to allow default contruction of the unique_ptr
Deleter(size_t size = 0) noexcept
: mCount{ size
? (size - traits_type::element_offset()) / sizeof(element_type)
: 0
}
{}
void operator()(Type *p) const noexcept(noexcept(
(void)std::declval<element_type>().~element_type(),
(void)std::declval<header_type>().~header_type(),
(void)std::declval<base_type>()(p)
)) {
if (!p)
return;
// Do nested deallocations for elements by decreasing subscript
auto pE = traits_type::element_ptr(p) + mCount;
for (auto count = mCount; count--;)
(--pE)->~element_type();
// Do nested deallocations for main structure
reinterpret_cast<const header_type*>(p)->~header_type();
// main deallocation
((base_type&)*this)(p);
}
size_t GetCount() const { return mCount; }
private:
size_t mCount = 0;
};
//! Smart pointer type that deallocates with Deleter
template<typename Type,
template<typename> typename BaseDeleter = std::default_delete>
struct Ptr
: std::unique_ptr<Type, Deleter<Type, BaseDeleter>>
{
using
std::unique_ptr<Type, Deleter<Type, BaseDeleter>>::unique_ptr;
//! Enables subscripting. Does not check for null!
auto &operator[](size_t ii) const
{
return *(begin(*this) + ii);
}
};
//! Find out how many elements were allocated with a Ptr
template<typename Type, template<typename> typename BaseDeleter>
inline size_t Count(const Ptr<Type, BaseDeleter> &p)
{
return p.get_deleter().GetCount();
}
//! Enables range-for
template<typename Type, template<typename> typename BaseDeleter>
inline auto begin(const Ptr<Type, BaseDeleter> &p)
{
using traits_type = detail::ExtendedTraits<Type>;
auto ptr = p.get();
return ptr ? traits_type::begin(ptr) : nullptr;
}
//! Enables range-for
template<typename Type, template<typename> typename BaseDeleter>
inline auto end(const Ptr<Type, BaseDeleter> &p)
{
auto result = begin(p);
if (result)
result += Count(p);
return result;
}
}
struct PackedArray_t{};
//! Tag argument to distinguish an overload of ::operator new
static constexpr inline PackedArray_t PackedArrayArg;
//! Allocator for objects managed by PackedArray::Ptr enlarges the size request
//! that the compiler makes
inline void *operator new(size_t size, PackedArray_t, size_t enlarged) {
return ::operator new(std::max(size, enlarged));
}
//! Complementary operator delete in case of exceptions in a new-expression
inline void operator delete(void *p, PackedArray_t, size_t) {
::operator delete(p);
}
namespace PackedArray {
//! Allocate a Ptr<Type> holding at least `enlarged` bytes
/*!
Usage: AllocateBytes<Type>(bytes)(constructor arguments for Type)
Meant to resemble placement-new syntax with separation of allocator and
constructor arguments
*/
template<typename Type> auto AllocateBytes(size_t enlarged)
{
return [enlarged](auto &&...args) {
return Ptr<Type>{
safenew(PackedArrayArg, enlarged)
Type{ std::forward<decltype(args)>(args)... },
std::max(sizeof(Type), enlarged) // Deleter's constructor argument
};
};
}
//! Allocate a Ptr<Type> holding `count` elements
/*!
Usage: AllocateCount<Type>(count)(constructor arguments for Type)
Meant to resemble placement-new syntax with separation of allocator and
constructor arguments
If Traits<Type> defines a nonempty header, the result always holds
at least one element
*/
template<typename Type> auto AllocateCount(size_t count)
{
using traits_type = detail::ExtendedTraits<Type>;
const auto bytes = traits_type::element_offset()
+ count * sizeof(typename traits_type::element_type);
return AllocateBytes<Type>(bytes);
}
}
#endif
|