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,191 +6,173 @@
6 #include <type_traits> 6 #include <type_traits>
7 7
8 template <class C> class vector { 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 #endif /* __VECTOR_H__ */ 178 #endif /* __VECTOR_H__ */