summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--framework/src/errors.h82
1 files changed, 54 insertions, 28 deletions
diff --git a/framework/src/errors.h b/framework/src/errors.h
index 4249cf8d..a0b27084 100644
--- a/framework/src/errors.h
+++ b/framework/src/errors.h
@@ -104,9 +104,13 @@ template <typename Error, typename Type>
104struct StorageBase 104struct StorageBase
105{ 105{
106protected: 106protected:
107 // Rule of 5 {{{
108 107
109 StorageBase(const StorageBase &other) : mIsValue(other.mIsValue) 108 // To be able to define a copy constructor in a child class
109 StorageBase() {}
110
111 // Rule of 5 (copy constructors defined in StorageCopyConstructor) {{{
112
113 StorageBase(StorageBase &&other) : mIsValue(other.mIsValue)
110 { 114 {
111 // This is a constructor, you have to construct object, not assign them 115 // This is a constructor, you have to construct object, not assign them
112 // (hence the placement new) 116 // (hence the placement new)
@@ -128,33 +132,12 @@ protected:
128 // And so, the placement new allows us to call the constructor of 132 // And so, the placement new allows us to call the constructor of
129 // `Type` or `Error` instead of its assignment operator. 133 // `Type` or `Error` instead of its assignment operator.
130 if (mIsValue) { 134 if (mIsValue) {
131 new (std::addressof(mValue)) Type(other.mValue);
132 } else {
133 new (std::addressof(mError)) Unexpected<Error>(other.mError);
134 }
135 }
136
137 StorageBase(StorageBase &&other) : mIsValue(other.mIsValue)
138 {
139 // If you're thinking WTF, see the comment in the copy constructor above.
140 if (mIsValue) {
141 new (std::addressof(mValue)) Type(std::move(other.mValue)); 135 new (std::addressof(mValue)) Type(std::move(other.mValue));
142 } else { 136 } else {
143 new (std::addressof(mError)) Unexpected<Error>(std::move(other.mError)); 137 new (std::addressof(mError)) Unexpected<Error>(std::move(other.mError));
144 } 138 }
145 } 139 }
146 140
147 constexpr StorageBase &operator=(const StorageBase &other)
148 {
149 mIsValue = other.mIsValue;
150 if (mIsValue) {
151 mValue = other.mValue;
152 } else {
153 mError = other.mError;
154 }
155 return *this;
156 }
157
158 constexpr StorageBase &operator=(StorageBase &&other) 141 constexpr StorageBase &operator=(StorageBase &&other)
159 { 142 {
160 this->~StorageBase(); 143 this->~StorageBase();
@@ -215,13 +198,56 @@ protected:
215 bool mIsValue; 198 bool mIsValue;
216}; 199};
217 200
201// Struct used to add the copy constructor / assignment only if both types are copy constructible
202template <typename Error, typename Type,
203 bool both_copy_constructible = std::is_copy_constructible<Error>::value &&std::is_copy_constructible<Type>::value>
204struct StorageCopyConstructor;
205
206template <typename Error, typename Type>
207struct StorageCopyConstructor<Error, Type, true> : StorageBase<Error, Type>
208{
209protected:
210 using StorageBase<Error, Type>::StorageBase;
211
212 StorageCopyConstructor(const StorageCopyConstructor &other) : StorageBase<Error, Type>()
213 {
214 // If you're thinking WTF, see the comment in the move constructor above.
215 this->mIsValue = other.mIsValue;
216 if (this->mIsValue) {
217 new (std::addressof(this->mValue)) Type(other.mValue);
218 } else {
219 new (std::addressof(this->mError)) Unexpected<Error>(other.mError);
220 }
221 }
222
223 StorageCopyConstructor &operator=(const StorageCopyConstructor &other)
224 {
225 this->mIsValue = other.mIsValue;
226 if (this->mIsValue) {
227 this->mValue = other.mValue;
228 } else {
229 this->mError = other.mError;
230 }
231 return *this;
232 }
233
234};
235
236template <typename Error, typename Type>
237struct StorageCopyConstructor<Error, Type, false> : StorageBase<Error, Type>
238{
239protected:
240 using StorageBase<Error, Type>::StorageBase;
241};
242
243
218// Write functions here when storage related, whether Type is void or not 244// Write functions here when storage related, whether Type is void or not
219template <typename Error, typename Type> 245template <typename Error, typename Type>
220struct Storage : StorageBase<Error, Type> 246struct Storage : StorageCopyConstructor<Error, Type>
221{ 247{
222protected: 248protected:
223 // Forward the construction to StorageBase 249 // Forward the construction to StorageBase
224 using StorageBase<Error, Type>::StorageBase; 250 using StorageCopyConstructor<Error, Type>::StorageCopyConstructor;
225}; 251};
226 252
227// Write functions here when dev API related and when Type != void 253// Write functions here when dev API related and when Type != void
@@ -266,17 +292,17 @@ struct ExpectedBase : detail::Storage<Error, Type>
266 292
267// Write functions here when dev API related and when Type == void 293// Write functions here when dev API related and when Type == void
268template <typename Error> 294template <typename Error>
269struct ExpectedBase<Error, void> : detail::Storage<Error, void> 295struct ExpectedBase<Error, void> : Storage<Error, void>
270{ 296{
271 // Rewrite constructors for unexpected because Expected doesn't have direct access to it. 297 // Rewrite constructors for unexpected because Expected doesn't have direct access to it.
272 template <typename OtherError> 298 template <typename OtherError>
273 constexpr ExpectedBase(const Unexpected<OtherError> &error) 299 constexpr ExpectedBase(const Unexpected<OtherError> &error)
274 : detail::Storage<Error, void>(detail::tags::Unexpected{}, error) 300 : Storage<Error, void>(tags::Unexpected{}, error)
275 { 301 {
276 } 302 }
277 template <typename OtherError> 303 template <typename OtherError>
278 constexpr ExpectedBase(Unexpected<OtherError> &&error) 304 constexpr ExpectedBase(Unexpected<OtherError> &&error)
279 : detail::Storage<Error, void>(detail::tags::Unexpected{}, std::move(error)) 305 : Storage<Error, void>(tags::Unexpected{}, std::move(error))
280 { 306 {
281 } 307 }
282}; 308};