lean cpp library
A lean C++ library providing efficient utility classes for high-performance C++ applications.
simple_vector.h
00001 /*****************************************************/
00002 /* lean Containers              (c) Tobias Zirr 2011 */
00003 /*****************************************************/
00004 
00005 #ifndef LEAN_CONTAINERS_SIMPLE_VECTOR
00006 #define LEAN_CONTAINERS_SIMPLE_VECTOR
00007 
00008 #include "../lean.h"
00009 #include "../meta/type_traits.h"
00010 #include <memory>
00011 #include <stdexcept>
00012 
00013 namespace lean 
00014 {
00015 namespace containers
00016 {
00017 
00019 namespace simple_vector_policies
00020 {
00022     template <bool RawMove = false, bool NoDestruct = false, bool NoConstruct = false>
00023     struct policy
00024     {
00026         static const bool raw_move = RawMove;
00028         static const bool no_destruct = NoDestruct;
00030         static const bool no_construct = NoConstruct;
00031     };
00032 
00034     typedef policy<> nonpod;
00036     typedef policy<true> semipod;
00038     typedef policy<true, true> inipod;
00040     typedef policy<true, true, true> pod;
00041 }
00042 
00044 template < class Element, class Policy = simple_vector_policies::nonpod, class Allocator = std::allocator<Element> >
00045 class simple_vector
00046 {
00047 private:
00048     typedef typename Allocator::template rebind<Element>::other allocator_type_;
00049     allocator_type_ m_allocator;
00050 
00051     Element *m_elements;
00052     Element *m_elementsEnd;
00053     Element *m_capacityEnd;
00054 
00055     typedef typename allocator_type_::size_type size_type_;
00056     static const size_type_ s_maxSize = static_cast<size_type_>(-1); // WORKAROUND: premature evaluation / sizeof(Element);
00057     static const size_type_ s_minSize = (16 < s_maxSize) ? 16 : s_maxSize;
00058 
00059     // Make sure size_type is unsigned
00060     LEAN_STATIC_ASSERT(is_unsigned<size_type_>::value);
00061 
00063     LEAN_INLINE void default_construct(Element *dest)
00064     {
00065         if (!Policy::no_construct)
00066             m_allocator.construct(dest, Element());
00067     }
00069     void default_construct(Element *dest, Element *destEnd)
00070     {
00071         if (!Policy::no_construct)
00072         {
00073             Element *destr = dest;
00074 
00075             try
00076             {
00077                 for (; dest != destEnd; ++dest)
00078                     default_construct(dest);
00079             }
00080             catch(...)
00081             {
00082                 destruct(destr, dest);
00083                 throw;
00084             }
00085         }
00086     }
00088     LEAN_INLINE void copy_construct(Element *dest, const Element &source)
00089     {
00090         m_allocator.construct(dest, source);
00091     }
00093     template <class Iterator>
00094     void copy_construct(Iterator source, Iterator sourceEnd, Element *dest)
00095     {
00096         Element *destr = dest;
00097 
00098         try
00099         {
00100             for (; source != sourceEnd; ++dest, ++source)
00101                 copy_construct(dest, *source);
00102         }
00103         catch(...)
00104         {
00105             destruct(destr, dest);
00106             throw;
00107         }
00108     }
00110     LEAN_INLINE void move_construct(Element *dest, Element &source)
00111     {
00112 #ifndef LEAN0X_NO_RVALUE_REFERENCES
00113         m_allocator.construct(dest, std::move(source));
00114 #else
00115         copy_construct(dest, source);
00116 #endif
00117     }
00119     void move_construct(Element *source, Element *sourceEnd, Element *dest)
00120     {
00121         Element *destr = dest;
00122 
00123         try
00124         {
00125             for (; source != sourceEnd; ++dest, ++source)
00126                 move_construct(dest, *source);
00127         }
00128         catch(...)
00129         {
00130             destruct(destr, dest);
00131             throw;
00132         }
00133     }
00135     LEAN_INLINE void move(Element *dest, Element &source)
00136     {
00137 #ifndef LEAN0X_NO_RVALUE_REFERENCES
00138         *dest = std::move(source);
00139 #else
00140         *dest = source;
00141 #endif
00142     }
00144     void move(Element *source, Element *sourceEnd, Element *dest)
00145     {
00146         for (; source != sourceEnd; ++dest, ++source)
00147             move(dest, *source);
00148     }
00149 
00151     LEAN_INLINE void destruct(Element *destr)
00152     {
00153         if (!Policy::no_destruct)
00154             m_allocator.destroy(destr);
00155     }
00157     void destruct(Element *destr, Element *destrEnd)
00158     {
00159         if (!Policy::no_destruct)
00160             for (; destr != destrEnd; ++destr)
00161                 destruct(destr);
00162     }
00163 
00165     void reallocate(size_type_ newCapacity)
00166     {
00167         Element *newElements = m_allocator.allocate(newCapacity);
00168 
00169         if (!empty())
00170             try
00171             {
00172                 if (Policy::raw_move)
00173                     memcpy(newElements, m_elements, size() * sizeof(Element));
00174                 else
00175                     move_construct(m_elements, m_elementsEnd, newElements);
00176             }
00177             catch(...)
00178             {
00179                 m_allocator.deallocate(newElements, newCapacity);
00180                 throw;
00181             }
00182 
00183         Element *oldElements = m_elements;
00184         Element *oldElementsEnd = m_elementsEnd;
00185         size_type_ oldCapacity = capacity();
00186         
00187         // Mind the order, size() based on member variables!
00188         m_elementsEnd = newElements + size();
00189         m_capacityEnd = newElements + newCapacity;
00190         m_elements = newElements;
00191 
00192         if (oldElements)
00193         {
00194             // Do nothing on exception, resources leaking anyways!
00195             if (!Policy::raw_move)
00196                 destruct(oldElements, oldElementsEnd);
00197             m_allocator.deallocate(oldElements, oldCapacity);
00198         }
00199     }
00200 
00202     LEAN_INLINE void free()
00203     {
00204         if (m_elements)
00205         {
00206             // Do nothing on exception, resources leaking anyways!
00207             destruct(m_elements, m_elementsEnd);
00208             m_allocator.deallocate(m_elements, capacity());
00209         }
00210     }
00211 
00213     LEAN_INLINE void growTo(size_type_ newCount, bool checkLength = true)
00214     {
00215         // Mind overflow
00216         if (checkLength)
00217             check_length(newCount);
00218 
00219         reallocate(next_capacity_hint(newCount));
00220     }
00222     LEAN_INLINE void grow(size_type_ count)
00223     {
00224         size_type_ oldSize = size();
00225 
00226         // Mind overflow
00227         if (count > s_maxSize || s_maxSize - count < oldSize)
00228             length_exceeded();
00229 
00230         growTo(oldSize + count, false);
00231     }
00233     LEAN_INLINE Element& grow_and_relocate(Element &value)
00234     {
00235         size_type_ index = lean::addressof(value) - m_elements;
00236         grow(1);
00237         
00238         // Index is unsigned, make use of wrap-around
00239         return (index < size())
00240             ? m_elements[index]
00241             : value;
00242     }
00243 
00245     LEAN_NOINLINE void growToHL(size_type_ newCount)
00246     {
00247         growTo(newCount);
00248     }
00250     LEAN_NOINLINE void growHL(size_type_ count)
00251     {
00252         grow(count);
00253     }
00255     LEAN_NOINLINE void grow_and_pushHL(const Element &value)
00256     {
00257         copy_construct(m_elementsEnd, grow_and_relocate(const_cast<Element&>(value)));
00258         ++m_elementsEnd;
00259     }
00261     LEAN_NOINLINE void grow_and_pushHL(Element &&value)
00262     {
00263         move_construct(m_elementsEnd, grow_and_relocate(value));
00264         ++m_elementsEnd;
00265     }
00266 
00268     LEAN_NOINLINE static void out_of_range()
00269     {
00270         throw std::out_of_range("simple_vector<T> out of range");
00271     }
00273     LEAN_INLINE void check_pos(size_type_ pos) const
00274     {
00275         if (pos >= size())
00276             out_of_range();
00277     }
00279     LEAN_NOINLINE static void length_exceeded()
00280     {
00281         throw std::length_error("simple_vector<T> too long");
00282     }
00284     LEAN_INLINE static void check_length(size_type_ count)
00285     {
00286         if (count > s_maxSize)
00287             length_exceeded();
00288     }
00289 
00290 public:
00292     typedef Policy construction_policy;
00293 
00295     typedef allocator_type_ allocator_type;
00297     typedef size_type_ size_type;
00299     typedef typename allocator_type::difference_type difference_type;
00300 
00302     typedef typename allocator_type::pointer pointer;
00304     typedef typename allocator_type::const_pointer const_pointer;
00306     typedef typename allocator_type::reference reference;
00308     typedef typename allocator_type::const_reference const_reference;
00310     typedef typename allocator_type::value_type value_type;
00311 
00313     typedef pointer iterator;
00315     typedef const_pointer const_iterator;
00316 
00318     simple_vector()
00319         : m_elements(nullptr),
00320         m_elementsEnd(nullptr),
00321         m_capacityEnd(nullptr) { }
00323     explicit simple_vector(allocator_type allocator)
00324         : m_allocator(allocator),
00325         m_elements(nullptr),
00326         m_elementsEnd(nullptr),
00327         m_capacityEnd(nullptr) { }
00329     simple_vector(const simple_vector &right)
00330         : m_allocator(right.m_allocator),
00331         m_elements(nullptr),
00332         m_elementsEnd(nullptr),
00333         m_capacityEnd(nullptr)
00334     {
00335         assign_disj(right.begin(), right.end());
00336     }
00337 #ifndef LEAN0X_NO_RVALUE_REFERENCES
00338 
00339     simple_vector(simple_vector &&right)
00340         : m_allocator(std::move(right.m_allocator)),
00341         m_elements(std::move(right.m_elements)),
00342         m_elementsEnd(std::move(right.m_elementsEnd)),
00343         m_capacityEnd(std::move(right.m_capacityEnd))
00344     {
00345         right.m_elements = nullptr;
00346         right.m_elementsEnd = nullptr;
00347         right.m_capacityEnd = nullptr;
00348     }
00349 #endif
00350 
00351     ~simple_vector()
00352     {
00353         free();
00354     }
00355 
00357     simple_vector& operator =(const simple_vector &right)
00358     {
00359         if (&right != this)
00360             assign_disj(right.begin(), right.end());
00361         return *this;
00362     }
00363 #ifndef LEAN0X_NO_RVALUE_REFERENCES
00364 
00365     simple_vector& operator =(simple_vector &&right)
00366     {
00367         if (&right != this)
00368         {
00369             free();
00370 
00371             m_elements = std::move(right.m_elements);
00372             m_elementsEnd = std::move(right.m_elementsEnd);
00373             m_capacityEnd = std::move(right.m_capacityEnd);
00374 
00375             right.m_elements = nullptr;
00376             right.m_elementsEnd = nullptr;
00377             right.m_capacityEnd = nullptr;
00378 
00379             m_allocator = std::move(right.m_allocator);
00380         }
00381         return *this;
00382     }
00383 #endif
00384 
00386     template <class Iterator>
00387     void assign(Iterator source, Iterator sourceEnd)
00388     {
00389         LEAN_ASSERT(source <= sourceEnd);
00390 
00391         Element *sourceElements = const_cast<Element*>( lean::addressof(*source) );
00392 
00393         // Index is unsigned, make use of wrap-around
00394         if (static_cast<size_type>(sourceElements - m_elements) < size())
00395         {
00396             Element *sourceElementsEnd = const_cast<Element*>( lean::addressof(*sourceEnd) );
00397             LEAN_ASSERT(sourceElementsEnd <= m_elementsEnd);
00398 
00399             // Move (always back to front)
00400             LEAN_ASSERT(m_elements <= sourceElements);
00401             move(sourceElements, sourceElementsEnd, m_elements);
00402 
00403             Element *oldElementsEnd = m_elementsEnd;
00404             m_elementsEnd = m_elements + (sourceElementsEnd - sourceElements);
00405             destruct(m_elementsEnd, oldElementsEnd);
00406         }
00407         else
00408             assign_disj(source, sourceEnd);
00409     }
00411     template <class Iterator>
00412     void assign_disj(Iterator source, Iterator sourceEnd)
00413     {
00414         // Clear before reallocation to prevent full-range moves
00415         clear();
00416 
00417         size_type count = sourceEnd - source;
00418 
00419         if (count > capacity())
00420             growToHL(count);
00421 
00422         copy_construct(source, sourceEnd, m_elements);
00423         m_elementsEnd = m_elements + count;
00424     }
00425 
00427     LEAN_INLINE reference push_back()
00428     {
00429         if (m_elementsEnd == m_capacityEnd)
00430             growHL(1);
00431 
00432         default_construct(m_elementsEnd);
00433         return *(m_elementsEnd++);
00434     }
00436     LEAN_INLINE void push_back(const value_type &value)
00437     {
00438         if (m_elementsEnd == m_capacityEnd)
00439             grow_and_pushHL(value);
00440         else
00441         {
00442             copy_construct(m_elementsEnd, value);
00443             ++m_elementsEnd;
00444         }
00445     }
00446 #ifndef LEAN0X_NO_RVALUE_REFERENCES
00447 
00448     LEAN_INLINE void push_back(value_type &&value)
00449     {
00450         if (m_elementsEnd == m_capacityEnd)
00451             grow_and_pushHL(std::move(value));
00452         else
00453         {
00454             move_construct(m_elementsEnd, value);
00455             ++m_elementsEnd;
00456         }
00457     }
00458 #endif
00459 
00460     LEAN_INLINE void pop_back()
00461     {
00462         LEAN_ASSERT(!empty());
00463 
00464         destruct(m_elementsEnd--);
00465     }
00466 
00468     LEAN_INLINE void clear()
00469     {
00470         Element *oldElementsEnd = m_elementsEnd;
00471         m_elementsEnd = m_elements;
00472         destruct(m_elements, oldElementsEnd);
00473     }
00474 
00476     LEAN_INLINE void reserve(size_type newCapacity)
00477     {
00478         // Mind overflow
00479         check_length(newCapacity);
00480 
00481         if (newCapacity > capacity())
00482             reallocate(newCapacity);
00483     }
00485     void resize(size_type newCount)
00486     {
00487         // Mind overflow
00488         check_length(newCount);
00489 
00490         if (newCount > size())
00491         {
00492             if (newCount > capacity())
00493                 growToHL(newCount);
00494             
00495             Element *newElementsEnd = m_elements + newCount;
00496             default_construct(m_elementsEnd, newElementsEnd);
00497             m_elementsEnd = newElementsEnd;
00498         }
00499         else
00500         {
00501             Element *oldElementsEnd = m_elementsEnd;
00502             m_elementsEnd = m_elements + newCount;
00503             destruct(m_elementsEnd, oldElementsEnd);
00504         }
00505     }
00506     
00508     LEAN_INLINE reference at(size_type pos) { check_pos(pos); return m_elements[pos]; };
00510     LEAN_INLINE const_reference at(size_type pos) const { check_pos(pos); return m_elements[pos]; };
00512     LEAN_INLINE reference front(void) { LEAN_ASSERT(!empty()); return *m_elements; };
00514     LEAN_INLINE const_reference front(void) const { LEAN_ASSERT(!empty()); return *m_elements; };
00516     LEAN_INLINE reference back(void) { LEAN_ASSERT(!empty()); return m_elementsEnd[-1]; };
00518     LEAN_INLINE const_reference back(void) const { LEAN_ASSERT(!empty()); return m_elementsEnd[-1]; };
00519 
00521     LEAN_INLINE reference operator [](size_type pos) { return m_elements[pos]; };
00523     LEAN_INLINE const_reference operator [](size_type pos) const { return m_elements[pos]; };
00524 
00526     LEAN_INLINE iterator begin(void) { return m_elements; };
00528     LEAN_INLINE const_iterator begin(void) const { return m_elements; };
00530     LEAN_INLINE iterator end(void) { return m_elementsEnd; };
00532     LEAN_INLINE const_iterator end(void) const { return m_elementsEnd; };
00533 
00535     LEAN_INLINE allocator_type get_allocator() const { return m_allocator; };
00536 
00538     LEAN_INLINE bool empty(void) const { return (m_elements == m_elementsEnd); };
00540     LEAN_INLINE size_type size(void) const { return m_elementsEnd - m_elements; };
00542     LEAN_INLINE size_type capacity(void) const { return m_capacityEnd - m_elements; };
00543 
00545     size_type next_capacity_hint(size_type count) const
00546     {
00547         size_type capacity = this->capacity();
00548         LEAN_ASSERT(capacity <= s_maxSize);
00549         size_type capacityDelta = capacity / 2;
00550 
00551         // Try to increase capacity by 1.5 (mind overflow)
00552         capacity = (s_maxSize - capacityDelta < capacity)
00553             ? s_maxSize
00554             : capacity + capacityDelta;
00555 
00556         if (capacity < count)
00557             capacity = count;
00558 
00559         if (capacity < s_minSize)
00560             capacity = s_minSize;
00561         
00562         return capacity;
00563     }
00564 
00566     LEAN_INLINE size_type max_size() const
00567     {
00568         return s_maxSize;
00569     }
00570 
00572     LEAN_INLINE void swap(simple_vector &right) throw()
00573     {
00574         using std::swap;
00575 
00576         swap(m_allocator, right.m_allocator);
00577         swap(m_elements, right.m_elements);
00578         swap(m_elementsEnd, right.m_elementsEnd);
00579         swap(m_capacityEnd, right.m_capacityEnd);
00580     }
00581 };
00582 
00584 template <class Element, class Policy, class Allocator>
00585 LEAN_INLINE void swap(simple_vector<Element, Policy, Allocator> &left, simple_vector<Element, Policy, Allocator> &right)
00586 {
00587     left.swap(right);
00588 }
00589 
00590 } // namespace
00591 
00592 namespace simple_vector_policies = containers::simple_vector_policies;
00593 using containers::simple_vector;
00594 
00595 } // namespace
00596 
00597 #endif