File indexing completed on 2024-04-28 16:53:08

0001 /*
0002  *   SPDX-FileCopyrightText: 2017 Ivan Čukić <ivan.cukic(at)kde.org>
0003  *
0004  *   SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005  */
0006 
0007 #ifndef ASYNQT_EXPECTED_H
0008 #define ASYNQT_EXPECTED_H
0009 
0010 namespace AsynQt
0011 {
0012 // Based on expected<T> by Alexandrescu,
0013 // with some nice syntax sugar on top
0014 
0015 template<typename T, typename E>
0016 class Expected
0017 {
0018 protected:
0019     union {
0020         T m_value;
0021         E m_error;
0022     };
0023 
0024     bool m_isValid;
0025 
0026     Expected() // used internally
0027     {
0028     }
0029 
0030 public:
0031     ~Expected()
0032     {
0033         if (m_isValid) {
0034             m_value.~T();
0035         } else {
0036             m_error.~E();
0037         }
0038     }
0039 
0040     Expected(const Expected &other)
0041         : m_isValid(other.m_isValid)
0042     {
0043         if (m_isValid) {
0044             new (&m_value) T(other.m_value);
0045         } else {
0046             new (&m_error) E(other.m_error);
0047         }
0048     }
0049 
0050     Expected(Expected &&other)
0051         : m_isValid(other.m_isValid)
0052     {
0053         if (m_isValid) {
0054             new (&m_value) T(std::move(other.m_value));
0055         } else {
0056             new (&m_error) E(std::move(other.m_error));
0057         }
0058     }
0059 
0060     Expected &operator=(Expected other)
0061     {
0062         swap(other);
0063         return *this;
0064     }
0065 
0066     void swap(Expected &other)
0067     {
0068         using std::swap;
0069         if (m_isValid) {
0070             if (other.m_isValid) {
0071                 // Both are valid, just swap the values
0072                 swap(m_value, other.m_value);
0073 
0074             } else {
0075                 // We are valid, but the other one is not
0076                 // we need to do the whole dance
0077                 auto temp = std::move(other.m_error); // moving the error into the temp
0078                 other.m_error.~E(); // destroying the original error object
0079                 new (&other.m_value) T(std::move(m_value)); // moving our value into the other
0080                 m_value.~T(); // destroying our value object
0081                 new (&m_error) E(std::move(temp)); // moving the error saved to the temp into us
0082                 std::swap(m_isValid, other.m_isValid); // swap the isValid flags
0083             }
0084 
0085         } else {
0086             if (other.m_isValid) {
0087                 // We are not valid, but the other one is,
0088                 // just call swap on other and rely on the
0089                 // implementation in the previous case
0090                 other.swap(*this);
0091 
0092             } else {
0093                 // Everything is rotten, just swap the errors
0094                 swap(m_error, other.m_error);
0095                 std::swap(m_isValid, other.m_isValid);
0096             }
0097         }
0098     }
0099 
0100     template<typename... ConsParams>
0101     static Expected success(ConsParams &&...params)
0102     {
0103         Expected result;
0104         result.m_isValid = true;
0105         new (&result.m_value) T(std::forward<ConsParams>(params)...);
0106         return result;
0107     }
0108 
0109     template<typename... ConsParams>
0110     static Expected error(ConsParams &&...params)
0111     {
0112         Expected result;
0113         result.m_isValid = false;
0114         new (&result.m_error) E(std::forward<ConsParams>(params)...);
0115         return result;
0116     }
0117 
0118     operator bool() const
0119     {
0120         return m_isValid;
0121     }
0122 
0123 #ifdef QT_NO_EXCEPTIONS
0124 #define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) std::terminate()
0125 #else
0126 #define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) throw std::logic_error(WHAT)
0127 #endif
0128 
0129     T &get()
0130     {
0131         if (!m_isValid)
0132             THROW_IF_EXCEPTIONS_ARE_ENABLED("expected<T, E> contains no value");
0133         return m_value;
0134     }
0135 
0136     const T &get() const
0137     {
0138         if (!m_isValid)
0139             THROW_IF_EXCEPTIONS_ARE_ENABLED("expected<T, E> contains no value");
0140         return m_value;
0141     }
0142 
0143     T *operator->()
0144     {
0145         return &get();
0146     }
0147 
0148     const T *operator->() const
0149     {
0150         return &get();
0151     }
0152 
0153     E &error()
0154     {
0155         if (m_isValid)
0156             THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
0157         return m_error;
0158     }
0159 
0160     const E &error() const
0161     {
0162         if (m_isValid)
0163             THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
0164         return m_error;
0165     }
0166 
0167 #undef THROW_IF_EXCEPTIONS_ARE_ENABLED
0168 
0169     template<typename F>
0170     void visit(F f)
0171     {
0172         if (m_isValid) {
0173             f(m_value);
0174         } else {
0175             f(m_error);
0176         }
0177     }
0178 };
0179 
0180 template<typename E>
0181 class Expected<void, E>
0182 {
0183 private:
0184     union {
0185         void *m_value;
0186         E m_error;
0187     };
0188 
0189     bool m_isValid;
0190 
0191     Expected()
0192     {
0193     } // used internally
0194 
0195 public:
0196     ~Expected()
0197     {
0198         if (m_isValid) {
0199             // m_value.~T();
0200         } else {
0201             m_error.~E();
0202         }
0203     }
0204 
0205     Expected(const Expected &other)
0206         : m_isValid(other.m_isValid)
0207     {
0208         if (m_isValid) {
0209             // new (&m_value) T(other.m_value);
0210         } else {
0211             new (&m_error) E(other.m_error);
0212         }
0213     }
0214 
0215     Expected(Expected &&other)
0216         : m_isValid(other.m_isValid)
0217     {
0218         if (m_isValid) {
0219             // new (&m_value) T(std::move(other.m_value));
0220         } else {
0221             new (&m_error) E(std::move(other.m_error));
0222         }
0223     }
0224 
0225     Expected &operator=(Expected other)
0226     {
0227         swap(other);
0228         return *this;
0229     }
0230 
0231     void swap(Expected &other)
0232     {
0233         using std::swap;
0234         if (m_isValid) {
0235             if (other.m_isValid) {
0236                 // Both are valid, we do not have any values
0237                 // to swap
0238 
0239             } else {
0240                 // We are valid, but the other one is not.
0241                 // We need to move the error into us
0242                 auto temp = std::move(other.m_error); // moving the error into the temp
0243                 other.m_error.~E(); // destroying the original error object
0244                 new (&m_error) E(std::move(temp)); // moving the error into us
0245                 std::swap(m_isValid, other.m_isValid); // swapping the isValid flags
0246             }
0247 
0248         } else {
0249             if (other.m_isValid) {
0250                 // We are not valid, but the other one is,
0251                 // just call swap on other and rely on the
0252                 // implementation in the previous case
0253                 other.swap(*this);
0254 
0255             } else {
0256                 // Everything is rotten, just swap the errors
0257                 swap(m_error, other.m_error);
0258                 std::swap(m_isValid, other.m_isValid);
0259             }
0260         }
0261     }
0262 
0263     static Expected success()
0264     {
0265         Expected result;
0266         result.m_isValid = true;
0267         result.m_value = nullptr;
0268         return result;
0269     }
0270 
0271     template<typename... ConsParams>
0272     static Expected error(ConsParams &&...params)
0273     {
0274         Expected result;
0275         result.m_isValid = false;
0276         new (&result.m_error) E(std::forward<ConsParams>(params)...);
0277         return result;
0278     }
0279 
0280     operator bool() const
0281     {
0282         return m_isValid;
0283     }
0284 
0285 #ifdef QT_NO_EXCEPTIONS
0286 #define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) std::terminate()
0287 #else
0288 #define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) throw std::logic_error(WHAT)
0289 #endif
0290 
0291     E &error()
0292     {
0293         if (m_isValid)
0294             THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
0295         return m_error;
0296     }
0297 
0298     const E &error() const
0299     {
0300         if (m_isValid)
0301             THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
0302         return m_error;
0303     }
0304 
0305 #undef THROW_IF_EXCEPTIONS_ARE_ENABLED
0306 
0307     template<typename F>
0308     void visit(F f)
0309     {
0310         if (m_isValid) {
0311             // f(m_value);
0312         } else {
0313             f(m_error);
0314         }
0315     }
0316 };
0317 
0318 } // namespace AsynQt
0319 
0320 #endif // ASYNQT_EXPECTED_H