PandaTree
Array.h
1 #ifndef PandaTree_Framework_Array_h
2 #define PandaTree_Framework_Array_h
3 
4 #include "ArrayBase.h"
5 #include "Iterator.h"
6 
7 #include <stdexcept>
8 #include <memory>
9 #include <type_traits>
10 
11 namespace panda {
12 
14 
19  template<class E>
20  class Array : public E::base_type::array_type {
21  public:
22  typedef Array<E> self_type;
23  typedef typename E::base_type::array_type base_type;
24  typedef E value_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  Array(UInt_t size, char const* name = "") : base_type(name, sizeof(E), kFALSE) { allocate_(size); }
33  Array(self_type const& src) : base_type(src.name_, sizeof(E), kFALSE) { allocate_(src.data.nmax()); copy(src); }
34  ~Array();
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_(data.nmax()), ArrayBase::ContainerBase::unitSize_); }
52  const_iterator end() const { return const_iterator(const_addr_(data.nmax()), ContainerBase::unitSize_); }
54  reference front() { return *addr_(); }
56  const_reference front() const { return *const_addr_(); }
58  reference back() { return *addr_(data.nmax() - 1); }
60  const_reference back() const { return *const_addr_(data.nmax() - 1); }
62  void copy(self_type const&);
63 
64  Element::datastore& getData() override { return data; }
65  Element::datastore const& getData() const override { return data; }
66  Element& elemAt(UInt_t idx) override { return at(idx); }
67  Element const& elemAt(UInt_t idx) const override { return at(idx); }
68 
69  std::vector<UInt_t> sort(ContainerBase::Comparison const&) override;
70 
71  void print(std::ostream& = std::cout, UInt_t level = 1) const override;
72  void dump(std::ostream& = std::cout) const override;
73 
74  typename value_type::datastore data{};
75 
76  protected:
77  Array(char const* name, UInt_t unitSize, Bool_t dummy) : base_type(name, unitSize, kFALSE) {}
78 
79  private:
80  value_type* addr_(UInt_t idx = 0);
81  value_type const* const_addr_(UInt_t idx = 0) const;
82 
83  template<class T = E>
84  typename std::enable_if<std::is_constructible<T>::value>::type allocate_(UInt_t);
85  template<class T = E>
86  typename std::enable_if<!std::is_constructible<T>::value>::type allocate_(UInt_t);
87  };
88 
89  template<class E>
91  {
92  if (ContainerBase::array_) {
93  // Call the destructor of the array elements
94  auto* p(addr_());
95  for (UInt_t iP(0); iP != data.nmax(); ++iP, ++p)
96  p->destructor(true);
97 
98  std::allocator<value_type>().deallocate(addr_(), data.nmax());
99  data.deallocate();
100 
101  // set the pointer to null so that the parent class destructors don't deallocate again
102  ContainerBase::array_ = 0;
103  }
104  }
105 
106  template<class E>
107  typename Array<E>::reference
108  Array<E>::at(UInt_t _idx)
109  {
110  if (_idx >= data.nmax())
111  throw std::out_of_range((ContainerBase::name_ + "::at").Data());
112 
113  return *addr_(_idx);
114  }
115 
116  template<class E>
117  typename Array<E>::const_reference
118  Array<E>::at(UInt_t _idx) const
119  {
120  if (_idx >= data.nmax())
121  throw std::out_of_range((ContainerBase::name_ + "::at").Data());
122 
123  return *const_addr_(_idx);
124  }
125 
126  template<class E>
127  void
129  {
130  if (_src.ContainerBase::unitSize_ != ContainerBase::unitSize_)
131  throw std::runtime_error((ContainerBase::name_ + "::copy incompatible array").Data());
132 
133  if (_src.data.nmax() != data.nmax())
134  throw std::runtime_error((ContainerBase::name_ + "::copy array size does not match").Data());
135 
136  for (UInt_t iP(0); iP != data.nmax(); ++iP)
137  (*this)[iP] = _src[iP];
138 
139  ContainerBase::setName(_src.name_);
140  }
141 
142  template<class E>
143  std::vector<UInt_t>
144  Array<E>::sort(ContainerBase::Comparison const& _c)
145  {
146  // define an index comparison using the element comparison
147  auto indexComp([this, &_c](UInt_t i1, UInt_t i2)->Bool_t {
148  return _c((*this)[i1], (*this)[i2]);
149  });
150 
151  std::vector<UInt_t> sortedIndices(data.nmax());
152  for (UInt_t iP(0); iP != data.nmax(); ++iP)
153  sortedIndices[iP] = iP;
154 
155  std::sort(sortedIndices.begin(), sortedIndices.end(), indexComp);
156 
157  self_type tmpCollection(*this);
158  for (UInt_t iP(0); iP != data.nmax(); ++iP)
159  (*this)[iP] = tmpCollection[sortedIndices[iP]];
160 
161  return sortedIndices;
162  }
163 
164  template<class E>
165  void
166  Array<E>::print(std::ostream& _out/* = std::cout*/, UInt_t _level/* = 1*/) const
167  {
168  _out << E::typeName() << "Array(" << ArrayBase::size() << ")" << std::endl;
169  ContainerBase::print(_out, _level);
170  }
171 
172  template<class E>
173  void
174  Array<E>::dump(std::ostream& _out/* = std::cout*/) const
175  {
176  _out << E::typeName() << "Array(" << ArrayBase::size() << ")" << std::endl;
177  ContainerBase::dump(_out);
178  }
179 
180  /*private*/
181  template<class E>
182  typename Array<E>::value_type*
183  Array<E>::addr_(unsigned _idx/* = 0*/)
184  {
185  // Here we may be calling from a base class (when E0 <- E1, (Array<E0>*)(&t1_array)->addr_(i) should properly point to the second *E1*, not E0),
186  // which means we must shift by _idx * unitSize_ instead of _idx * sizeof(value_type).
187 
188  return reinterpret_cast<value_type*>(ContainerBase::array_ + _idx * ContainerBase::unitSize_);
189  }
190 
191  /*private*/
192  template<class E>
193  typename Array<E>::value_type const*
194  Array<E>::const_addr_(unsigned _idx/* = 0*/) const
195  {
196  // Here we may be calling from a base class (when E0 <- E1, (Array<E0>*)(&t1_array)->addr_(i) should properly point to the second *E1*, not E0),
197  // which means we must shift by _idx * unitSize_ instead of _idx * sizeof(value_type).
198 
199  return reinterpret_cast<value_type const*>(ContainerBase::array_ + _idx * ContainerBase::unitSize_);
200  }
201 
202  /*private*/
203  template<class E>
204  template<class T/* = E*/>
205  typename std::enable_if<std::is_constructible<T>::value>::type
206  Array<E>::allocate_(UInt_t _nmax)
207  {
208  data.allocate(_nmax);
209 
210  ContainerBase::array_ = reinterpret_cast<Char_t*>(std::allocator<value_type>().allocate(data.nmax()));
211 
212  value_type* p(addr_());
213  for (UInt_t iP(0); iP != data.nmax(); ++iP, ++p)
214  new (p) value_type(data, iP);
215  }
216 
217  /*private*/
218  template<class E>
219  template<class T/* = E*/>
220  typename std::enable_if<!std::is_constructible<T>::value>::type
221  Array<E>::allocate_(UInt_t)
222  {
223  throw std::runtime_error(("Cannot create an Array of pure-virtual elements (" + ContainerBase::name_ + ")").Data());
224  }
225 
226 }
227 
228 #endif
const_reference back() const
Reference to the last element.
Definition: Array.h:60
void dump(std::ostream &=std::cout) const override
Dump the object content.
Definition: ContainerBase.cc:23
void print(std::ostream &=std::cout, UInt_t level=1) const override
Print the object content.
Definition: ContainerBase.cc:7
const_reference front() const
Reference to the first element.
Definition: Array.h:56
const_iterator begin() const
Return an iterator pointing to the first element.
Definition: Array.h:48
iterator end()
Return an iterator pointing to the end of the array (invalid address)
Definition: Array.h:50
Template class for fixed-size container implementations. Inherits from base_type::array_type of the e...
Definition: Array.h:20
Definition: Element.h:45
void copy(self_type const &)
Copy the array contents.
Definition: Array.h:128
void setName(char const *name) final
Set object name.
Definition: ContainerBase.h:26
reference operator[](UInt_t idx)
Element accessor with no range check.
Definition: Array.h:42
const_iterator end() const
Return an iterator pointing to the end of the array (invalid address)
Definition: Array.h:52
const_reference operator[](UInt_t idx) const
Element accessor with no range check.
Definition: Array.h:44
reference back()
Reference to the last element.
Definition: Array.h:58
Base class for elements of containers.
Definition: Element.h:32
reference front()
Reference to the first element.
Definition: Array.h:54
Iterator class for containers.
Definition: Iterator.h:11
reference at(UInt_t idx)
Element accessor with range check.
Definition: Array.h:108
Definition: Array.h:11
iterator begin()
Return an iterator pointing to the first element.
Definition: Array.h:46