diff --git a/CMakeLists.txt b/CMakeLists.txt index 1da14c4b..09306f85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ include_directories(third-party/zydis/include/Zydis) include_directories(third-party/winpthread/include) include_directories(third-party/vulkan/include) include_directories(third-party/xxhash/include) +include_directories(third-party/result/include) add_subdirectory("third-party") #=================== EXAMPLE =================== include_directories(src) diff --git a/third-party/result/LICENSE b/third-party/result/LICENSE new file mode 100644 index 00000000..6f4e91e3 --- /dev/null +++ b/third-party/result/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {2016} {Mathieu Stefani} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third-party/result/README.md b/third-party/result/README.md new file mode 100644 index 00000000..7f588045 --- /dev/null +++ b/third-party/result/README.md @@ -0,0 +1,132 @@ +# Result +This is an adaption of [https://github.com/oktal/result](https://github.com/oktal/result). Make sure to support the original library! + +## Overview + +`Result` is a template type that can be used to return and propage errors. It can be used to replace +exceptions in context where they are not allowed or too slow to be used. `Result` is an algebraic data +type of `Ok(T)` that represents success and `Err(E)` representing an error. + +Design of this class has been mainly inspired by Rust's [std::result](https://doc.rust-lang.org/std/result/) + +``` + +struct Request { +}; + +struct Error { + + enum class Kind { + Timeout, + Invalid, + TooLong + } + + Error(Kind kind, std::string text); + + Kind kind; + std::string text; +}; + +Result parseRequest(const std::string& payload) { + if (payload.size() > 512) return Err(Error(Kind::TooLong, "Request exceeded maximum allowed size (512 bytes)")); + + Request request; + return Ok(request); +} + +std::string payload = receivePayload(); +auto request = parseRequest(payload).expect("Failed to parse request"); +``` + +To return a successfull `Result`, use the `Ok()` function. To return an error one, use the `Err()` function. + +## Extract and unwrap + +To extract the value from a `Result` type, you can use the `expect()` function that will yield the value +of an `Ok(T)` or terminate the program with an error message passed as a parameter. + +``` +Result r1 = Ok(3u); + +auto val = r1.expect("Failed to retrieve the value"); +assert(val == 3); +``` + +`unwrap()` can also be used to extract the value of a `Result`, yielding the value of an `Ok(T)` value or terminating +the program otherwise: + +``` +Result r1 = Ok(3u); + +auto val = r1.unwrap(); +assert(val == 3); +``` + +Instead a terminating the program, `unwrapOr` can be used to return a default value for an `Err(E)` Result: + +``` +Result r1 = Err(9u); + +auto val = r1.unwrapOr(0); +assert(val == 0); +``` + +## Map and bind + +To transform (or map) a `Result` to a `Result`, `Result` provides a `map` member function. +`map` will apply a function to a contained `Ok(T)` value and will return the result of the transformation, +and will leave an `Err(E)` untouched: + +``` +std::string stringify(int val) { return std::to_string(val); } + +Result r1 = Ok(2u); +auto r2 = r1.map(stringify); // Maps a Result to Result + +assert(r2.unwrap(), "2"); +``` + +Note that `map` should return a simple value and not a `Result`. A function returning nothing (`void`) +applied to a `Result` will yield a `Result`. + +To map a function to a contained `Err(E)` value, use the `mapError` function. + +To *bind* a `Result` to a `Result`, you can use the `andThen` member function: + +``` +Result square(uint32_t val) { return Ok(val * val); } + +Result r1 = Ok(3u); +auto r2 = r1.andThen(square); + +assert(r2.unwrap(), 9); +``` + +Use `orElse` to apply a function to a contained `Err(E)` value: + +``` +Result identity(uint32_t val) { return Ok(val); } + +Result r1 = Err(3u); +assert(r1.andThen(identity).orElse(square).unwrap(), 9); +``` + +## The TRY macro + +Like Rust, a `TRY` macro is also provided that comes in handy when writing code that calls a lot of functions returning a `Result`. + +the `TRY` macro will simply call its argument and short-cirtcuit the function returning an `Err(E)` if the operation returned an error `Result`: + +``` +Result copy(int srcFd, const char* dstFile) { + + auto fd = TRY(open(dstFile)); + auto data = TRY(read(srcFd)); + TRY(write(fd, data)); + + return Ok(); +} +``` + +Note that this macro uses a special extension called *compound statement* only supported by gcc and clang diff --git a/third-party/result/include/result.hpp b/third-party/result/include/result.hpp new file mode 100644 index 00000000..b75dee90 --- /dev/null +++ b/third-party/result/include/result.hpp @@ -0,0 +1,910 @@ +/* + Mathieu Stefani, 03 mai 2016 + + This header provides a Result type that can be used to replace exceptions in code + that has to handle error. + + Result can be used to return and propagate an error to the caller. Result is an algebraic + data type that can either Ok(T) to represent success or Err(E) to represent an error. +*/ + +#pragma once + +#include +#include +#include + +namespace types { + template + struct Ok { + Ok(const T& val) : val(val) { } + Ok(T&& val) : val(std::move(val)) { } + + T val; + }; + + template<> + struct Ok { }; + + template + struct Err { + Err(const E& val) : val(val) { } + Err(E&& val) : val(std::move(val)) { } + + E val; + }; +} + +template::type> +types::Ok Ok(T&& val) { + return types::Ok(std::forward(val)); +} + +inline types::Ok Ok() { + return types::Ok(); +} + +template::type> +types::Err Err(E&& val) { + return types::Err(std::forward(val)); +} + +namespace Rust { +template struct Result; +} + +namespace details { + +template struct void_t { typedef void type; }; + +namespace impl { + template struct result_of; + + template + struct result_of : public result_of { }; + + template + struct result_of { + typedef Ret type; + }; +} + +template +struct result_of : public impl::result_of { }; + +template +struct result_of { + typedef Ret type; +}; + +template +struct result_of { + typedef Ret type; +}; + +template +struct ResultOkType { typedef typename std::decay::type type; }; + +template +struct ResultOkType> { + typedef T type; +}; + +template +struct ResultErrType { typedef R type; }; + +template +struct ResultErrType> { + typedef typename std::remove_reference::type type; +}; + +template struct IsResult : public std::false_type { }; +template +struct IsResult> : public std::true_type { }; + +namespace ok { + +namespace impl { + +template struct Map; + +template +struct Map : public Map { }; + +template +struct Map : public Map { }; + +// General implementation +template +struct Map { + + static_assert(!IsResult::value, + "Can not map a callback returning a Result, use andThen instead"); + + template + static Rust::Result map(const Rust::Result& result, Func func) { + + static_assert( + std::is_same::value || + std::is_convertible::value, + "Incompatible types detected"); + + if (result.isOk()) { + auto res = func(result.storage().template get()); + return types::Ok(std::move(res)); + } + + return types::Err(result.storage().template get()); + } +}; + +// Specialization for callback returning void +template +struct Map { + + template + static Rust::Result map(const Rust::Result& result, Func func) { + + if (result.isOk()) { + func(result.storage().template get()); + return types::Ok(); + } + + return types::Err(result.storage().template get()); + } +}; + +// Specialization for a void Result +template +struct Map { + + template + static Rust::Result map(const Rust::Result& result, Func func) { + static_assert(std::is_same::value, + "Can not map a void callback on a non-void Result"); + + if (result.isOk()) { + auto ret = func(); + return types::Ok(std::move(ret)); + } + + return types::Err(result.storage().template get()); + } +}; + +// Specialization for callback returning void on a void Result +template<> +struct Map { + + template + static Rust::Result map(const Rust::Result& result, Func func) { + static_assert(std::is_same::value, + "Can not map a void callback on a non-void Result"); + + if (result.isOk()) { + func(); + return types::Ok(); + } + + return types::Err(result.storage().template get()); + } +}; + +// General specialization for a callback returning a Result +template +struct Map (Arg)> { + + template + static Rust::Result map(const Rust::Result& result, Func func) { + static_assert( + std::is_same::value || + std::is_convertible::value, + "Incompatible types detected"); + + if (result.isOk()) { + auto res = func(result.storage().template get()); + return res; + } + + return types::Err(result.storage().template get()); + } +}; + +// Specialization for a void callback returning a Result +template +struct Map (void)> { + + template + static Rust::Result map(const Rust::Result& result, Func func) { + static_assert(std::is_same::value, "Can not call a void-callback on a non-void Result"); + + if (result.isOk()) { + auto res = func(); + return res; + } + + return types::Err(result.storage().template get()); + } + +}; + +} // namespace impl + +template struct Map : public impl::Map { }; + +template +struct Map : public impl::Map { }; + +template +struct Map : public impl::Map { }; + +template +struct Map : public impl::Map { }; + +template +struct Map> : public impl::Map { }; + +} // namespace ok + + +namespace err { + +namespace impl { + +template struct Map; + +template +struct Map { + + static_assert(!IsResult::value, + "Can not map a callback returning a Result, use orElse instead"); + + template + static Rust::Result map(const Rust::Result& result, Func func) { + if (result.isErr()) { + auto res = func(result.storage().template get()); + return types::Err(res); + } + + return types::Ok(result.storage().template get()); + } + + template + static Rust::Result map(const Rust::Result& result, Func func) { + if (result.isErr()) { + auto res = func(result.storage().template get()); + return types::Err(res); + } + + return types::Ok(); + } + + +}; + +} // namespace impl + +template struct Map : public impl::Map { }; + +} // namespace err; + +namespace And { + +namespace impl { + + template struct Then; + + template + struct Then : public Then { }; + + template + struct Then : public Then { }; + + template + struct Then : public Then { }; + + template + struct Then { + static_assert(std::is_same::value, + "then() should not return anything, use map() instead"); + + template + static Rust::Result then(const Rust::Result& result, Func func) { + if (result.isOk()) { + func(result.storage().template get()); + } + return result; + } + }; + + template + struct Then { + static_assert(std::is_same::value, + "then() should not return anything, use map() instead"); + + template + static Rust::Result then(const Rust::Result& result, Func func) { + static_assert(std::is_same::value, "Can not call a void-callback on a non-void Result"); + + if (result.isOk()) { + func(); + } + + return result; + } + }; + + +} // namespace impl + +template +struct Then : public impl::Then { }; + +template +struct Then : public impl::Then { }; + +template +struct Then : public impl::Then { }; + +template +struct Then : public impl::Then { }; + +} // namespace And + +namespace Or { + +namespace impl { + + template struct Else; + + template + struct Else : public Else { }; + + template + struct Else : public Else { }; + + template + struct Else : public Else { }; + + template + struct Else (Arg)> { + + template + static Rust::Result orElse(const Rust::Result& result, Func func) { + static_assert( + std::is_same::value || + std::is_convertible::value, + "Incompatible types detected"); + + if (result.isErr()) { + auto res = func(result.storage().template get()); + return res; + } + + return types::Ok(result.storage().template get()); + } + + template + static Rust::Result orElse(const Rust::Result& result, Func func) { + if (result.isErr()) { + auto res = func(result.storage().template get()); + return res; + } + + return types::Ok(); + } + + }; + + template + struct Else (void)> { + + template + static Rust::Result orElse(const Rust::Result& result, Func func) { + static_assert(std::is_same::value, + "Can not call a void-callback on a non-void Result"); + + if (result.isErr()) { + auto res = func(); + return res; + } + + return types::Ok(result.storage().template get()); + } + + template + static Rust::Result orElse(const Rust::Result& result, Func func) { + if (result.isErr()) { + auto res = func(); + return res; + } + + return types::Ok(); + } + + }; + +} // namespace impl + +template +struct Else : public impl::Else { }; + +template +struct Else : public impl::Else { }; + +template +struct Else : public impl::Else { }; + +template +struct Else : public impl::Else { }; + +} // namespace Or + +namespace Other { + +namespace impl { + + template struct Wise; + + template + struct Wise : public Wise { }; + + template + struct Wise : public Wise { }; + + template + struct Wise : public Wise { }; + + template + struct Wise { + + template + static Rust::Result otherwise(const Rust::Result& result, Func func) { + static_assert( + std::is_same::value || + std::is_convertible::value, + "Incompatible types detected"); + + static_assert(std::is_same::value, + "callback should not return anything, use mapError() for that"); + + if (result.isErr()) { + func(result.storage().template get()); + } + return result; + } + + }; + +} // namespace impl + +template +struct Wise : public impl::Wise { }; + +template +struct Wise : public impl::Wise { }; + +template +struct Wise : public impl::Wise { }; + +template +struct Wise : public impl::Wise { }; + +} // namespace Other + +template::type + >::type, + E> + > +Ret map(const Rust::Result& result, Func func) { + return ok::Map::map(result, func); +} + +template::type + >::type + > + > +Ret mapError(const Rust::Result& result, Func func) { + return err::Map::map(result, func); +} + +template +Rust::Result then(const Rust::Result& result, Func func) { + return And::Then::then(result, func); +} + +template +Rust::Result otherwise(const Rust::Result& result, Func func) { + return Other::Wise::otherwise(result, func); +} + +template::type + >::type + > +> +Ret orElse(const Rust::Result& result, Func func) { + return Or::Else::orElse(result, func); +} + +struct ok_tag { }; +struct err_tag { }; + +template +struct Storage { + static constexpr size_t Size = sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E); + static constexpr size_t Align = sizeof(T) > sizeof(E) ? alignof(T) : alignof(E); + + typedef typename std::aligned_storage::type type; + + Storage() + : initialized_(false) + { } + + void construct(types::Ok ok) + { + new (&storage_) T(ok.val); + initialized_ = true; + } + void construct(types::Err err) + { + new (&storage_) E(err.val); + initialized_ = true; + } + + template + void rawConstruct(U&& val) { + typedef typename std::decay::type CleanU; + + new (&storage_) CleanU(std::forward(val)); + initialized_ = true; + } + + template + const U& get() const { + return *reinterpret_cast(&storage_); + } + + template + U& get() { + return *reinterpret_cast(&storage_); + } + + void destroy(ok_tag) { + if (initialized_) { + get().~T(); + initialized_ = false; + } + } + + void destroy(err_tag) { + if (initialized_) { + get().~E(); + initialized_ = false; + } + } + + type storage_; + bool initialized_; +}; + +template +struct Storage { + typedef typename std::aligned_storage::type type; + + void construct(types::Ok) + { + initialized_ = true; + } + + void construct(types::Err err) + { + new (&storage_) E(err.val); + initialized_ = true; + } + + template + void rawConstruct(U&& val) { + typedef typename std::decay::type CleanU; + + new (&storage_) CleanU(std::forward(val)); + initialized_ = true; + } + + void destroy(ok_tag) { initialized_ = false; } + void destroy(err_tag) { + if (initialized_) { + get().~E(); initialized_ = false; + } + } + + template + const U& get() const { + return *reinterpret_cast(&storage_); + } + + template + U& get() { + return *reinterpret_cast(&storage_); + } + + type storage_; + bool initialized_; +}; + +template +struct Constructor { + + static void move(Storage&& src, Storage& dst, ok_tag) { + dst.rawConstruct(std::move(src.template get())); + src.destroy(ok_tag()); + } + + static void copy(const Storage& src, Storage& dst, ok_tag) { + dst.rawConstruct(src.template get()); + } + + static void move(Storage&& src, Storage& dst, err_tag) { + dst.rawConstruct(std::move(src.template get())); + src.destroy(err_tag()); + } + + static void copy(const Storage& src, Storage& dst, err_tag) { + dst.rawConstruct(src.template get()); + } +}; + +template +struct Constructor { + static void move(Storage&& src, Storage& dst, ok_tag) { + } + + static void copy(const Storage& src, Storage& dst, ok_tag) { + } + + static void move(Storage&& src, Storage& dst, err_tag) { + dst.rawConstruct(std::move(src.template get())); + src.destroy(err_tag()); + } + + static void copy(const Storage& src, Storage& dst, err_tag) { + dst.rawConstruct(src.template get()); + } +}; + +} // namespace details + +namespace rpog { + +template struct EqualityComparable : std::false_type { }; + +template +struct EqualityComparable() == std::declval())>::type + >::type +> : std::true_type +{ +}; + + +} // namespace rpog + +namespace Rust { +template +struct Result { + + static_assert(!std::is_same::value, "void error type is not allowed"); + + typedef details::Storage storage_type; + + Result(types::Ok ok) + : ok_(true) + { + storage_.construct(std::move(ok)); + } + + Result(types::Err err) + : ok_(false) + { + storage_.construct(std::move(err)); + } + + Result(Result&& other) { + if (other.isOk()) { + details::Constructor::move(std::move(other.storage_), storage_, details::ok_tag()); + ok_ = true; + } else { + details::Constructor::move(std::move(other.storage_), storage_, details::err_tag()); + ok_ = false; + } + } + + Result(const Result& other) { + if (other.isOk()) { + details::Constructor::copy(other.storage_, storage_, details::ok_tag()); + ok_ = true; + } else { + details::Constructor::copy(other.storage_, storage_, details::err_tag()); + ok_ = false; + } + } + + ~Result() { + if (ok_) + storage_.destroy(details::ok_tag()); + else + storage_.destroy(details::err_tag()); + } + + bool isOk() const { + return ok_; + } + + bool isErr() const { + return !ok_; + } + + T expect(const char* str) const { + if (!isOk()) { + std::fprintf(stderr, "%s\n", str); + std::terminate(); + } + return expect_impl(std::is_same()); + } + + template::type + >::type, + E> + > + Ret map(Func func) const { + return details::map(*this, func); + } + + template::type + >::type + > + > + Ret mapError(Func func) const { + return details::mapError(*this, func); + } + + template + Result then(Func func) const { + return details::then(*this, func); + } + + template + Result otherwise(Func func) const { + return details::otherwise(*this, func); + } + + template::type + >::type + > + > + Ret orElse(Func func) const { + return details::orElse(*this, func); + } + + storage_type& storage() { + return storage_; + } + + const storage_type& storage() const { + return storage_; + } + + template + typename std::enable_if< + !std::is_same::value, + U + >::type + unwrapOr(const U& defaultValue) const { + if (isOk()) { + return storage().template get(); + } + return defaultValue; + } + + template + typename std::enable_if< + !std::is_same::value, + U + >::type + unwrap() const { + if (isOk()) { + return storage().template get(); + } + + std::fprintf(stderr, "Attempting to unwrap an error Result\n"); + std::terminate(); + } + + E unwrapErr() const { + if (isErr()) { + return storage().template get(); + } + + std::fprintf(stderr, "Attempting to unwrapErr an ok Result\n"); + std::terminate(); + } + +private: + T expect_impl(std::true_type) const { } + T expect_impl(std::false_type) const { return storage_.template get(); } + + bool ok_; + storage_type storage_; +}; + +template +bool operator==(const Rust::Result& lhs, const Rust::Result& rhs) { + static_assert(rpog::EqualityComparable::value, "T must be EqualityComparable for Result to be comparable"); + static_assert(rpog::EqualityComparable::value, "E must be EqualityComparable for Result to be comparable"); + + if (lhs.isOk() && rhs.isOk()) { + return lhs.storage().template get() == rhs.storage().template get(); + } + if (lhs.isErr() && rhs.isErr()) { + return lhs.storage().template get() == rhs.storage().template get(); + } +} + +template +bool operator==(const Rust::Result& lhs, types::Ok ok) { + static_assert(rpog::EqualityComparable::value, "T must be EqualityComparable for Result to be comparable"); + + if (!lhs.isOk()) return false; + + return lhs.storage().template get() == ok.val; +} + +template +bool operator==(const Rust::Result& lhs, types::Ok) { + return lhs.isOk(); +} + +template +bool operator==(const Rust::Result& lhs, types::Err err) { + static_assert(rpog::EqualityComparable::value, "E must be EqualityComparable for Result to be comparable"); + if (!lhs.isErr()) return false; + + return lhs.storage().template get() == err.val; +} +} // end namespace Rust + +#define TRY(...) \ + ({ \ + auto res = __VA_ARGS__; \ + if (!res.isOk()) { \ + typedef details::ResultErrType::type E; \ + return types::Err(res.storage().get()); \ + } \ + typedef details::ResultOkType::type T; \ + res.storage().get(); \ + })