TPIE

11a2c2d
array.h
Go to the documentation of this file.
1 // -*- mode: c++; tab-width: 4; indent-tabs-mode: t; eval: (progn (c-set-style "stroustrup") (c-set-offset 'innamespace 0)); -*-
2 // vi:set ts=4 sts=4 sw=4 noet :
3 // Copyright 2010, 2012, The TPIE development team
4 //
5 // This file is part of TPIE.
6 //
7 // TPIE is free software: you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License as published by the
9 // Free Software Foundation, either version 3 of the License, or (at your
10 // option) any later version.
11 //
12 // TPIE is distributed in the hope that it will be useful, but WITHOUT ANY
13 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 // License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public License
18 // along with TPIE. If not, see <http://www.gnu.org/licenses/>
19 #ifndef __TPIE_ARRAY_H__
20 #define __TPIE_ARRAY_H__
21 
26 #include <tpie/util.h>
27 #include <boost/iterator/iterator_facade.hpp>
28 #include <tpie/memory.h>
29 #include <tpie/array_view_base.h>
30 #include <type_traits>
31 #include <cassert>
32 #include <ostream>
33 
34 namespace tpie {
35 
40 template <typename TT, bool forward>
41 class array_iter_base: public boost::iterator_facade<
42  array_iter_base<TT, forward>,
43  TT , boost::random_access_traversal_tag> {
44 private:
45  template <typename, typename> friend class array;
46  friend class boost::iterator_core_access;
47  template <typename, bool> friend class array_iter_base;
48 
49  explicit array_iter_base(TT * e): elm(e) {}
50 
51  TT & dereference() const {return * elm;}
52  template <class U>
53  bool equal(array_iter_base<U, forward> const& o) const {return elm == o.elm;}
54  void increment() {elm += forward?1:-1;}
55  void decrement() {elm += forward?-1:1;}
56  void advance(size_t n) {if (forward) elm += n; else elm -= n;}
57  ptrdiff_t distance_to(array_iter_base const & o) const {return (forward?1:-1) * (o.elm - elm);}
58  TT * elm;
59 public:
63  array_iter_base(): elm(0) {};
64 
65 
66  TT & operator[](ptrdiff_t n) const noexcept {
67  return *(forward?elm+n:elm-n);
68  }
69 
75  template <class U>
77  typename std::enable_if<
78  std::is_convertible<U*,TT*>::value>::type* = 0)
79  : elm(o.elm) {}
80 };
81 
82 #pragma pack(push, 1)
83 template <typename C>
85  char c[sizeof(C)];
86 };
87 #pragma pack(pop)
88 
89 namespace bits {
90  template <typename T, typename Allocator> struct allocator_usage;
91 } // namespace bits
92 
102 // ASIDE about the C++ language: A type is said to be trivially constructible
103 // if it has no user-defined constructors and all its members are trivially
104 // constructible. `new T` will allocate memory for a T-element, and if T is
105 // trivially constructible, the memory will be left uninitialized. This
106 // goes for arrays (T[]) as well, meaning an array initialization of a
107 // trivially constructible type takes practically no time.
108 //
109 // IMPLEMENTATION NOTE. We have three cases for how we want the item buffer
110 // `array::m_elements` to be initialized:
111 //
112 // 1. Default constructed. tpie_new_array<T> does this
113 // allocation+initialization.
114 //
115 // 2. Copy constructed from a single element. Cannot be done with
116 // tpie_new_array<T>.
117 //
118 // 3. Copy constructed from elements in another array. Cannot be done with
119 // tpie_new_array<T> either.
120 //
121 // For cases 2 and 3, we must keep `new`-allocation separate from item
122 // initialization. Thus, for these cases we instead allocate an array of
123 // trivial_same_size<T>. This is a struct that has the same size as T, but is
124 // trivially constructible, meaning no memory initialization is done.
125 //
126 // Then, for case 2, we use std::uninitialized_fill, and for case 3 we use
127 // std::uninitialized_copy.
128 //
129 // Unfortunately, although we have the choice in case 1 of allocating a
130 // trivial_same_size<T> and then calling the default constructors afterwards,
131 // this turns out in some cases to be around 10% slower than allocating a
132 // T-array directly.
133 //
134 // Also, in order to have the same initialization semantics with regards to
135 // trivially constructible types as C++ new[], we need to check if the type is
136 // trivially constructible (using SFINAE/boost::enable_if/boost::type_traits)
137 // to avoid zero-initializing those (a speed penalty we cannot afford).
138 //
139 // Now it is clear that we must sometimes use tpie_new_array<T>, other times
140 // tpie_new_array<trivial_same_size<T> >. The TPIE memory manager checks that
141 // buffers allocated as one type are not deallocated as another type, so when
142 // the buffer is allocated as a trivial_same_size, we must remember this fact
143 // for later destruction and deallocation.
144 //
145 // We remember this fact in array::m_tss_used.
147 
148 template <typename T, typename Allocator = allocator<T> >
149 class array : public linear_memory_base<array<T> > {
150 public:
153 
156 
159 
162 
164  typedef T value_type;
165 
172  iterator find(size_t idx) throw () {
173  assert(idx <= size());
174  return get_iter(idx);
175  }
176 
183  const_iterator find(size_t idx) const throw () {
184  assert(idx <= size());
185  return get_iter(idx);
186  }
187 
193  T & at(size_t i) throw() {
194  assert(i < size());
195  return m_elements[i];
196  }
197 
201  const T & at(size_t i) const throw() {
202  assert(i < size());
203  return m_elements[i];
204  }
205 
214  array & operator=(const array & other) {
215  resize(other.size());
216  for (size_t i=0; i < size(); ++i) m_elements[i] = other[i];
217  return *this;
218  }
219 
228  array & operator=(array && other) {
229  resize(0);
230  std::swap(m_allocator, other.m_allocator);
231  std::swap(m_elements, other.m_elements);
232  std::swap(m_size, other.m_size);
233  std::swap(m_tss_used, other.m_tss_used);
234  return *this;
235  }
236 
246  template <typename OtherAllocator>
248  resize(other.size());
249  for (size_t i=0; i < size(); ++i) m_elements[i] = other[i];
250  return *this;
251  }
252 
258  bool empty() const {return size() == 0;}
259 
266  const T & operator[](size_t i) const {
267  assert(i < size());
268  return at(i);
269  }
270 
277  T & operator[](size_t i) {
278  assert(i < size());
279  return at(i);
280  }
281 
289  bool operator==(const array & other) const {
290  if (size() != other.size()) return false;
291  for (size_t i=0;i<size();++i) if (*get_iter(i) != *other.get_iter(i)) return false;
292  return true;
293  }
294 
301  bool operator!=(const array & other) const {
302  if (size() != other.size()) return true;
303  for (size_t i=0; i<size(); ++i) if (*get_iter(i) != *other.get_iter(i)) return true;
304  return false;
305  }
306 
312  iterator begin() {return get_iter(0);}
313 
319  const_iterator begin() const {return get_iter(0);}
320 
326  iterator end() {return get_iter(size());}
327 
333  const_iterator end() const {return get_iter(size());}
334 
338  const T & front() const {return at(0);}
339 
343  T & front() {return at(0);}
344 
348  const T & back() const {return at(size()-1);}
349 
353  T & back() {return at(size()-1);}
354 
358  reverse_iterator rbegin() {return get_rev_iter(0);}
359 
363  const_reverse_iterator rbegin() const {return get_rev_iter(0);}
364 
368  reverse_iterator rend() {return get_rev_iter(size());}
369 
373  const_reverse_iterator rend() const {return get_rev_iter(size());}
374 
375 private:
376  T * m_elements;
377  size_t m_size;
378 
379  iterator get_iter(size_t idx) {
380  return iterator(m_elements+idx);
381  }
382 
383  const_iterator get_iter(size_t idx) const {
384  return const_iterator(m_elements+idx);
385  }
386 
387  reverse_iterator get_rev_iter(size_t idx) {
388  return reverse_iterator(m_elements+m_size-idx-1);
389  }
390 
391  const_reverse_iterator get_rev_iter(size_t idx) const {
392  return const_reverse_iterator(m_elements+m_size-idx-1);
393  }
394 public:
398  static constexpr double memory_coefficient() noexcept {
399  return (double)sizeof(T);
400  }
401 
405  static constexpr double memory_overhead() noexcept {return sizeof(array);}
406 
413  array(size_type s, const T & value,
414  const Allocator & alloc=Allocator()
415  ): m_elements(0), m_size(0), m_tss_used(false), m_allocator(alloc)
416  {resize(s, value);}
417 
418  array(size_type s, memory_bucket_ref bucket):
419  m_elements(0), m_size(0), m_tss_used(false), m_allocator(bucket)
420  {resize(s);}
421 
422  array(memory_bucket_ref bucket):
423  m_elements(0), m_size(0), m_tss_used(false), m_allocator(bucket) {}
424 
430  array(size_type s=0, const Allocator & alloc=
431  Allocator()): m_elements(0), m_size(0), m_tss_used(false),
432  m_allocator(alloc) {resize(s);}
433 
438  array(const array & other): m_elements(0), m_size(other.m_size), m_tss_used(false), m_allocator(other.m_allocator) {
439  if (other.size() == 0) return;
440  alloc_copy(other.m_elements);
441  }
442 
447  array(array && other)
448  : m_elements(other.m_elements)
449  , m_size(other.m_size)
450  , m_tss_used(other.m_tss_used)
451  , m_allocator(other.m_allocator) {
452  other.m_elements = nullptr;
453  other.m_size = 0;
454  other.m_tss_used = false;
455  }
456 
457  array(const array_view_base<T> & view)
458  : m_elements(0)
459  , m_size(view.size())
460  , m_tss_used(false)
461  {
462  if (view.size() == 0) return;
463  alloc_copy(&*view.begin());
464  }
465 
466  array(const array_view_base<const T> & view)
467  : m_elements(0)
468  , m_size(view.size())
469  , m_tss_used(false)
470  {
471  if (view.size() == 0) return;
472  alloc_copy(&*view.begin());
473  }
474 
478  ~array() {resize(0);}
479 
490  void resize(size_t size, const T & elm) {
491  if (size != m_size) {
492  destruct_and_dealloc();
493  m_size = size;
494 
495  alloc_fill(elm);
496  } else {
497  std::fill(m_elements+0, m_elements+m_size, elm);
498  }
499  }
500 
504  void swap(array & other) {
505  std::swap(m_allocator, other.m_allocator);
506  std::swap(m_elements, other.m_elements);
507  std::swap(m_size, other.m_size);
508  std::swap(m_tss_used, other.m_tss_used);
509  }
510 
520  void resize(size_t s) {
521  destruct_and_dealloc();
522  m_size = s;
523  alloc_dfl();
524  }
525 
531  size_type size() const {return m_size;}
532 
536  T * get() {return m_elements;}
537 
541  const T * get() const {return m_elements;}
542 
546  Allocator get_allocator() const {return m_allocator;}
547 private:
548  friend struct bits::allocator_usage<T, Allocator>;
549 
557  void alloc_copy(const T * copy_from) { bits::allocator_usage<T, Allocator>::alloc_copy(*this, copy_from); }
558 
566  void alloc_fill(const T & elm) { bits::allocator_usage<T, Allocator>::alloc_fill(*this, elm); }
567 
573  void alloc_dfl() { bits::allocator_usage<T, Allocator>::alloc_dfl(*this); }
574 
580  void destruct_and_dealloc() { bits::allocator_usage<T, Allocator>::destruct_and_dealloc(*this); }
581 
584  bool m_tss_used;
585 
586  Allocator m_allocator;
587 };
588 
589 namespace bits {
590 
591 template <typename T>
592 struct allocator_usage<T, allocator<T> > {
593  static void alloc_copy(array<T, allocator<T> > & host, const T * copy_from) {
594  host.m_elements = host.m_size ? reinterpret_cast<T*>(tpie_new_array<trivial_same_size<T> >(host.m_size)) : 0;
595  host.m_tss_used = true;
596 
597  if (host.m_allocator.bucket)
598  host.m_allocator.bucket->count += sizeof(T) * host.m_size;
599 
600  std::uninitialized_copy(copy_from+0, copy_from+host.m_size, host.m_elements+0);
601  }
602 
603  static void alloc_fill(array<T, allocator<T> > & host, const T & elm) {
604  host.m_elements = host.m_size ? reinterpret_cast<T*>(tpie_new_array<trivial_same_size<T> >(host.m_size)) : 0;
605  host.m_tss_used = true;
606 
607  if (host.m_allocator.bucket)
608  host.m_allocator.bucket->count += sizeof(T) * host.m_size;
609 
610  // call copy constructors manually
611  std::uninitialized_fill(host.m_elements+0, host.m_elements+host.m_size, elm);
612  }
613 
614  static void alloc_dfl(array<T, allocator<T> > & host) {
615  host.m_elements = host.m_size ? tpie_new_array<T>(host.m_size) : 0;
616  host.m_tss_used = false;
617 
618  if (host.m_allocator.bucket)
619  host.m_allocator.bucket->count += sizeof(T) * host.m_size;
620  }
621 
622  static void destruct_and_dealloc(array<T, allocator<T> > & host) {
623  if (host.m_allocator.bucket)
624  host.m_allocator.bucket->count -= sizeof(T) * host.m_size;
625 
626  if (!host.m_tss_used) {
627  // calls destructors
628  tpie_delete_array(host.m_elements, host.m_size);
629  return;
630  }
631 
632  // call destructors manually
633  for (size_t i = 0; i < host.m_size; ++i) {
634  host.m_elements[i].~T();
635  }
636  tpie_delete_array(reinterpret_cast<trivial_same_size<T>*>(host.m_elements), host.m_size);
637  }
638 };
639 
640 template <typename T, typename Allocator>
641 struct allocator_usage {
642  static void alloc_copy(array<T, Allocator> & host, const T * copy_from) {
643  host.m_elements = host.m_size ? host.m_allocator.allocate(host.m_size) : 0;
644  for (size_t i = 0; i < host.m_size; ++i) {
645  host.m_allocator.construct(host.m_elements+i, copy_from[i]);
646  }
647  }
648 
649  static void alloc_fill(array<T, Allocator> & host, const T & elm) {
650  host.m_elements = host.m_size ? host.m_allocator.allocate(host.m_size) : 0;
651  for (size_t i = 0; i < host.m_size; ++i) {
652  host.m_allocator.construct(host.m_elements+i, elm);
653  }
654  }
655 
656  static void alloc_dfl(array<T, Allocator> & host) {
657  host.m_elements = host.m_size ? host.m_allocator.allocate(host.m_size) : 0;
658  for (size_t i = 0; i < host.m_size; ++i) {
659  host.m_allocator.construct(host.m_elements+i);
660  }
661  }
662 
663  static void destruct_and_dealloc(array<T, Allocator> & host) {
664  for (size_t i = 0; i < host.m_size; ++i) {
665  host.m_allocator.destroy(host.m_elements+i);
666  }
667  host.m_allocator.deallocate(host.m_elements, host.m_size);
668  }
669 };
670 
671 } // namespace bits
672 
673 template <typename T>
674 std::ostream & operator<<(std::ostream & o, const array<T> & a) {
675  o << "[";
676  bool first=true;
677  for(size_t i=0; i < a.size(); ++i) {
678  if (first) first = false;
679  else o << ", ";
680  o << a[i];
681  }
682  return o << "]";
683 }
684 
685 } // namespace tpie
686 
687 namespace std {
688 
707 
708 template <typename T>
710  a.swap(b);
711 }
712 
716 template <typename TT1, bool forward1, typename TT2, bool forward2>
721 
722  ptrdiff_t dist = copy(&*first, &*last, &*d_first) - &*d_first;
723  return d_first + dist;
724 }
725 
729 template <typename TT, bool forward, typename OutputIterator>
730 OutputIterator
733  OutputIterator d_first) {
734 
735  return copy(&*first, &*last, d_first);
736 }
737 
741 template <typename TT, bool forward, typename InputIterator>
743 copy(InputIterator first,
744  InputIterator last,
746 
747  ptrdiff_t dist = copy(first, last, &*d_first) - &*d_first;
748  return d_first + dist;
749 }
750 
751 } // namespace std
752 
753 #endif //__TPIE_ARRAY_H__
tpie::array::rend
const_reverse_iterator rend() const
Const reverse iterator to end of reverse sequence.
Definition: array.h:373
tpie::array::array
array(array &&other)
Move construct from another array.
Definition: array.h:447
tpie::trivial_same_size
Definition: array.h:84
array_view_base.h
tpie::bits::allocator_usage
Definition: array.h:90
tpie::linear_memory_base
Base class of data structures with linear memory usage.
Definition: util.h:98
tpie::array::const_reverse_iterator
array_iter_base< T const, false > const_reverse_iterator
Reverse iterator over a const array.
Definition: array.h:155
tpie::array::operator[]
T & operator[](size_t i)
Return a reference to an array entry.
Definition: array.h:277
tpie::array::reverse_iterator
array_iter_base< T, false > reverse_iterator
Reverse iterator over an array.
Definition: array.h:161
tpie::array::operator[]
const T & operator[](size_t i) const
Return a const reference to an array entry.
Definition: array.h:266
tpie::array_view_base::begin
iterator begin() const
Return an iterator to the beginning of the array.
Definition: array_view_base.h:151
tpie::array::front
T & front()
Return the first element in the array.
Definition: array.h:343
tpie::array::rbegin
reverse_iterator rbegin()
Reverse iterator to beginning of reverse sequence.
Definition: array.h:358
tpie::array_iter_base
Shared implementation of array iterators.
Definition: array.h:41
tpie::array::at
const T & at(size_t i) const
Return the element located at the given index.
Definition: array.h:201
tpie::array
A generic array with a fixed size.
Definition: array.h:149
tpie::array::begin
iterator begin()
Return an iterator to the beginning of the array.
Definition: array.h:312
tpie::array::memory_overhead
static constexpr double memory_overhead() noexcept
Return the memory overhead of the structure.
Definition: array.h:405
tpie::array::operator=
array & operator=(const array &other)
Copy elements from another array into this.
Definition: array.h:214
tpie::array::end
const_iterator end() const
Return a const iterator to the end of the array.
Definition: array.h:333
tpie::array::operator!=
bool operator!=(const array &other) const
Check if two arrays differ.
Definition: array.h:301
tpie::array::find
iterator find(size_t idx)
Return an iterator to the i'th element of the array.
Definition: array.h:172
tpie::memory_bucket_ref
Class storring a reference to a memory bucket.
Definition: memory.h:334
tpie::array_iter_base::array_iter_base
array_iter_base()
Default constructor.
Definition: array.h:63
tpie::array::front
const T & front() const
Return the first element in the array.
Definition: array.h:338
tpie::array::rend
reverse_iterator rend()
Reverse iterator to end of reverse sequence.
Definition: array.h:368
std::copy
tpie::array_iter_base< TT2, forward2 > copy(tpie::array_iter_base< TT1, forward1 > first, tpie::array_iter_base< TT1, forward1 > last, tpie::array_iter_base< TT2, forward2 > d_first)
std::copy template specialization for tpie::array.
Definition: array.h:718
tpie::array_view_base::size
size_t size() const
Get number of elements in the array.
Definition: array_view_base.h:108
tpie::array::array
array(size_type s, const T &value, const Allocator &alloc=Allocator())
Construct array of given size.
Definition: array.h:413
tpie::array::at
T & at(size_t i)
Return the element located at the given index.
Definition: array.h:193
tpie::array::resize
void resize(size_t s)
Change the size of the array.
Definition: array.h:520
tpie::array::array
array(size_type s=0, const Allocator &alloc=Allocator())
Construct array of given size.
Definition: array.h:430
tpie::array::get
T * get()
Return a raw pointer to the array content.
Definition: array.h:536
tpie::array::rbegin
const_reverse_iterator rbegin() const
Const reverse iterator to beginning of reverse sequence.
Definition: array.h:363
tpie::array::value_type
T value_type
Type of values containd in the array.
Definition: array.h:164
tpie::array::operator=
array & operator=(const array< T, OtherAllocator > &other)
Copy elements from another array with any allocator into this.
Definition: array.h:247
std::swap
void swap(tpie::array< T > &a, tpie::array< T > &b)
Enable std::swapping two tpie::arrays.
Definition: array.h:709
tpie::array::end
iterator end()
Return an iterator to the end of the array.
Definition: array.h:326
tpie::array::empty
bool empty() const
Check if the array is empty.
Definition: array.h:258
tpie::array::operator=
array & operator=(array &&other)
Move elements from another array into this.
Definition: array.h:228
tpie::array::get_allocator
Allocator get_allocator() const
Return copy of the allocator.
Definition: array.h:546
tpie::array::swap
void swap(array &other)
Swap two arrays.
Definition: array.h:504
tpie::array::begin
const_iterator begin() const
Return a const iterator to the beginning of the array.
Definition: array.h:319
tpie::array::memory_coefficient
static constexpr double memory_coefficient() noexcept
Return the memory coefficient of the structure.
Definition: array.h:398
tpie::array::array
array(const array &other)
Construct a copy of another array.
Definition: array.h:438
tpie::array::operator==
bool operator==(const array &other) const
Compare if the other array has the same elements in the same order as this.
Definition: array.h:289
tpie::array::get
const T * get() const
Return a raw pointer to the array content.
Definition: array.h:541
tpie::array::back
const T & back() const
Return the last element in the array.
Definition: array.h:348
tpie::array::resize
void resize(size_t size, const T &elm)
Change the size of the array.
Definition: array.h:490
tpie::array::back
T & back()
Return the last element in the array.
Definition: array.h:353
tpie::array_view_base
Base class for array_view.
Definition: array_view_base.h:40
tpie::array::find
const_iterator find(size_t idx) const
Return a const iterator to the i'th element of the array.
Definition: array.h:183
tpie::array::~array
~array()
Free up all memory used by the array.
Definition: array.h:478
tpie::tpie_delete_array
void tpie_delete_array(T *a, size_t size)
Delete an array allocated with tpie_new_array.
Definition: memory.h:288
tpie::array::const_iterator
array_iter_base< T const, true > const_iterator
Iterator over a const array.
Definition: array.h:152
util.h
memory.h
tpie::allocator
A allocator object usable in STL containers, using the TPIE memory manager.
Definition: memory.h:358
tpie
Definition: access_type.h:26
tpie::array::size
size_type size() const
Return the size of the array.
Definition: array.h:531
tpie::array::iterator
array_iter_base< T, true > iterator
Iterator over an array.
Definition: array.h:158
tpie::array_iter_base::array_iter_base
array_iter_base(array_iter_base< U, forward > const &o, typename std::enable_if< std::is_convertible< U *, TT * >::value >::type *=0)
Copy constructor.
Definition: array.h:76