Logo ROOT   6.14/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 
17 namespace ROOT {
18 namespace Detail {
19 namespace VecOps {
20 
21 /**
22 \class ROOT::Detail::VecOps::RAdoptAllocator
23 \ingroup vecops
24 \brief RAdoptAllocator can provide a view on already allocated memory.
25 
26 The RAdoptAllocator behaves like the standard allocator, and, as such, can be used to create
27 stl containers. In addition, it behaves as if it allocated a certain memory region which
28 is indeed not managed by it, but rather is "adopted".
29 This is most useful to take advantage of widely adopted entities such as std::vector in a
30 novel way, namely offering nice interfaces around an arbitrary memory region.
31 
32 If memory is adopted, the first allocation returns the address of this memory region. For
33 the subsequent allocations, the RAdoptAllocator behaves like a standard allocator.
34 
35 For example:
36 ~~~{.cpp}
37 std::vector<double> model {1, 2, 3};
38 unsigned int dummy;
39 RAdoptAllocator<double> alloc(model.data(), model.size());
40 std::vector<double, RAdoptAllocator<double>> v(model.size(), 0., alloc);
41 ~~~
42 Now the vector *v* is ready to be used, de facto proxying the memory of the vector *model*.
43 Upon a second allocation, the vector *v* ceases to be a proxy
44 ~~~{.cpp}
45 v.emplace_back(0.);
46 ~~~
47 now the vector *v* owns its memory as a regular vector.
48 **/
49 
50 template <typename T>
52 public:
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 
70 private:
71  enum class EAllocType : char { kOwning, kAdopting, kAdoptingNoAllocYet };
72  using StdAllocTraits_t = std::allocator_traits<StdAlloc_t>;
76 
77 public:
78  /// This is the constructor which allows the allocator to adopt a certain memory region.
79  RAdoptAllocator(pointer p) : fInitialAddress(p), fAllocType(EAllocType::kAdoptingNoAllocYet){};
80  RAdoptAllocator() = default;
81  RAdoptAllocator(const RAdoptAllocator &) = default;
82  RAdoptAllocator(RAdoptAllocator &&) = default;
83  RAdoptAllocator &operator=(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
97  if (EAllocType::kAdopting == fAllocType)
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();
109  if (EAllocType::kAdoptingNoAllocYet == fAllocType) {
110  fAllocType = EAllocType::kAdopting;
111  return fInitialAddress;
112  }
113  fAllocType = EAllocType::kOwning;
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  {
127  if (EAllocType::kAdopting != fAllocType) {
128  fStdAllocator.destroy(p);
129  }
130  }
131 
133  {
134  return fInitialAddress == other.fInitialAddress && fAllocType == other.fAllocType &&
135  fStdAllocator == other.fStdAllocator;
136  }
137 
138  bool operator!=(const RAdoptAllocator<T> &other) { return !(*this == other); }
139 };
140 
141 // The different semantics of std::vector<bool> make memory adoption through a
142 // custom allocator more complex -- namely, RAdoptAllocator<bool> must be rebindable
143 // to RAdoptAllocator<unsigned long>, but if adopted memory is really a buffer of
144 // bools reinterpretation of the buffer is not going to work. As a workaround,
145 // RAdoptAllocator<bool> is specialized to be a simple allocator that forwards calls
146 // to std::allocator and never adopts memory.
147 template <>
148 class RAdoptAllocator<bool> {
149  std::allocator<bool> fStdAllocator;
150 
151 public:
152  template <typename U>
153  struct rebind {
155  };
156 
157  template <typename T>
158  friend class RAdoptAllocator;
159 
160  using StdAlloc_t = std::allocator<bool>;
161  using value_type = typename StdAlloc_t::value_type;
162  using pointer = typename StdAlloc_t::pointer;
163  using const_pointer = typename StdAlloc_t::const_pointer;
164  using reference = typename StdAlloc_t::reference;
165  using const_reference = typename StdAlloc_t::const_reference;
166  using size_type = typename StdAlloc_t::size_type;
167  using difference_type = typename StdAlloc_t::difference_type;
168 
169  RAdoptAllocator() = default;
170  RAdoptAllocator(const RAdoptAllocator &) = default;
171 
172  template <typename U>
173  RAdoptAllocator(const RAdoptAllocator<U> &o) : fStdAllocator(o.fStdAllocator)
174  {
176  throw std::runtime_error("Cannot rebind owning RAdoptAllocator");
177  }
178 
179  bool *allocate(std::size_t n) { return fStdAllocator.allocate(n); }
180 
181  template <typename U, class... Args>
182  void construct(U *p, Args &&... args)
183  {
184  fStdAllocator.construct(p, std::forward<Args>(args)...);
185  }
186 
187  void deallocate(bool *p, std::size_t s) noexcept { fStdAllocator.deallocate(p, s); }
188 
189  template <class U>
190  void destroy(U *p)
191  {
192  fStdAllocator.destroy(p);
193  }
194 
195  bool operator==(const RAdoptAllocator &) { return true; }
196 
197  bool operator!=(const RAdoptAllocator &) { return false; }
198 };
199 
200 template <typename T>
202 
203 } // End NS VecOps
204 } // End NS Detail
205 } // End NS ROOT
206 
207 #endif
pointer allocate(std::size_t n)
Allocate some memory If an address has been adopted, at the first call, that address is returned...
void construct(U *p, Args &&... args)
Construct an object at a certain memory address.
RAdoptAllocator can provide a view on already allocated memory.
typename StdAlloc_t::const_reference const_reference
Namespace for new ROOT classes and functions.
Definition: StringConv.hxx:21
typename StdAlloc_t::size_type size_type
typename StdAlloc_t::const_pointer const_pointer
double T(double x)
Definition: ChebyshevPol.h:34
typename StdAlloc_t::pointer pointer
std::allocator_traits< StdAlloc_t > StdAllocTraits_t
void deallocate(pointer p, std::size_t n)
Dellocate some memory if that had not been adopted.
bool operator==(const RAdoptAllocator< T > &other)
typename StdAlloc_t::value_type value_type
typename StdAlloc_t::difference_type difference_type
typename StdAlloc_t::const_reference const_reference
RAdoptAllocator(const RAdoptAllocator< U > &o)
typename StdAlloc_t::reference reference
RAdoptAllocator & operator=(const RAdoptAllocator &)=default
bool operator!=(const RAdoptAllocator< T > &other)
void deallocate(bool *p, std::size_t s) noexcept
RAdoptAllocator(pointer p)
This is the constructor which allows the allocator to adopt a certain memory region.
static constexpr double s
typename StdAlloc_t::const_pointer const_pointer
typename StdAlloc_t::difference_type difference_type
typename StdAlloc_t::value_type value_type
const Int_t n
Definition: legend1.C:16