casacore
Storage.h
Go to the documentation of this file.
1 #ifndef CASACORE_STORAGE_2_H
2 #define CASACORE_STORAGE_2_H
3 
4 #include <cstring>
5 #include <memory>
6 
7 namespace casacore {
8 
9 namespace arrays_internal {
10 
11 // This class emplements a static (but run-time) sized array. It is used in the
12 // Array class, and is necessary because std::vector specializes for bool.
13 // It holds the same functionality as a normal array, and enables allocation
14 // through different allocators similar to std::vector.
15 template<typename T, typename Alloc>
16 class Storage : public Alloc
17 {
18 public:
19  // Construct an empty Storage
20  Storage(const Alloc& allocator) :
21  Alloc(allocator),
22  _data(nullptr),
23  _end(nullptr),
24  _isShared(false)
25  { }
26 
27  // Construct Storage with a given size.
28  // The elements will be default constructed
29  Storage(std::size_t n, const Alloc& allocator) :
30  Alloc(allocator),
31  _data(construct(n)),
32  _end(_data + n),
33  _isShared(false)
34  { }
35 
36  // Construct Storage with a given size.
37  // The elements will be copy constructed from the given value
38  Storage(std::size_t n, const T& val, const Alloc& allocator) :
39  Alloc(allocator),
40  _data(construct(n, val)),
41  _end(_data + n),
42  _isShared(false)
43  { }
44 
45  // Construct Storage from a range.
46  // The elements will be copy constructed from the given values.
47  // Note that this constructor can be chosen over
48  // of Storage(std::size_t, const T&, const Alloc) when T=size_t. Therefore,
49  // this constructor forwards to the appropriate constructor based on
50  // whether T is an integral.
51  template<typename InputIterator>
52  Storage(InputIterator startIter, InputIterator endIter, const Alloc& allocator) :
53  Storage(startIter, endIter, allocator,
55  std::is_integral<InputIterator>,
57  std::is_same<InputIterator, const char*>,
58  std::is_base_of<std::string, T>
59  >
60  >())
61  { }
62 
63  // Construct Storage from a range by moving.
64  // The elements will be move constructed from the given values.
65  static std::unique_ptr<Storage<T, Alloc>> MakeFromMove(T* startIter, T* endIter, const Alloc& allocator)
66  {
67  return std::unique_ptr<Storage<T, Alloc>>(new Storage(startIter, endIter, allocator, std::false_type(), std::true_type()));
68  }
69 
70  // Construct a Storage from existing data.
71  // The given pointer will not be owned by this class.
72  static std::unique_ptr<Storage<T, Alloc>> MakeFromSharedData(T* existingData, size_t n, const Alloc& allocator)
73  {
74  std::unique_ptr<Storage<T, Alloc>> newStorage = std::unique_ptr<Storage>(new Storage<T, Alloc>(allocator));
75  newStorage->_data = existingData;
76  newStorage->_end = existingData + n;
77  newStorage->_isShared = true;
78  return newStorage;
79  }
80 
81  // Construct a Storage with uninitialized data.
82  // This will skip the constructor of the elements. This is only allowed for
83  // trivial types.
84  static std::unique_ptr<Storage<T, Alloc>> MakeUninitialized(size_t n, const Alloc& allocator)
85  {
86  static_assert(std::is_trivial<T>::value, "Only trivial types can be constructed uninitialized");
87  std::unique_ptr<Storage<T, Alloc>> newStorage = std::unique_ptr<Storage>(new Storage<T, Alloc>(allocator));
88  if(n == 0)
89  newStorage->_data = nullptr;
90  else
91  newStorage->_data = static_cast<Alloc&>(*newStorage).allocate(n);
92  newStorage->_end = newStorage->_data + n;
93  return newStorage;
94  }
95 
96  // Destructs the elements and deallocates the data
97  ~Storage() noexcept
98  {
99  if(size() && !_isShared)
100  {
101  for(size_t i=0; i!=size(); ++i)
102  _data[size()-i-1].~T();
103  Alloc::deallocate(_data, size());
104  }
105  }
106 
107  // Return a pointer to the storage data.
108  // @{
109  T* data() { return _data; }
110  const T* data() const { return _data; }
111  // @}
112 
113  // Size of the data, zero if empty.
114  size_t size() const { return _end - _data; }
115 
116  // Returns the allocator associated with this Storage.
117  const Alloc& get_allocator() const { return static_cast<const Alloc&>(*this); }
118 
119  // Whether this Storage owns its data.
120  // Returns @c true when this Storage was constructed with MakeFromSharedData().
121  bool is_shared() const { return _isShared; }
122 
123  Storage(const Storage<T, Alloc>&) = delete;
125  Storage& operator=(const Storage&) = delete;
126  Storage& operator=(Storage&&) = delete;
127 
128 private:
129  // Moving range constructor implementation. Parameter integral is only a place-holder.
130  Storage(T* startIter, T* endIter, const Alloc& allocator, std::false_type /*integral*/, std::true_type /*move*/) :
131  Alloc(allocator),
132  _data(construct_move(startIter, endIter)),
133  _end(_data + (endIter-startIter)),
134  _isShared(false)
135  { }
136 
137  // Copying range constructor implementation for non-integral types
138  template<typename InputIterator>
139  Storage(InputIterator startIter, InputIterator endIter, const Alloc& allocator, std::false_type /*integral*/) :
140  Alloc(allocator),
141  _data(construct_range(startIter, endIter)),
142  _end(_data + std::distance(startIter, endIter)),
143  _isShared(false)
144  { }
145 
146  // Copying range constructor implementation for integral types
147  template<typename Integral>
148  Storage(Integral n, Integral val, const Alloc& allocator, std::true_type /*integral*/) :
149  Alloc(allocator),
150  _data(construct(n, val)),
151  _end(_data + n),
152  _isShared(false)
153  { }
154 
155  // These methods allocate the storage and construct the elements.
156  // When any element constructor throws, the already constructed elements are destructed in reverse
157  // and the allocated storage is deallocated.
158  // @{
159 
160  T* construct(size_t n)
161  {
162  if(n == 0)
163  return nullptr;
164  else {
165  T* data = Alloc::allocate(n);
166  T* current = data;
167  try {
168  for (; current != data+n; ++current) {
169  new (current) T();
170  }
171  } catch(...) {
172  while(current != data)
173  {
174  --current;
175  current->~T();
176  }
177  Alloc::deallocate(data, n);
178  throw;
179  }
180  return data;
181  }
182  }
183 
184  T* construct(size_t n, const T& val)
185  {
186  if(n == 0)
187  return nullptr;
188  else {
189  T* data = Alloc::allocate(n);
190  T* current = data;
191  try {
192  for (; current != data+n; ++current) {
193  new (current) T(val);
194  }
195  } catch(...) {
196  while(current != data)
197  {
198  --current;
199  current->~T();
200  }
201  Alloc::deallocate(data, n);
202  throw;
203  }
204  return data;
205  }
206  }
207 
208  template<typename InputIterator>
209  T* construct_range(InputIterator startIter, InputIterator endIter)
210  {
211  if(startIter == endIter)
212  return nullptr;
213  else {
214  size_t n = std::distance(startIter, endIter);
215  T* data = Alloc::allocate(n);
216  T* current = data;
217  try {
218  for (; current != data+n; ++current) {
219  new (current) T(*startIter);
220  ++startIter;
221  }
222  } catch(...) {
223  while(current != data)
224  {
225  --current;
226  current->~T();
227  }
228  Alloc::deallocate(data, n);
229  throw;
230  }
231  return data;
232  }
233  }
234 
235  T* construct_move(T* startIter, T* endIter)
236  {
237  if(startIter == endIter)
238  return nullptr;
239  else {
240  size_t n = endIter - startIter;
241  T* data = Alloc::allocate(n);
242  T* current = data;
243  try {
244  for (; current != data+n; ++current) {
245  new (current) T(std::move(*startIter));
246  ++startIter;
247  }
248  } catch(...) {
249  while(current != data)
250  {
251  --current;
252  current->~T();
253  }
254  Alloc::deallocate(data, n);
255  throw;
256  }
257  return data;
258  }
259  }
260 
261  // @}
262 
263  // Used by template code above
264  // These are already in C++17, but currently only using C++11...
265  template<class...> struct disjunction : std::false_type { };
266  template<class B1> struct disjunction<B1> : B1 { };
267  template<class B1, class... Bn>
268  struct disjunction<B1, Bn...>
269  : std::conditional<bool(B1::value), B1, disjunction<Bn...>>::type { };
270 
271  template<class...> struct conjunction : std::true_type { };
272  template<class B1> struct conjunction<B1> : B1 { };
273  template<class B1, class... Bn>
274  struct conjunction<B1, Bn...>
275  : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
276 
277  T* _data;
278  T* _end;
279  bool _isShared;
280 };
281 
282 } }
283 
284 #endif
This class emplements a static (but run-time) sized array.
Definition: Storage.h:17
Storage(const Storage< T, Alloc > &)=delete
size_t size() const
Size of the data, zero if empty.
Definition: Storage.h:114
T * construct(size_t n, const T &val)
Definition: Storage.h:184
static std::unique_ptr< Storage< T, Alloc > > MakeUninitialized(size_t n, const Alloc &allocator)
Construct a Storage with uninitialized data.
Definition: Storage.h:84
Storage(T *startIter, T *endIter, const Alloc &allocator, std::false_type, std::true_type)
Moving range constructor implementation.
Definition: Storage.h:130
Storage(InputIterator startIter, InputIterator endIter, const Alloc &allocator)
Construct Storage from a range.
Definition: Storage.h:52
~Storage() noexcept
Destructs the elements and deallocates the data.
Definition: Storage.h:97
static std::unique_ptr< Storage< T, Alloc > > MakeFromMove(T *startIter, T *endIter, const Alloc &allocator)
Construct Storage from a range by moving.
Definition: Storage.h:65
T * construct_range(InputIterator startIter, InputIterator endIter)
Definition: Storage.h:209
Storage & operator=(const Storage &)=delete
Storage(Integral n, Integral val, const Alloc &allocator, std::true_type)
Copying range constructor implementation for integral types.
Definition: Storage.h:148
Storage(const Alloc &allocator)
Construct an empty Storage.
Definition: Storage.h:20
T * construct(size_t n)
These methods allocate the storage and construct the elements.
Definition: Storage.h:160
Storage(InputIterator startIter, InputIterator endIter, const Alloc &allocator, std::false_type)
Copying range constructor implementation for non-integral types.
Definition: Storage.h:139
Storage(std::size_t n, const T &val, const Alloc &allocator)
Construct Storage with a given size.
Definition: Storage.h:38
const Alloc & get_allocator() const
Returns the allocator associated with this Storage.
Definition: Storage.h:117
Storage(std::size_t n, const Alloc &allocator)
Construct Storage with a given size.
Definition: Storage.h:29
Storage(Storage< T, Alloc > &&)=delete
Storage & operator=(Storage &&)=delete
bool is_shared() const
Whether this Storage owns its data.
Definition: Storage.h:121
static std::unique_ptr< Storage< T, Alloc > > MakeFromSharedData(T *existingData, size_t n, const Alloc &allocator)
Construct a Storage from existing data.
Definition: Storage.h:72
T * data()
Return a pointer to the storage data.
Definition: Storage.h:109
T * construct_move(T *startIter, T *endIter)
Definition: Storage.h:235
this file contains all the compiler specific defines
Definition: mainpage.dox:28
LatticeExprNode value(const LatticeExprNode &expr)
This function returns the value of the expression without a mask.
Define real & complex conjugation for non-complex types and put comparisons into std namespace.
Definition: Complex.h:352
Used by template code above These are already in C++17, but currently only using C++11....
Definition: Storage.h:265