Commit 1df054ae3cafe9c9d2f1f7895e291d0ed8b71530

Authored by Grzegorz Jabłoński
1 parent 84f16473

Fixed error with 0-size vector

examples11/06-template_metaprogramming/vector.h
... ... @@ -6,191 +6,173 @@
6 6 #include <type_traits>
7 7  
8 8 template <class C> class vector {
9   - C* data;
10   - unsigned int size;
11   - unsigned int cap;
12   -
13   - private:
14   - static unsigned int round_up_to_power_of_2(unsigned w)
15   - {
16   - constexpr auto bits = std::numeric_limits<unsigned>::digits;
17   - --w;
18   - for (unsigned s = 1; s < bits; s *= 2)
19   - w |= w >> s;
20   - return ++w;
21   - };
22   -
23   - void destroy_array(C* ptr, unsigned int s)
24   - {
25   - for (unsigned j = 0; j < s; ++j)
26   - (ptr + j)->~C();
27   - free(ptr);
  9 + C *data;
  10 + unsigned int size;
  11 + unsigned int cap;
  12 +
  13 +private:
  14 + static unsigned int round_up_to_power_of_2(unsigned w) {
  15 + constexpr auto bits = std::numeric_limits<unsigned>::digits;
  16 + --w;
  17 + for (unsigned s = 1; s < bits; s *= 2)
  18 + w |= w >> s;
  19 + return ++w;
  20 + };
  21 +
  22 + void destroy_array(C *ptr, unsigned int s) {
  23 + for (unsigned j = 0; j < s; ++j)
  24 + (ptr + j)->~C();
  25 + free(ptr);
  26 + }
  27 +
  28 + void reserve_internal(unsigned capacity) {
  29 + unsigned int newcap = capacity;
  30 + C *newdata;
  31 + if constexpr (!std::is_trivially_copyable<C>()) {
  32 + newdata =
  33 + static_cast<C *>(std::aligned_alloc(alignof(C), newcap * sizeof(C)));
  34 + if (!newdata)
  35 + throw bad_alloc();
  36 + unsigned i;
  37 + try {
  38 + for (i = 0; i < size; i++)
  39 + if constexpr (noexcept(C(std::move(data[i]))))
  40 + new (newdata + i) C(std::move(data[i]));
  41 + else
  42 + new (newdata + i) C(data[i]);
  43 + } catch (...) {
  44 + destroy_array(newdata, i);
  45 + throw;
  46 + }
  47 + destroy_array(data, size);
  48 + } else {
  49 + newdata = static_cast<C *>(std::realloc(data, newcap * sizeof(C)));
  50 + if (!newdata)
  51 + throw bad_alloc();
28 52 }
29   -
30   - void reserve_internal(unsigned capacity)
31   - {
32   - unsigned int newcap = capacity;
33   - C* newdata;
34   - if constexpr (!std::is_trivially_copyable<C>()) {
35   - newdata = static_cast<C*>(
36   - std::aligned_alloc(alignof(C), newcap * sizeof(C)));
37   - if (!newdata)
38   - throw bad_alloc();
39   - unsigned i;
40   - try {
41   - for (i = 0; i < size; i++)
42   - if constexpr (noexcept(C(std::move(data[i]))))
43   - new (newdata + i) C(std::move(data[i]));
44   - else
45   - new (newdata + i) C(data[i]);
46   - }
47   - catch (...) {
48   - destroy_array(newdata, i);
49   - throw;
50   - }
51   - destroy_array(data, size);
52   - }
53   - else {
54   - newdata = static_cast<C*>(std::realloc(data, newcap * sizeof(C)));
55   - if (!newdata)
56   - throw bad_alloc();
57   - }
58   - data = newdata;
59   - cap = newcap;
  53 + data = newdata;
  54 + cap = newcap;
  55 + }
  56 +
  57 + void resize_before_push() {
  58 + if (size >= cap) {
  59 + if (cap > std::numeric_limits<decltype(size)>::max() / (2 * sizeof(C)))
  60 + throw length_error("Vector too long");
  61 + reserve_internal((cap == 0) ? 1 : 2 * cap);
60 62 }
61   -
62   - void resize_before_push()
63   - {
64   - if (size >= cap) {
65   - if (cap >
66   - std::numeric_limits<decltype(size)>::max() / (2 * sizeof(C)))
67   - throw length_error("Vector too long");
68   - reserve_internal((cap == 0) ? 1 : 2 * cap);
69   - }
  63 + }
  64 +
  65 +public:
  66 + class index_out_of_range {};
  67 + explicit vector(unsigned s) {
  68 +
  69 + cap = round_up_to_power_of_2(s);
  70 + if (s == 0) {
  71 + size = 0;
  72 + cap = 0;
  73 + data = 0;
  74 + return;
70 75 }
71 76  
72   - public:
73   - class index_out_of_range {};
74   - explicit vector(unsigned s)
75   - {
76   -
77   - cap = round_up_to_power_of_2(s);
78   -
79   - data = static_cast<C*>(std::aligned_alloc(alignof(C), cap * sizeof(C)));
80   - if (!data)
81   - throw bad_alloc();
82   - size = s;
83   -
84   - unsigned i;
85   - try {
86   - for (i = 0; i < size; i++)
87   - new (data + i) C();
88   - }
89   - catch (...) {
90   - destroy_array(data, i);
91   - throw;
92   - }
  77 + data = static_cast<C *>(std::aligned_alloc(alignof(C), cap * sizeof(C)));
  78 + if (!data)
  79 + throw bad_alloc();
  80 + size = s;
  81 +
  82 + unsigned i;
  83 + try {
  84 + for (i = 0; i < size; i++)
  85 + new (data + i) C();
  86 + } catch (...) {
  87 + destroy_array(data, i);
  88 + throw;
93 89 }
94   -
95   - ~vector()
96   - {
97   - destroy_array(data, size);
98   - }
99   -
100   - C& operator[](unsigned int pos)
101   - {
102   - if (pos >= size)
103   - throw index_out_of_range();
104   - return data[pos];
105   - }
106   -
107   - C operator[](unsigned int pos) const
108   - {
109   - if (pos >= size)
110   - throw index_out_of_range();
111   - return data[pos];
112   - }
113   -
114   - vector(const vector<C>& s)
115   - {
116   - cap = s.cap;
117   - data = static_cast<C*>(std::aligned_alloc(alignof(C), cap * sizeof(C)));
118   - if (!data)
119   - throw bad_alloc();
120   - size = s.size;
121   - unsigned i;
122   - try {
123   - for (i = 0; i < size; i++)
124   - new (data + i) C(s.data[i]);
125   - }
126   - catch (...) {
127   - destroy_array(data, i);
128   - throw;
129   - }
130   - }
131   -
132   - void swap(vector<C>& s) noexcept
133   - {
134   - C* t1 = s.data;
135   - unsigned int t2 = s.size;
136   - unsigned int t3 = s.cap;
137   - s.data = data;
138   - s.size = size;
139   - data = t1;
140   - size = t2;
141   - cap = t3;
142   - }
143   -
144   - vector<C>& operator=(const vector<C>& s)
145   - {
146   - if (this == &s)
147   - return *this;
148   - vector<C> n(s);
149   - swap(n);
150   - return *this;
151   - }
152   - friend ostream& operator<<(ostream& o, const vector<C>& v)
153   - {
154   - o << '[';
155   - for (unsigned i = 0; i < v.size; i++) {
156   - o << v[i];
157   - if (i != v.size - 1)
158   - o << ',';
159   - };
160   - o << ']';
161   - return o;
162   - }
163   -
164   - template <class... Args> C& emplace_back(Args&&... args)
165   - {
166   - // cout << "emplace_back" << endl;
167   - resize_before_push();
168   - new (data + size) C(std::forward<Args>(args)...);
169   - return *(data + size++);
170   - }
171   -
172   - private:
173   - public:
174   - void reserve(unsigned capacity)
175   - {
176   - if (capacity >
177   - std::numeric_limits<decltype(size)>::max() / (2 * sizeof(C)))
178   - throw length_error("Vector too long");
179   - unsigned int cap_rounded_up = round_up_to_power_of_2(capacity);
180   - reserve_internal(cap_rounded_up);
181   - }
182   -
183   - template <class T> std::tuple<C&> push_back(T&& s)
184   - {
185   - C& r = emplace_back(std::forward<T>(s));
186   - return std::tuple<C&>(r);
187   - }
188   -
189   - template <class T, class... Args> auto push_back(T&& a, Args&&... args)
190   - {
191   - auto op1 = push_back(std::forward<T>(a));
192   - auto op2 = push_back(args...);
193   - return std::tuple_cat(op1, op2);
  90 + }
  91 +
  92 + ~vector() { destroy_array(data, size); }
  93 +
  94 + C &operator[](unsigned int pos) {
  95 + if (pos >= size)
  96 + throw index_out_of_range();
  97 + return data[pos];
  98 + }
  99 +
  100 + C operator[](unsigned int pos) const {
  101 + if (pos >= size)
  102 + throw index_out_of_range();
  103 + return data[pos];
  104 + }
  105 +
  106 + vector(const vector<C> &s) {
  107 + cap = s.cap;
  108 + data = static_cast<C *>(std::aligned_alloc(alignof(C), cap * sizeof(C)));
  109 + if (!data)
  110 + throw bad_alloc();
  111 + size = s.size;
  112 + unsigned i;
  113 + try {
  114 + for (i = 0; i < size; i++)
  115 + new (data + i) C(s.data[i]);
  116 + } catch (...) {
  117 + destroy_array(data, i);
  118 + throw;
194 119 }
  120 + }
  121 +
  122 + void swap(vector<C> &s) noexcept {
  123 + C *t1 = s.data;
  124 + unsigned int t2 = s.size;
  125 + unsigned int t3 = s.cap;
  126 + s.data = data;
  127 + s.size = size;
  128 + data = t1;
  129 + size = t2;
  130 + cap = t3;
  131 + }
  132 +
  133 + vector<C> &operator=(const vector<C> &s) {
  134 + if (this == &s)
  135 + return *this;
  136 + vector<C> n(s);
  137 + swap(n);
  138 + return *this;
  139 + }
  140 + friend ostream &operator<<(ostream &o, const vector<C> &v) {
  141 + o << '[';
  142 + for (unsigned i = 0; i < v.size; i++) {
  143 + o << v[i];
  144 + if (i != v.size - 1)
  145 + o << ',';
  146 + };
  147 + o << ']';
  148 + return o;
  149 + }
  150 +
  151 + template <class... Args> C &emplace_back(Args &&...args) {
  152 + // cout << "emplace_back" << endl;
  153 + resize_before_push();
  154 + new (data + size) C(std::forward<Args>(args)...);
  155 + return *(data + size++);
  156 + }
  157 +
  158 +private:
  159 +public:
  160 + void reserve(unsigned capacity) {
  161 + if (capacity > std::numeric_limits<decltype(size)>::max() / (2 * sizeof(C)))
  162 + throw length_error("Vector too long");
  163 + unsigned int cap_rounded_up = round_up_to_power_of_2(capacity);
  164 + reserve_internal(cap_rounded_up);
  165 + }
  166 +
  167 + template <class T> std::tuple<C &> push_back(T &&s) {
  168 + C &r = emplace_back(std::forward<T>(s));
  169 + return std::tuple<C &>(r);
  170 + }
  171 +
  172 + template <class T, class... Args> auto push_back(T &&a, Args &&...args) {
  173 + auto op1 = push_back(std::forward<T>(a));
  174 + auto op2 = push_back(args...);
  175 + return std::tuple_cat(op1, op2);
  176 + }
195 177 };
196 178 #endif /* __VECTOR_H__ */
... ...