PandaTree
Collection.h
1 #ifndef PandaTree_Framework_Collection_h
2 #define PandaTree_Framework_Collection_h
3 
4 #include "CollectionBase.h"
5 #include "Iterator.h"
6 
7 #include <stdexcept>
8 #include <memory>
9 
10 namespace panda {
11 
13 
18  template<class E>
19  class Collection : public E::base_type::collection_type {
20  public:
21  typedef Collection<E> self_type;
22  typedef typename E::base_type::collection_type base_type;
23  typedef E value_type;
24  typedef typename value_type::datastore data_type;
25  typedef value_type& reference;
26  typedef value_type const& const_reference;
27  typedef value_type* pointer;
28  typedef value_type const* const_pointer;
31 
32  Collection(char const* name = "", UInt_t initialMax = 64) : base_type(name, sizeof(E), kFALSE) { allocate_(initialMax); }
33  Collection(self_type const& src) : base_type(src.ContainerBase::name_, sizeof(E), kFALSE) { allocate_(src.data.nmax()); copy(src); }
34  ~Collection();
35  self_type& operator=(self_type const& rhs) { copy(rhs); return *this; }
36 
38  reference at(UInt_t idx);
40  const_reference at(UInt_t idx) const;
42  reference operator[](UInt_t idx) { return *addr_(idx); }
44  const_reference operator[](UInt_t idx) const { return *const_addr_(idx); }
46  iterator begin() { return iterator(addr_(), ContainerBase::unitSize_); }
48  const_iterator begin() const { return const_iterator(const_addr_(), ContainerBase::unitSize_); }
50  iterator end() { return iterator(addr_(CollectionBase::size()), ContainerBase::unitSize_); }
52  const_iterator end() const { return const_iterator(const_addr_(CollectionBase::size()), ContainerBase::unitSize_); }
54  reference front() { return *addr_(); }
56  const_reference front() const { return *const_addr_(); }
58  reference back() { return *addr_(CollectionBase::size() - 1); }
60  const_reference back() const { return *const_addr_(CollectionBase::size() - 1); }
62 
65  void copy(self_type const&);
67 
72  void push_back(const_reference);
74 
77  reference create_back();
78 
79  Element::datastore& getData() override { return data; }
80  Element::datastore const& getData() const override { return data; }
81  Element& elemAt(UInt_t idx) override { return at(idx); }
82  Element const& elemAt(UInt_t idx) const override { return at(idx); }
83 
84  std::vector<UInt_t> sort(ContainerBase::Comparison const&) override;
85 
86  void print(std::ostream& = std::cout, UInt_t level = 1) const override;
87  void dump(std::ostream& = std::cout) const override;
88 
89  data_type data{};
90 
91  protected:
92  Collection(char const* name, UInt_t unitSize, Bool_t dummy) : base_type(name, unitSize, kFALSE) {}
93 
94  void reallocate_(UInt_t) override;
95 
96  private:
97  value_type* addr_(unsigned idx = 0);
98  value_type const* const_addr_(unsigned idx = 0) const;
99 
100  template<class T = E>
101  typename std::enable_if<std::is_constructible<T>::value>::type allocate_(UInt_t);
102  template<class T = E>
103  typename std::enable_if<!std::is_constructible<T>::value>::type allocate_(UInt_t);
104 
105  template<class T = E>
106  typename std::enable_if<std::is_constructible<T>::value>::type
107  deallocate_(value_type* addr, data_type&);
108  template<class T = E>
109  typename std::enable_if<!std::is_constructible<T>::value>::type
110  deallocate_(value_type* addr, data_type&);
111  };
112 
113  template<class E>
115  {
116  if (ContainerBase::array_) {
117  deallocate_(addr_(), data);
118 
119  // set the pointer to null so that the parent class destructors don't deallocate again
120  ContainerBase::array_ = 0;
121  }
122  }
123 
124  template<class E>
125  typename Collection<E>::reference
126  Collection<E>::at(UInt_t _idx)
127  {
128  if (_idx >= CollectionBase::size())
129  throw std::out_of_range((ContainerBase::name_ + "::at").Data());
130 
131  return *addr_(_idx);
132  }
133 
134  template<class E>
135  typename Collection<E>::const_reference
136  Collection<E>::at(UInt_t _idx) const
137  {
138  if (_idx >= CollectionBase::size())
139  throw std::out_of_range((ContainerBase::name_ + "::at").Data());
140 
141  return *const_addr_(_idx);
142  }
143 
144  template<class E>
145  void
147  {
148  if (_src.ContainerBase::unitSize_ != ContainerBase::unitSize_)
149  throw std::runtime_error((ContainerBase::name_ + "::copy incompatible array").Data());
150 
151  CollectionBase::resize(_src.size());
152 
153  for (UInt_t iP(0); iP != CollectionBase::size(); ++iP)
154  (*this)[iP] = _src[iP];
155 
156  ContainerBase::setName(_src.name_);
157  }
158 
159  template<class E>
160  void
161  Collection<E>::push_back(const_reference _elem)
162  {
163  CollectionBase::resize(CollectionBase::size() + 1);
164  back() = _elem;
165  }
166 
167  template<class E>
168  typename panda::Collection<E>::reference
170  {
171  CollectionBase::resize(CollectionBase::size() + 1);
172  auto& e(back());
173  e.init();
174  return e;
175  }
176 
177  template<class E>
178  std::vector<UInt_t>
179  Collection<E>::sort(ContainerBase::Comparison const& _c)
180  {
181  // define an index comparison using the element comparison
182  auto indexComp([this, &_c](UInt_t i1, UInt_t i2)->Bool_t {
183  return _c((*this)[i1], (*this)[i2]);
184  });
185 
186  std::vector<UInt_t> sortedIndices(CollectionBase::size());
187  for (UInt_t iP(0); iP != CollectionBase::size(); ++iP)
188  sortedIndices[iP] = iP;
189 
190  std::sort(sortedIndices.begin(), sortedIndices.end(), indexComp);
191 
192  self_type tmpCollection(*this);
193  for (UInt_t iP(0); iP != CollectionBase::size(); ++iP)
194  (*this)[iP] = tmpCollection[sortedIndices[iP]];
195 
196  return sortedIndices;
197  }
198 
199  template<class E>
200  void
201  Collection<E>::print(std::ostream& _out/* = std::cout*/, UInt_t _level/* = 1*/) const
202  {
203  _out << E::typeName() << "Collection" << std::endl;
204  CollectionBase::print(_out, _level);
205  }
206 
207  template<class E>
208  void
209  Collection<E>::dump(std::ostream& _out/* = std::cout*/) const
210  {
211  _out << E::typeName() << "Collection" << std::endl;
212  CollectionBase::dump(_out);
213  }
214 
215  /*protected*/
216  template<class E>
217  void
218  Collection<E>::reallocate_(UInt_t _nmax)
219  {
220  if (_nmax <= data.nmax())
221  return;
222 
223  // keep the copy of the pointers temporarily
224  // tmpStore is itself a bunch of pointers (no values are copied here)
225  data_type tmpStore(data);
226  value_type* tmpArray(reinterpret_cast<value_type*>(ContainerBase::array_));
227 
228  // allocate a new chunk of memory in heap
229  allocate_(_nmax);
230 
231  // copy old values
232  // CollectionBase::size() still has the old value
233  for (UInt_t iP(0); iP != CollectionBase::size(); ++iP)
234  (*this)[iP] = tmpArray[iP];
235 
236  // deallocate old space
237  deallocate_(tmpArray, tmpStore);
238  }
239 
240  /*private*/
241  template<class E>
242  typename Collection<E>::value_type*
243  Collection<E>::addr_(unsigned _idx/* = 0*/)
244  {
245  // Here we may be calling from a base class (when E0 <- E1, (Collection<E0>*)(&t1_array)->addr_(i) should properly point to the second *E1*, not E0),
246  // which means we must shift by _idx * unitSize_ instead of _idx * sizeof(value_type).
247 
248  return reinterpret_cast<value_type*>(ContainerBase::array_ + _idx * ContainerBase::unitSize_);
249  }
250 
251  /*private*/
252  template<class E>
253  typename Collection<E>::value_type const*
254  Collection<E>::const_addr_(unsigned _idx/* = 0*/) const
255  {
256  // Here we may be calling from a base class (when E0 <- E1, (Collection<E0>*)(&t1_array)->addr_(i) should properly point to the second *E1*, not E0),
257  // which means we must shift by _idx * unitSize_ instead of _idx * sizeof(value_type).
258 
259  return reinterpret_cast<value_type const*>(ContainerBase::array_ + _idx * ContainerBase::unitSize_);
260  }
261 
262  /*private*/
263  template<class E>
264  template<class T/* = E*/>
265  typename std::enable_if<std::is_constructible<T>::value>::type
266  Collection<E>::allocate_(UInt_t _nmax)
267  {
268  data.allocate(_nmax);
269 
270  ContainerBase::array_ = reinterpret_cast<Char_t*>(std::allocator<value_type>().allocate(data.nmax()));
271 
272  value_type* p(addr_());
273  for (UInt_t iP(0); iP != data.nmax(); ++iP, ++p)
274  new (p) value_type(data, iP);
275  }
276 
277  /*private*/
278  template<class E>
279  template<class T/* = E*/>
280  typename std::enable_if<!std::is_constructible<T>::value>::type
282  {
283  throw std::runtime_error(("Cannot create a Collection of pure-virtual elements (" + ContainerBase::name_ + ")").Data());
284  }
285 
286  /*private*/
287  template<class E>
288  template<class T/* = E*/>
289  typename std::enable_if<std::is_constructible<T>::value>::type
290  Collection<E>::deallocate_(value_type* _addr, data_type& _data)
291  {
292  // Call the destructor of the array elements
293  value_type* p(_addr);
294  for (UInt_t iP(0); iP != _data.nmax(); ++iP, ++p)
295  p->destructor(true);
296 
297  std::allocator<value_type>().deallocate(_addr, _data.nmax());
298  _data.deallocate();
299  }
300 
301  /*private*/
302  template<class E>
303  template<class T/* = E*/>
304  typename std::enable_if<!std::is_constructible<T>::value>::type
306  {
307  throw std::runtime_error(("Cannot deallocate pure-virtual elements (" + ContainerBase::name_ + ")").Data());
308  }
309 
310 }
311 
312 #endif
reference create_back()
Create an element at the end of the collection and return a reference.
Definition: Collection.h:169
reference operator[](UInt_t idx)
Element accessor with no range check.
Definition: Collection.h:42
const_iterator begin() const
Return an iterator pointing to the first element.
Definition: Collection.h:48
reference at(UInt_t idx)
Element accessor with range check.
Definition: Collection.h:126
iterator begin()
Return an iterator pointing to the first element.
Definition: Collection.h:46
reference front()
Reference to the first element.
Definition: Collection.h:54
void push_back(const_reference)
Append an element to the back and resize by 1.
Definition: Collection.h:161
Definition: Proton.h:12
Definition: Element.h:45
void setName(char const *name) final
Set object name.
Definition: ContainerBase.h:26
void dump(std::ostream &=std::cout) const override
Dump the object content.
Definition: CollectionBase.cc:171
const_reference back() const
Reference to the last element.
Definition: Collection.h:60
const_reference front() const
Reference to the first element.
Definition: Collection.h:56
void copy(self_type const &)
Copy the array contents.
Definition: Collection.h:146
Template class for dynamic-size container implementations. Inherits from base_type::collection_type o...
Definition: Collection.h:19
const_reference operator[](UInt_t idx) const
Element accessor with no range check.
Definition: Collection.h:44
reference back()
Reference to the last element.
Definition: Collection.h:58
iterator end()
Return an iterator pointing to the end of the array (invalid address)
Definition: Collection.h:50
void print(std::ostream &=std::cout, UInt_t level=1) const override
Print the object content.
Definition: CollectionBase.cc:164
Base class for elements of containers.
Definition: Element.h:32
Iterator class for containers.
Definition: Iterator.h:11
Definition: Array.h:11
void resize(UInt_t size)
Resize the container.
Definition: CollectionBase.cc:192
const_iterator end() const
Return an iterator pointing to the end of the array (invalid address)
Definition: Collection.h:52