File indexing completed on 2024-05-12 05:26:16
0001 /* 0002 Copyright (c) 2018 Christian Mollekopf <mollekopf@kolabsys.com> 0003 0004 This library is free software; you can redistribute it and/or modify it 0005 under the terms of the GNU Library General Public License as published by 0006 the Free Software Foundation; either version 2 of the License, or (at your 0007 option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, but WITHOUT 0010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 0012 License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to the 0016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0017 02110-1301, USA. 0018 */ 0019 #pragma once 0020 0021 #include <memory> 0022 #include <type_traits> 0023 #include <utility> 0024 0025 // A somewhat implementation of the expected monad, proposed here: 0026 // https://isocpp.org/files/papers/n4015.pdf 0027 0028 // A class used to differentiate errors and values when they are of the same type. 0029 template <typename Error> 0030 class Unexpected 0031 { 0032 0033 static_assert(!std::is_same<Error, void>::value, "Cannot have an Unexpected void"); 0034 0035 public: 0036 Unexpected() = delete; 0037 0038 constexpr explicit Unexpected(const Error &error) : mValue(error) {} 0039 constexpr explicit Unexpected(Error &&error) : mValue(std::move(error)) {} 0040 0041 // For implicit conversions when doing makeUnexpected(other) 0042 template <typename Other> 0043 constexpr explicit Unexpected(const Unexpected<Other> &error) : mValue(error.value()) 0044 { 0045 } 0046 template <typename Other> 0047 constexpr explicit Unexpected(Unexpected<Other> &&error) : mValue(std::move(error.value())) 0048 { 0049 } 0050 0051 constexpr const Error &value() const & 0052 { 0053 return mValue; 0054 } 0055 Error &value() & 0056 { 0057 return mValue; 0058 } 0059 0060 constexpr const Error &&value() const && 0061 { 0062 return std::move(mValue); 0063 } 0064 Error &&value() && 0065 { 0066 return std::move(mValue); 0067 } 0068 0069 private: 0070 Error mValue; 0071 }; 0072 0073 template <class Error> 0074 Unexpected<typename std::decay<Error>::type> makeUnexpected(Error &&e) 0075 { 0076 return Unexpected<typename std::decay<Error>::type>(std::forward<Error>(e)); 0077 } 0078 0079 template <typename Error> 0080 bool operator==(const Unexpected<Error> &lhs, const Unexpected<Error> &rhs) 0081 { 0082 return lhs.value() == rhs.value(); 0083 } 0084 0085 template <typename Error> 0086 bool operator!=(const Unexpected<Error> &lhs, const Unexpected<Error> &rhs) 0087 { 0088 return lhs.value() != rhs.value(); 0089 } 0090 0091 namespace detail { 0092 0093 namespace tags { 0094 struct Expected 0095 {}; 0096 struct Unexpected 0097 {}; 0098 } // namespace tags 0099 0100 // Write functions here when storage related and when Type != void 0101 template <typename Error, typename Type> 0102 struct StorageBase 0103 { 0104 protected: 0105 0106 // To be able to define a copy constructor in a child class 0107 StorageBase() {} 0108 0109 // Rule of 5 (copy constructors defined in StorageCopyConstructor) {{{ 0110 0111 StorageBase(StorageBase &&other) : mIsValue(other.mIsValue) 0112 { 0113 // This is a constructor, you have to construct object, not assign them 0114 // (hence the placement new) 0115 // 0116 // Here's the problem: 0117 // 0118 // Object that are part of a union are not initialized (which is 0119 // normal). If we replaced the placement new by a line like this: 0120 // 0121 // ``` 0122 // mValue = other.mValue; 0123 // ``` 0124 // 0125 // If overloaded, this will call `mValue.operator=(other.mValue);`, but 0126 // since we're in the constructor, mValue is not initialized. This can 0127 // cause big issues if `Type` / `Error` is not trivially (move) 0128 // assignable. 0129 // 0130 // And so, the placement new allows us to call the constructor of 0131 // `Type` or `Error` instead of its assignment operator. 0132 if (mIsValue) { 0133 new (std::addressof(mValue)) Type(std::move(other.mValue)); 0134 } else { 0135 new (std::addressof(mError)) Unexpected<Error>(std::move(other.mError)); 0136 } 0137 } 0138 0139 StorageBase &operator=(StorageBase &&other) 0140 { 0141 this->~StorageBase(); 0142 mIsValue = other.mIsValue; 0143 if (mIsValue) { 0144 mValue = std::move(other.mValue); 0145 } else { 0146 mError = std::move(other.mError); 0147 } 0148 return *this; 0149 } 0150 0151 ~StorageBase() 0152 { 0153 if (mIsValue) { 0154 mValue.~Type(); 0155 } else { 0156 mError.~Unexpected<Error>(); 0157 } 0158 } 0159 0160 // }}} 0161 0162 template <typename... Args> 0163 constexpr StorageBase(tags::Expected, Args &&... args) 0164 : mValue(std::forward<Args>(args)...), mIsValue(true) 0165 { 0166 } 0167 0168 template <typename... Args> 0169 constexpr StorageBase(tags::Unexpected, Args &&... args) 0170 : mError(std::forward<Args>(args)...), mIsValue(false) 0171 { 0172 } 0173 0174 union 0175 { 0176 Unexpected<Error> mError; 0177 Type mValue; 0178 }; 0179 bool mIsValue; 0180 }; 0181 0182 // Write functions here when storage related and when Type == void 0183 template <typename Error> 0184 struct StorageBase<Error, void> 0185 { 0186 protected: 0187 constexpr StorageBase(tags::Expected) : mIsValue(true) {} 0188 0189 template <typename... Args> 0190 constexpr StorageBase(tags::Unexpected, Args &&... args) 0191 : mError(std::forward<Args>(args)...), mIsValue(false) 0192 { 0193 } 0194 0195 Unexpected<Error> mError; 0196 bool mIsValue; 0197 }; 0198 0199 // Struct used to add the copy constructor / assignment only if both types are copy constructible 0200 template <typename Error, typename Type, 0201 bool both_copy_constructible = std::is_copy_constructible<Error>::value &&std::is_copy_constructible<Type>::value> 0202 struct StorageCopyConstructor; 0203 0204 template <typename Error, typename Type> 0205 struct StorageCopyConstructor<Error, Type, true> : StorageBase<Error, Type> 0206 { 0207 protected: 0208 using StorageBase<Error, Type>::StorageBase; 0209 0210 StorageCopyConstructor(const StorageCopyConstructor &other) : StorageBase<Error, Type>() 0211 { 0212 // If you're thinking WTF, see the comment in the move constructor above. 0213 this->mIsValue = other.mIsValue; 0214 if (this->mIsValue) { 0215 new (std::addressof(this->mValue)) Type(other.mValue); 0216 } else { 0217 new (std::addressof(this->mError)) Unexpected<Error>(other.mError); 0218 } 0219 } 0220 0221 StorageCopyConstructor &operator=(const StorageCopyConstructor &other) 0222 { 0223 this->mIsValue = other.mIsValue; 0224 if (this->mIsValue) { 0225 this->mValue = other.mValue; 0226 } else { 0227 this->mError = other.mError; 0228 } 0229 return *this; 0230 } 0231 0232 }; 0233 0234 template <typename Error, typename Type> 0235 struct StorageCopyConstructor<Error, Type, false> : StorageBase<Error, Type> 0236 { 0237 protected: 0238 using StorageBase<Error, Type>::StorageBase; 0239 }; 0240 0241 0242 // Write functions here when storage related, whether Type is void or not 0243 template <typename Error, typename Type> 0244 struct Storage : StorageCopyConstructor<Error, Type> 0245 { 0246 protected: 0247 // Forward the construction to StorageBase 0248 using StorageCopyConstructor<Error, Type>::StorageCopyConstructor; 0249 }; 0250 0251 // Write functions here when dev API related and when Type != void 0252 template <typename Error, typename Type> 0253 struct ExpectedBase : detail::Storage<Error, Type> 0254 { 0255 constexpr ExpectedBase() : detail::Storage<Error, Type>(detail::tags::Expected{}) {} 0256 0257 template <typename OtherError> 0258 constexpr ExpectedBase(const Unexpected<OtherError> &error) 0259 : detail::Storage<Error, Type>(detail::tags::Unexpected{}, error) 0260 { 0261 } 0262 template <typename OtherError> 0263 constexpr ExpectedBase(Unexpected<OtherError> &&error) 0264 : detail::Storage<Error, Type>(detail::tags::Unexpected{}, std::move(error)) 0265 { 0266 } 0267 0268 constexpr ExpectedBase(const Type &value) 0269 : detail::Storage<Error, Type>(detail::tags::Expected{}, value) 0270 { 0271 } 0272 constexpr ExpectedBase(Type &&value) 0273 : detail::Storage<Error, Type>(detail::tags::Expected{}, std::move(value)) 0274 { 0275 } 0276 0277 // Warning: will crash if this is an error. You should always check this is 0278 // an expected value before calling `.value()` 0279 constexpr const Type &value() const & 0280 { 0281 //FIXME: Q_ASSERT cannot be used in a constexpr with qt 5.9. See also: https://git.qt.io/consulting-usa/qtbase-xcb-rendering/commit/8ea27bb1c669e21100a6a042b0378b3346bdf671 0282 //Q_ASSERT(this->mIsValue); 0283 return this->mValue; 0284 } 0285 Type &&value() && 0286 { 0287 Q_ASSERT(this->mIsValue); 0288 return std::move(this->mValue); 0289 } 0290 }; 0291 0292 // Write functions here when dev API related and when Type == void 0293 template <typename Error> 0294 struct ExpectedBase<Error, void> : Storage<Error, void> 0295 { 0296 // Rewrite constructors for unexpected because Expected doesn't have direct access to it. 0297 template <typename OtherError> 0298 constexpr ExpectedBase(const Unexpected<OtherError> &error) 0299 : Storage<Error, void>(tags::Unexpected{}, error) 0300 { 0301 } 0302 template <typename OtherError> 0303 constexpr ExpectedBase(Unexpected<OtherError> &&error) 0304 : Storage<Error, void>(tags::Unexpected{}, std::move(error)) 0305 { 0306 } 0307 }; 0308 0309 } // namespace detail 0310 0311 // Write functions here when dev API related, whether Type is void or not 0312 template <typename Error, typename Type = void> 0313 class Expected : public detail::ExpectedBase<Error, Type> 0314 { 0315 static_assert(!std::is_same<Error, void>::value, "Expected with void Error is not implemented"); 0316 0317 public: 0318 using detail::ExpectedBase<Error, Type>::ExpectedBase; 0319 0320 constexpr const Error &error() const & 0321 { 0322 return this->mError.value(); 0323 } 0324 0325 constexpr bool isValue() const 0326 { 0327 return this->mIsValue; 0328 } 0329 constexpr explicit operator bool() const 0330 { 0331 return this->mIsValue; 0332 } 0333 };