Logo ROOT   6.18/05
Reference Guide
RAdoptAllocator.hxx
Go to the documentation of this file.
1// Author: Enrico Guiraud, Enric Tejedor, Danilo Piparo CERN 01/2018
2
3/*************************************************************************
4 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#ifndef ROOT_TADOPTALLOCATOR
12#define ROOT_TADOPTALLOCATOR
13
14#include <iostream>
15#include <memory>
16
17namespace ROOT {
18namespace Detail {
19namespace VecOps {
20
21/**
22\class ROOT::Detail::VecOps::RAdoptAllocator
23\ingroup vecops
24\brief RAdoptAllocator can provide a view on already allocated memory.
25
26The RAdoptAllocator behaves like the standard allocator, and, as such, can be used to create
27stl containers. In addition, it behaves as if it allocated a certain memory region which
28is indeed not managed by it, but rather is "adopted".
29This is most useful to take advantage of widely adopted entities such as std::vector in a
30novel way, namely offering nice interfaces around an arbitrary memory region.
31
32If memory is adopted, the first allocation returns the address of this memory region. For
33the subsequent allocations, the RAdoptAllocator behaves like a standard allocator.
34
35For example:
36~~~{.cpp}
37std::vector<double> model {1, 2, 3};
38unsigned int dummy;
39RAdoptAllocator<double> alloc(model.data(), model.size());
40std::vector<double, RAdoptAllocator<double>> v(model.size(), 0., alloc);
41~~~
42Now the vector *v* is ready to be used, de facto proxying the memory of the vector *model*.
43Upon a second allocation, the vector *v* ceases to be a proxy
44~~~{.cpp}
45v.emplace_back(0.);
46~~~
47now the vector *v* owns its memory as a regular vector.
48**/
49
50template <typename T>
52public:
53 friend class RAdoptAllocator<bool>;
54
56 using propagate_on_container_swap = std::true_type;
57 using StdAlloc_t = std::allocator<T>;
58 using value_type = typename StdAlloc_t::value_type;
59 using pointer = typename StdAlloc_t::pointer;
60 using const_pointer = typename StdAlloc_t::const_pointer;
61 using reference = typename StdAlloc_t::reference;
62 using const_reference = typename StdAlloc_t::const_reference;
63 using size_type = typename StdAlloc_t::size_type;
64 using difference_type = typename StdAlloc_t::difference_type;
65 template <typename U>
66 struct rebind {
68 };
69
70private:
71 enum class EAllocType : char { kOwning, kAdopting, kAdoptingNoAllocYet };
72 using StdAllocTraits_t = std::allocator_traits<StdAlloc_t>;
76
77public:
78 /// This is the constructor which allows the allocator to adopt a certain memory region.
80 RAdoptAllocator() = default;
81 RAdoptAllocator(const RAdoptAllocator &) = default;
86
87 /// Construct an object at a certain memory address
88 /// \tparam U The type of the memory address at which the object needs to be constructed
89 /// \tparam Args The arguments' types necessary for the construction of the object
90 /// \param[in] p The memory address at which the object needs to be constructed
91 /// \param[in] args The arguments necessary for the construction of the object
92 /// This method is a no op if memory has been adopted.
93 template <class U, class... Args>
94 void construct(U *p, Args &&... args)
95 {
96 // We refuse to do anything since we assume the memory is already initialised
98 return;
99 fStdAllocator.construct(p, args...);
100 }
101
102 /// \brief Allocate some memory
103 /// If an address has been adopted, at the first call, that address is returned.
104 /// Subsequent calls will make "decay" the allocator to a regular stl allocator.
105 pointer allocate(std::size_t n)
106 {
107 if (n > std::size_t(-1) / sizeof(T))
108 throw std::bad_alloc();
111 return fInitialAddress;
112 }
114 return StdAllocTraits_t::allocate(fStdAllocator, n);
115 }
116
117 /// \brief Dellocate some memory if that had not been adopted.
118 void deallocate(pointer p, std::size_t n)
119 {
120 if (p != fInitialAddress)
121 StdAllocTraits_t::deallocate(fStdAllocator, p, n);
122 }
123
124 template <class U>
125 void destroy(U *p)
126 {
128 fStdAllocator.destroy(p);
129 }
130 }
131
133 {
134 return fInitialAddress == other.fInitialAddress && fAllocType == other.fAllocType &&
136 }
137
138 bool operator!=(const RAdoptAllocator<T> &other) { return !(*this == other); }
139
140 size_type max_size() const { return fStdAllocator.max_size(); };
141};
142
143// The different semantics of std::vector<bool> make memory adoption through a
144// custom allocator more complex -- namely, RAdoptAllocator<bool> must be rebindable
145// to RAdoptAllocator<unsigned long>, but if adopted memory is really a buffer of
146// bools reinterpretation of the buffer is not going to work. As a workaround,
147// RAdoptAllocator<bool> is specialized to be a simple allocator that forwards calls
148// to std::allocator and never adopts memory.
149template <>
150class RAdoptAllocator<bool> {
151 std::allocator<bool> fStdAllocator;
152
153public:
154 template <typename U>
155 struct rebind {
157 };
158
159 template <typename T>
160 friend class RAdoptAllocator;
161
162 using StdAlloc_t = std::allocator<bool>;
163 using value_type = typename StdAlloc_t::value_type;
164 using pointer = typename StdAlloc_t::pointer;
165 using const_pointer = typename StdAlloc_t::const_pointer;
166 using reference = typename StdAlloc_t::reference;
167 using const_reference = typename StdAlloc_t::const_reference;
168 using size_type = typename StdAlloc_t::size_type;
169 using difference_type = typename StdAlloc_t::difference_type;
170
171 RAdoptAllocator() = default;
173
174 template <typename U>
176 {
178 throw std::runtime_error("Cannot rebind owning RAdoptAllocator");
179 }
180
181 bool *allocate(std::size_t n) { return fStdAllocator.allocate(n); }
182
183 template <typename U, class... Args>
184 void construct(U *p, Args &&... args)
185 {
186 fStdAllocator.construct(p, std::forward<Args>(args)...);
187 }
188
189 void deallocate(bool *p, std::size_t s) noexcept { fStdAllocator.deallocate(p, s); }
190
191 template <class U>
192 void destroy(U *p)
193 {
194 fStdAllocator.destroy(p);
195 }
196
197 bool operator==(const RAdoptAllocator &) { return true; }
198
199 bool operator!=(const RAdoptAllocator &) { return false; }
200};
201
202template <typename T>
203RAdoptAllocator<T>::RAdoptAllocator(const RAdoptAllocator<bool> &o) : fStdAllocator(o.fStdAllocator) {}
204
205} // End NS VecOps
206} // End NS Detail
207} // End NS ROOT
208
209#endif
typename StdAlloc_t::value_type value_type
typename StdAlloc_t::const_reference const_reference
typename StdAlloc_t::const_pointer const_pointer
void deallocate(bool *p, std::size_t s) noexcept
RAdoptAllocator(const RAdoptAllocator &)=default
RAdoptAllocator(const RAdoptAllocator< U > &o)
typename StdAlloc_t::difference_type difference_type
RAdoptAllocator can provide a view on already allocated memory.
typename StdAlloc_t::size_type size_type
RAdoptAllocator(RAdoptAllocator &&)=default
bool operator==(const RAdoptAllocator< T > &other)
RAdoptAllocator(const RAdoptAllocator &)=default
RAdoptAllocator & operator=(RAdoptAllocator &&)=default
typename StdAlloc_t::const_reference const_reference
std::allocator_traits< StdAlloc_t > StdAllocTraits_t
RAdoptAllocator & operator=(const RAdoptAllocator &)=default
typename StdAlloc_t::value_type value_type
void deallocate(pointer p, std::size_t n)
Dellocate some memory if that had not been adopted.
bool operator!=(const RAdoptAllocator< T > &other)
RAdoptAllocator(pointer p)
This is the constructor which allows the allocator to adopt a certain memory region.
typename StdAlloc_t::const_pointer const_pointer
void construct(U *p, Args &&... args)
Construct an object at a certain memory address.
pointer allocate(std::size_t n)
Allocate some memory If an address has been adopted, at the first call, that address is returned.
typename StdAlloc_t::pointer pointer
typename StdAlloc_t::difference_type difference_type
typename StdAlloc_t::reference reference
const Int_t n
Definition: legend1.C:16
double T(double x)
Definition: ChebyshevPol.h:34
Namespace for new ROOT classes and functions.
Definition: StringConv.hxx:21
static constexpr double s