diff --git a/.clang-format b/.clang-format index 48451bcf..c075a60a 100644 --- a/.clang-format +++ b/.clang-format @@ -3,6 +3,7 @@ IndentWidth: 4 AlignAfterOpenBracket: BlockIndent AlignEscapedNewlines: Left +AlignConsecutiveAssignments: false AlignOperands: DontAlign AlignTrailingComments: false @@ -22,7 +23,7 @@ AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: Yes -BinPackArguments: true +BinPackArguments: false BinPackParameters: true BraceWrapping: @@ -49,7 +50,7 @@ BraceWrapping: BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeConceptDeclarations: true -BreakBeforeTernaryOperators: true +BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon BreakStringLiterals: true @@ -57,7 +58,7 @@ ColumnLimit: 100 CompactNamespaces: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 -Cpp11BracedListStyle: false +Cpp11BracedListStyle: true DeriveLineEnding: false UseCRLF: false @@ -84,7 +85,7 @@ IndentCaseLabels: true IndentExternBlock: Indent IndentGotoLabels: true IndentPPDirectives: BeforeHash -IndentRequiresClause: false +IndentRequiresClause: true IndentWrappedFunctionNames: false # InsertBraces: true InsertTrailingCommas: None @@ -101,8 +102,9 @@ PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 +PenaltyBreakOpenParenthesis: 0 PenaltyBreakTemplateDeclaration: 10 -PenaltyExcessCharacter: 1000000 +PenaltyExcessCharacter: 10 PenaltyReturnTypeOnItsOwnLine: 1000000 PenaltyIndentedWhitespace: 0 @@ -121,7 +123,7 @@ SpaceAfterTemplateKeyword: true SpaceAroundPointerQualifiers: Default SpaceBeforeAssignmentOperators: true SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: true +SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements diff --git a/bindings/Cocos2d.bro b/bindings/Cocos2d.bro index 71644c07..21cfb16e 100644 --- a/bindings/Cocos2d.bro +++ b/bindings/Cocos2d.bro @@ -210,6 +210,7 @@ class cocos2d::CCFileUtils : cocos2d::TypeInfo { virtual void addSearchPath(const char* path); virtual void removeSearchPath(const char *path); virtual std::string fullPathForFilename(const char* filename, bool unk); + void removeAllPaths() = mac 0x241600; } class cocos2d::CCGLProgram { diff --git a/loader/include/Geode/DefaultInclude.hpp b/loader/include/Geode/DefaultInclude.hpp index 71f58cf0..23a7a8e8 100644 --- a/loader/include/Geode/DefaultInclude.hpp +++ b/loader/include/Geode/DefaultInclude.hpp @@ -69,45 +69,120 @@ namespace cocos2d::extension {} #define GEODE_EXPAND(x) x #define GEODE_INVOKE(macro, ...) GEODE_EXPAND(macro(__VA_ARGS__)) -#define GEODE_FILL_CONSTRUCTOR(Class_, Offset_) \ - Class_(std::monostate, size_t fill) : \ - Class_({}, std::memset(reinterpret_cast(this) + Offset_, 0, fill - Offset_)) { \ - } \ +#define GEODE_FILL_CONSTRUCTOR(Class_, Offset_) \ + Class_(std::monostate, size_t fill) : \ + Class_({}, std::memset(reinterpret_cast(this) + Offset_, 0, fill - Offset_)) {} \ Class_(std::monostate, void*) -#define GEODE_MONOSTATE_CONSTRUCTOR_BEGIN(Class_) \ - GEODE_MACOS(GEODE_FILL_CONSTRUCTOR(Class_, 0) {}) \ - GEODE_IOS(GEODE_FILL_CONSTRUCTOR(Class_, 0) {}) +#define GEODE_MONOSTATE_CONSTRUCTOR_BEGIN(Class_) \ + GEODE_MACOS(GEODE_FILL_CONSTRUCTOR(Class_, 0){}) \ + GEODE_IOS(GEODE_FILL_CONSTRUCTOR(Class_, 0){}) -#define GEODE_MONOSTATE_CONSTRUCTOR_COCOS(Class_, Base_) \ - GEODE_MACOS(Class_(std::monostate, size_t fill) : Base_({}, fill) {}) \ - GEODE_IOS(Class_(std::monostate, size_t fill) : Base_({}, fill) {}) +#define GEODE_MONOSTATE_CONSTRUCTOR_COCOS(Class_, Base_) \ + GEODE_MACOS(Class_(std::monostate, size_t fill) : Base_({}, fill){}) \ + GEODE_IOS(Class_(std::monostate, size_t fill) : Base_({}, fill){}) -#define GEODE_MONOSTATE_CONSTRUCTOR_GD(Class_, Base_) \ - GEODE_WINDOWS(Class_(std::monostate, size_t fill) : Base_({}, fill) {}) \ - GEODE_MACOS(Class_(std::monostate, size_t fill) : Base_({}, fill) {}) \ - GEODE_IOS(Class_(std::monostate, size_t fill) : Base_({}, fill) {}) +#define GEODE_MONOSTATE_CONSTRUCTOR_GD(Class_, Base_) \ + GEODE_WINDOWS(Class_(std::monostate, size_t fill) : Base_({}, fill){}) \ + GEODE_MACOS(Class_(std::monostate, size_t fill) : Base_({}, fill){}) \ + GEODE_IOS(Class_(std::monostate, size_t fill) : Base_({}, fill){}) -#define GEODE_MONOSTATE_CONSTRUCTOR_CUTOFF(Class_, Base_) \ - GEODE_WINDOWS(GEODE_FILL_CONSTRUCTOR(Class_, sizeof(Base_)) : Base_() {}) \ - GEODE_MACOS(Class_(std::monostate, size_t fill) : Base_({}, fill) {}) \ - GEODE_IOS(Class_(std::monostate, size_t fill) : Base_({}, fill) {}) +#define GEODE_MONOSTATE_CONSTRUCTOR_CUTOFF(Class_, Base_) \ + GEODE_WINDOWS(GEODE_FILL_CONSTRUCTOR(Class_, sizeof(Base_)) : Base_(){}) \ + GEODE_MACOS(Class_(std::monostate, size_t fill) : Base_({}, fill){}) \ + GEODE_IOS(Class_(std::monostate, size_t fill) : Base_({}, fill){}) #define GEODE_NUMBER_OF_ARGS(...) \ GEODE_EXPAND(GEODE_NUMBER_OF_ARGS_(__VA_ARGS__, GEODE_NUMBER_SEQUENCE(), )) #define GEODE_NUMBER_OF_ARGS_(...) GEODE_EXPAND(GEODE_NUMBER_OF_ARGS_N(__VA_ARGS__)) -#define GEODE_NUMBER_OF_ARGS_N( \ - _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ - _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ - _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, \ - _59, _60, _61, _62, _63, N, ... \ -) \ +#define GEODE_NUMBER_OF_ARGS_N( \ + _1, \ + _2, \ + _3, \ + _4, \ + _5, \ + _6, \ + _7, \ + _8, \ + _9, \ + _10, \ + _11, \ + _12, \ + _13, \ + _14, \ + _15, \ + _16, \ + _17, \ + _18, \ + _19, \ + _20, \ + _21, \ + _22, \ + _23, \ + _24, \ + _25, \ + _26, \ + _27, \ + _28, \ + _29, \ + _30, \ + _31, \ + _32, \ + _33, \ + _34, \ + _35, \ + _36, \ + _37, \ + _38, \ + _39, \ + _40, \ + _41, \ + _42, \ + _43, \ + _44, \ + _45, \ + _46, \ + _47, \ + _48, \ + _49, \ + _50, \ + _51, \ + _52, \ + _53, \ + _54, \ + _55, \ + _56, \ + _57, \ + _58, \ + _59, \ + _60, \ + _61, \ + _62, \ + _63, \ + N, \ + ... \ +) \ N #define GEODE_NUMBER_SEQUENCE() \ 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \ 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, \ 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +#define $execute \ + template \ + void GEODE_CONCAT(geodeExecFunction, __LINE__)(); \ + namespace { \ + struct GEODE_CONCAT(ExecFuncUnique, __LINE__) {}; \ + } \ + static inline auto GEODE_CONCAT(Exec, __LINE__) = \ + (Loader::get()->scheduleOnModLoad( \ + getMod(), \ + &GEODE_CONCAT(geodeExecFunction, __LINE__) < GEODE_CONCAT(ExecFuncUnique, __LINE__) > \ + ), \ + 0); \ + template \ + void GEODE_CONCAT(geodeExecFunction, __LINE__)() + // #define GEODE_NEST1(macro, begin) \ // macro(GEODE_CONCAT(begin, 0)), \ // macro(GEODE_CONCAT(begin, 1)), \ diff --git a/loader/include/Geode/c++stl/gdlib.hpp b/loader/include/Geode/c++stl/gdlib.hpp new file mode 100644 index 00000000..9e0e6fc6 --- /dev/null +++ b/loader/include/Geode/c++stl/gdlib.hpp @@ -0,0 +1,153 @@ +#pragma once + +#include +#include + +namespace geode::stl { + + template + class AllocatorTemplate { + protected: + inline Type* allocate(size_t count); + inline void deallocate(Type* pointer); + }; + +#if defined(GEODE_IS_WINDOWS) + template + class AllocatorBase { + protected: + inline Type* allocate(size_t count) { + + } + inline void deallocate(Type* pointer) { + + } + }; +#elif defined(GEODE_IS_MACOS) || defined(GEODE_IS_ANDROID) + +#elif defined(GEODE_IS_IOS) + +#endif + + + class StringTemplate : public AllocatorBase { + protected: + // depends on already set size and capacity + inline void setStorage(char* pointer) = delete; + inline char* getStorage() = delete; + + inline void setSize(size_t size) = delete; + inline size_t getSize() = delete; + + inline void setCapacity(size_t capacity) = delete; + inline size_t getCapacity() = delete; + }; + +#if defined(GEODE_IS_WINDOWS) + class StringBase : public StringTemplate { + protected: + union { + std::array m_smallStorage; + char* m_bigStorage; + }; + + size_t m_size; + size_t m_capacity; + + inline void setStorage(char* storage) { + if (m_capacity > 15) { + return m_bigStorage = storage; + } + } + inline char* getStorage() { + if (m_capacity > 15) { + return m_bigStorage; + } + return m_smallStorage.data(); + } + + inline void setSize(size_t size) { + m_size = size; + } + inline size_t getSize() { + return m_size; + } + + inline void setCapacity(size_t capacity) { + m_capacity = capacity; + } + inline size_t getCapacity() { + return m_capacity; + } + }; +#elif defined(GEODE_IS_MACOS) || defined(GEODE_IS_ANDROID) + class StringBase : public StringTemplate { + protected: + char* m_storage; + size_t m_size; + size_t m_capacity; + + inline void setStorage(char* storage) { + m_storage = storage; + } + inline char* getStorage() { + return m_storage; + } + + inline void setSize(size_t size) { + m_size = size; + } + inline size_t getSize() { + return m_size; + } + + inline void setCapacity(size_t capacity) { + m_capacity = capacity; + } + inline size_t getCapacity() { + return m_capacity; + } + }; +#elif defined(GEODE_IS_IOS) + class StringBase : public StringTemplate { + protected: + struct Short { + uint8_t sizex2; + std::array shortStorage; + }; + + struct Long { + size_t capacitym1; + size_t size; + char* longStorage; + }; + + union { + Short m_short; + Long m_long; + }; + + inline void setStorage(char* storage) { + m_storage = storage; + } + inline char* getStorage() { + return m_storage; + } + + inline void setSize(size_t size) { + m_size = size; + } + inline size_t getSize() { + return m_size; + } + + inline void setCapacity(size_t capacity) { + m_capacity = capacity; + } + inline size_t getCapacity() { + return m_capacity; + } + }; +#endif + +} \ No newline at end of file diff --git a/loader/include/Geode/c++stl/gnustl.hpp b/loader/include/Geode/c++stl/gnustl.hpp index 398b8bb2..8f1c7bd6 100644 --- a/loader/include/Geode/c++stl/gnustl.hpp +++ b/loader/include/Geode/c++stl/gnustl.hpp @@ -296,84 +296,166 @@ namespace gd { } }; + // template + // using vector = std::vector; + template class GEODE_DLL vector { public: using value_type = T; + auto allocator() const { + return std::allocator(); + } + operator std::vector() const { - std::vector out; - - for (auto i = m_start; i != m_finish; ++i) { - out.push_back(*i); - } - return out; + return std::vector(m_start, m_finish); } - vector(std::vector input) { - std::allocator alloc; - auto tmp = alloc.allocate(input.size()); + vector() { + m_start = nullptr; + m_finish = nullptr; + m_reserveEnd = nullptr; + } - m_start = tmp; - m_finish = m_start + input.size(); - m_capacity_end = m_start + input.size(); - for (auto i : input) { - *tmp = i; - tmp++; + vector(std::vector const& input) : vector() { + if (input.size()) { + m_start = this->allocator().allocate(input.size()); + m_finish = m_start + input.size(); + m_reserveEnd = m_start + input.size(); + + std::copy(input.begin(), input.end(), m_start); } } - vector(std::initializer_list const& input) { - std::allocator alloc; - auto tmp = alloc.allocate(input.size()); - m_start = tmp; - m_finish = m_start + input.size(); - m_capacity_end = m_start + input.size(); - std::copy(input.begin(), input.end(), tmp); + vector(gd::vector const& input) : vector() { + if (input.size()) { + m_start = this->allocator().allocate(input.size()); + m_finish = m_start + input.size(); + m_reserveEnd = m_start + input.size(); + + std::copy(input.begin(), input.end(), m_start); + } } - + + vector(gd::vector&& input) : vector() { + m_start = input.m_start; + m_finish = input.m_finish; + m_reserveEnd = input.m_reserveEnd; + + input.m_start = nullptr; + input.m_finish = nullptr; + input.m_reserveEnd = nullptr; + } + + vector& operator=(gd::vector const& input) { + this->clear(); + + if (input.size()) { + m_start = this->allocator().allocate(input.size()); + m_finish = m_start + input.size(); + m_reserveEnd = m_start + input.size(); + + std::copy(input.begin(), input.end(), m_start); + } + + return *this; + } + + vector& operator=(gd::vector&& input) { + m_start = input.m_start; + m_finish = input.m_finish; + m_reserveEnd = input.m_reserveEnd; + + input.m_start = nullptr; + input.m_finish = nullptr; + input.m_reserveEnd = nullptr; + + return *this; + } + + vector(std::initializer_list const& input) : vector() { + if (input.size()) { + m_start = this->allocator().allocate(input.size()); + m_finish = m_start + input.size(); + m_reserveEnd = m_start + input.size(); + + std::copy(input.begin(), input.end(), m_start); + } + } + void clear() { - std::allocator alloc; - alloc.deallocate(m_start, (m_finish - m_start) / 8); - m_start = 0; - m_finish = 0; - m_capacity_end = 0; + if (m_start) { + std::destroy(m_start, m_finish); + + this->allocator().deallocate(m_start, this->size()); + } + + m_start = nullptr; + m_finish = nullptr; + m_reserveEnd = nullptr; + } + + T& operator[](size_t index) { + return m_start[index]; + } + + T const& operator[](size_t index) const { + return m_start[index]; + } + + T& at(size_t index) { + if (index >= this->size()) { + throw std::out_of_range("gd::vector::at"); + } + return m_start[index]; + } + + T const& at(size_t index) const { + if (index >= this->size()) { + throw std::out_of_range("gd::vector::at"); + } + return m_start[index]; } T& front() { return *m_start; } - auto begin() { + T* begin() { return m_start; } - auto end() { + T* end() { return m_finish; } - auto begin() const { - return static_cast(m_start); + T const* begin() const { + return m_start; } - auto end() const { - return static_cast(m_finish); + T const* end() const { + return m_finish; } - vector(vector const& lol) : vector(std::vector(lol)) {} - - vector() : vector(std::vector()) {} - ~vector() { for (auto i = m_start; i != m_finish; ++i) { delete i; } } + size_t size() const { + return m_finish - m_start; + } + + size_t capacity() const { + return m_reserveEnd - m_start; + } + protected: T* m_start; T* m_finish; - T* m_capacity_end; + T* m_reserveEnd; }; struct _bit_reference { @@ -444,24 +526,32 @@ namespace gd { uintptr_t* m_capacity_end; public: - vector(std::vector input) : m_start(0), m_end(0) { - auto realsize = input.size() / int(sizeof(uintptr_t)); - auto tmp = new uintptr_t[realsize]; - - m_start = _bit_iterator(tmp); - m_end = _bit_iterator(tmp + realsize, input.size() % sizeof(uintptr_t)); - m_capacity_end = tmp + realsize; - - auto itmp = m_start; - for (auto i : input) { - *itmp = i; - ++itmp; - } + auto allocator() const { + return std::allocator(); } - vector(vector const& lol) : vector(std::vector(lol)) {} + vector() : m_start(nullptr), m_end(nullptr), m_capacity_end(nullptr) {} - vector() : vector(std::vector()) {} + // vector(std::vector input) : vector() { + // auto realsize = input.size() / int(sizeof(uintptr_t)); + // auto start = this->allocator().allocate(realsize); + + // m_start = _bit_iterator(start); + // m_end = _bit_iterator(start + realsize, input.size() % sizeof(uintptr_t)); + // m_capacity_end = start + realsize; + + // auto itmp = m_start; + // for (auto i : input) { + // *itmp = i; + // ++itmp; + // } + // } + + // vector(vector const& input) : vector() { + + // } + + // vector() : vector(std::vector()) {} ~vector() { delete[] m_start.m_bitptr; @@ -476,8 +566,8 @@ namespace gd { } _bit_reference operator[](size_t index) { - const auto real_index = index / sizeof(uintptr_t); - const auto offset = index % sizeof(uintptr_t); + auto const real_index = index / sizeof(uintptr_t); + auto const offset = index % sizeof(uintptr_t); return _bit_reference(&m_start.m_bitptr[real_index], 1UL << offset); } @@ -535,7 +625,7 @@ namespace gd { operator std::vector() { return m_internal; } - + void clear() { m_internal.clear(); } diff --git a/loader/include/Geode/external/result/result.hpp b/loader/include/Geode/external/result/result.hpp new file mode 100644 index 00000000..f2ac2802 --- /dev/null +++ b/loader/include/Geode/external/result/result.hpp @@ -0,0 +1,5994 @@ +//////////////////////////////////////////////////////////////////////////////// +/// \file result.hpp +/// +/// \brief This header contains the 'result' monadic type for indicating +/// possible error conditions +//////////////////////////////////////////////////////////////////////////////// + +/* + The MIT License (MIT) + + Copyright (c) 2017-2021 Matthew Rodusek All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef RESULT_RESULT_HPP +#define RESULT_RESULT_HPP + +#include // std::size_t +#include // std::enable_if, std::is_constructible, etc +#include // placement-new +#include // std::address_of +#include // std::reference_wrapper, std::invoke +#include // std::in_place_t, std::forward +#include // std::initializer_list +#include // std::string (for exception message) + +#if defined(RESULT_EXCEPTIONS_DISABLED) +# include // std::fprintf, stderr +#else +# include // std::logic_error +#endif + +#if __cplusplus >= 201402L +# define RESULT_CPP14_CONSTEXPR constexpr +#else +# define RESULT_CPP14_CONSTEXPR +#endif + +#if __cplusplus >= 201703L +# define RESULT_CPP17_INLINE inline +#else +# define RESULT_CPP17_INLINE +#endif + +#if defined(__clang__) && defined(_MSC_VER) +# define RESULT_INLINE_VISIBILITY __attribute__((visibility("hidden"))) +#elif defined(__clang__) || defined(__GNUC__) +# define RESULT_INLINE_VISIBILITY __attribute__((visibility("hidden"), always_inline)) +#elif defined(_MSC_VER) +# define RESULT_INLINE_VISIBILITY __forceinline +#else +# define RESULT_INLINE_VISIBILITY +#endif + +// [[clang::warn_unused_result]] is more full-featured than gcc's variant, since +// it supports being applied to class objects. +#if __cplusplus >= 201703L +# define RESULT_NODISCARD [[nodiscard]] +# define RESULT_WARN_UNUSED [[nodiscard]] +#elif defined(__clang__) && ((__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ >= 9))) +# define RESULT_NODISCARD [[clang::warn_unused_result]] +# define RESULT_WARN_UNUSED [[clang::warn_unused_result]] +#elif defined(__GNUC__) +# define RESULT_NODISCARD +# define RESULT_WARN_UNUSED [[gnu::warn_unused_result]] +#else +# define RESULT_WARN_UNUSED +# define RESULT_NODISCARD +#endif + +#if defined(RESULT_NAMESPACE) +# define RESULT_NAMESPACE_INTERNAL RESULT_NAMESPACE +#else +# define RESULT_NAMESPACE_INTERNAL cpp +#endif +#define RESULT_NS_IMPL RESULT_NAMESPACE_INTERNAL::bitwizeshift + +// clang's `-Wdocumentation-unknown-command` flag is bugged and does not +// understand `\copydoc` tags, despite this being a valid doxygen tag. +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +namespace RESULT_NAMESPACE_INTERNAL { +inline namespace bitwizeshift { + + //=========================================================================== + // utilities : constexpr forward + //=========================================================================== + + // std::forward is not constexpr until C++14 + namespace detail { +#if __cplusplus >= 201402L + using std::forward; +#else + template + inline RESULT_INLINE_VISIBILITY constexpr + auto forward(typename std::remove_reference::type& t) + noexcept -> T&& + { + return static_cast(t); + } + + template + inline RESULT_INLINE_VISIBILITY constexpr + auto forward(typename std::remove_reference::type&& t) + noexcept -> T&& + { + return static_cast(t); + } +#endif + } // namespace detail + + + //=========================================================================== + // utilities : invoke / invoke_result + //=========================================================================== + + // std::invoke was introduced in C++17 + + namespace detail { +#if __cplusplus >= 201703L + using std::invoke; + using std::invoke_result; + using std::invoke_result_t; +#else + template + struct is_reference_wrapper : std::false_type{}; + + template + struct is_reference_wrapper> : std::true_type{}; + + //------------------------------------------------------------------------- + + template ::value && + std::is_base_of::type>::value + >::type> + inline RESULT_INLINE_VISIBILITY constexpr + auto invoke(T Base::*pmf, Derived&& ref, Args&&... args) + noexcept(noexcept((::RESULT_NS_IMPL::detail::forward(ref).*pmf)(::RESULT_NS_IMPL::detail::forward(args)...))) + -> decltype((::RESULT_NS_IMPL::detail::forward(ref).*pmf)(::RESULT_NS_IMPL::detail::forward(args)...)) + { + return (RESULT_NS_IMPL::detail::forward(ref).*pmf)(RESULT_NS_IMPL::detail::forward(args)...); + } + + template ::value && + is_reference_wrapper::type>::value + >::type> + inline RESULT_INLINE_VISIBILITY constexpr + auto invoke(T Base::*pmf, RefWrap&& ref, Args&&... args) + noexcept(noexcept((ref.get().*pmf)(std::forward(args)...))) + -> decltype((ref.get().*pmf)(RESULT_NS_IMPL::detail::forward(args)...)) + { + return (ref.get().*pmf)(RESULT_NS_IMPL::detail::forward(args)...); + } + + template ::value && + !is_reference_wrapper::type>::value && + !std::is_base_of::type>::value + >::type> + inline RESULT_INLINE_VISIBILITY constexpr + auto invoke(T Base::*pmf, Pointer&& ptr, Args&&... args) + noexcept(noexcept(((*std::forward(ptr)).*pmf)(std::forward(args)...))) + -> decltype(((*RESULT_NS_IMPL::detail::forward(ptr)).*pmf)(RESULT_NS_IMPL::detail::forward(args)...)) + { + return ((*RESULT_NS_IMPL::detail::forward(ptr)).*pmf)(RESULT_NS_IMPL::detail::forward(args)...); + } + + template ::value && + std::is_base_of::type>::value + >::type> + inline RESULT_INLINE_VISIBILITY constexpr + auto invoke(T Base::*pmd, Derived&& ref) + noexcept(noexcept(std::forward(ref).*pmd)) + -> decltype(RESULT_NS_IMPL::detail::forward(ref).*pmd) + { + return RESULT_NS_IMPL::detail::forward(ref).*pmd; + } + + template ::value && + is_reference_wrapper::type>::value + >::type> + inline RESULT_INLINE_VISIBILITY constexpr + auto invoke(T Base::*pmd, RefWrap&& ref) + noexcept(noexcept(ref.get().*pmd)) + -> decltype(ref.get().*pmd) + { + return ref.get().*pmd; + } + + template ::value && + !is_reference_wrapper::type>::value && + !std::is_base_of::type>::value + >::type> + inline RESULT_INLINE_VISIBILITY constexpr + auto invoke(T Base::*pmd, Pointer&& ptr) + noexcept(noexcept((*std::forward(ptr)).*pmd)) + -> decltype((*RESULT_NS_IMPL::detail::forward(ptr)).*pmd) + { + return (*RESULT_NS_IMPL::detail::forward(ptr)).*pmd; + } + + template ::type>::value>::type> + inline RESULT_INLINE_VISIBILITY constexpr + auto invoke(F&& f, Args&&... args) + noexcept(noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(RESULT_NS_IMPL::detail::forward(f)(RESULT_NS_IMPL::detail::forward(args)...)) + { + return RESULT_NS_IMPL::detail::forward(f)(RESULT_NS_IMPL::detail::forward(args)...); + } + + template + struct is_invocable + { + template + static auto test( Fn2&&, Args2&&... ) + -> decltype(invoke(std::declval(), std::declval()...), std::true_type{}); + + static auto test(...) + -> std::false_type; + + using type = decltype(test(std::declval(), std::declval()...)); + static constexpr bool value = type::value; + }; + + template + struct invoke_result_impl { + using type = decltype(RESULT_NS_IMPL::detail::invoke(std::declval(), std::declval()...)); + }; + template + struct invoke_result_impl{}; + + template + struct invoke_result + : invoke_result_impl::value, Fn, Args...>{}; + + template + using invoke_result_t = typename invoke_result::type; +#endif + } + + //=========================================================================== + // struct : in_place_t + //=========================================================================== + +#if __cplusplus >= 201703L + using std::in_place_t; + using std::in_place; +#else + /// \brief A structure for representing in-place construction + struct in_place_t + { + explicit in_place_t() = default; + }; + RESULT_CPP17_INLINE constexpr auto in_place = in_place_t{}; +#endif + + //=========================================================================== + // struct : in_place_t + //=========================================================================== + + /// \brief A structure for representing in-place construction of an error type + struct in_place_error_t + { + explicit in_place_error_t() = default; + }; + + RESULT_CPP17_INLINE constexpr auto in_place_error = in_place_error_t{}; + + //=========================================================================== + // forward-declarations + //=========================================================================== + + template + class failure; + + template + class result; + + template + class bad_result_access; + + //=========================================================================== + // traits + //=========================================================================== + + template + struct is_failure : std::false_type{}; + template + struct is_failure> : std::true_type{}; + + template + struct is_result : std::false_type{}; + template + struct is_result> : std::true_type{}; + + //=========================================================================== + // trait : detail::wrapped_result_type + //=========================================================================== + + namespace detail { + + template + using wrapped_result_type = typename std::conditional< + std::is_lvalue_reference::value, + std::reference_wrapper< + typename std::remove_reference::type + >, + typename std::remove_const::type + >::type; + + } // namespace detail + +#if !defined(RESULT_DISABLE_EXCEPTIONS) + + //=========================================================================== + // class : bad_result_access + //=========================================================================== + + ///////////////////////////////////////////////////////////////////////////// + /// \brief An exception thrown when result::value is accessed without + /// a contained value + ///////////////////////////////////////////////////////////////////////////// + template + class bad_result_access : public std::logic_error + { + //------------------------------------------------------------------------- + // Constructor / Assignment + //------------------------------------------------------------------------- + public: + + /// \brief Constructs this exception using the underlying error type for + /// the error type + /// + /// \param error the underlying error + template ::value>::type> + explicit bad_result_access(E2&& error); + + /// \{ + /// \brief Constructs this exception using the underlying error type for + /// the error and a message + /// + /// \param what_arg the message for the failure + /// \param error the underlying error + template ::value>::type> + bad_result_access(const char* what_arg, E2&& error); + template ::value>::type> + bad_result_access(const std::string& what_arg, E2&& error); + /// \} + + bad_result_access(const bad_result_access& other) = default; + bad_result_access(bad_result_access&& other) = default; + + //------------------------------------------------------------------------- + + auto operator=(const bad_result_access& other) -> bad_result_access& = default; + auto operator=(bad_result_access&& other) -> bad_result_access& = default; + + /// \{ + /// \brief Gets the underlying error + /// + /// \return the error + auto error() & noexcept -> E&; + auto error() && noexcept -> E&&; + auto error() const & noexcept -> const E&; + auto error() const && noexcept -> const E&&; + /// \} + + //------------------------------------------------------------------------- + // Private Members + //------------------------------------------------------------------------- + private: + + E m_error; + }; + +#endif + + namespace detail { + + template + using failure_is_value_convertible = std::integral_constant::value && + !std::is_same::type, in_place_t>::value && + !is_failure::type>::value && + !is_result::type>::value + )>; + + template + using failure_is_explicit_value_convertible = std::integral_constant::value && + !std::is_convertible::value + )>; + + template + using failure_is_implicit_value_convertible = std::integral_constant::value && + std::is_convertible::value + )>; + + template + using failure_is_value_assignable = std::integral_constant::type>::value && + !is_failure::type>::value && + std::is_assignable&,E2>::value + )>; + + } // namespace detail + + //=========================================================================== + // class : failure_type + //=========================================================================== + + ////////////////////////////////////////////////////////////////////////////// + /// \brief A semantic type used for distinguishing failure values in an + /// API that returns result types + /// + /// \tparam E the error type + ////////////////////////////////////////////////////////////////////////////// + template + class failure + { + static_assert( + !is_result::type>::value, + "A (possibly CV-qualified) result 'E' type is ill-formed." + ); + static_assert( + !is_failure::type>::value, + "A (possibly CV-qualified) failure 'E' type is ill-formed." + ); + static_assert( + !std::is_void::type>::value, + "A (possibly CV-qualified) 'void' 'E' type is ill-formed." + ); + static_assert( + !std::is_rvalue_reference::value, + "rvalue references for 'E' type is ill-formed. " + "Only lvalue references are valid." + ); + + //------------------------------------------------------------------------- + // Public Member Types + //------------------------------------------------------------------------- + public: + + using error_type = E; + + //------------------------------------------------------------------------- + // Constructors / Assignment + //------------------------------------------------------------------------- + public: + + /// \brief Constructs a failure via default construction + failure() = default; + + /// \brief Constructs a failure by delegating construction to the + /// underlying constructor + /// + /// \param args the arguments to forward to E's constructor + template ::value>::type> + constexpr failure(in_place_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value); + + /// \brief Constructs a failure by delegating construction to the + /// underlying constructor + /// + /// \param ilist the initializer list + /// \param args the arguments to forward to E's constructor + template ,Args...>::value>::type> + constexpr failure(in_place_t, std::initializer_list ilist, Args&&...args) + noexcept(std::is_nothrow_constructible, Args...>::value); + + /// \{ + /// \brief Constructs a failure from the given error + /// + /// \param error the error to create a failure from + template ::value,int>::type = 0> + constexpr failure(E2&& error) + noexcept(std::is_nothrow_constructible::value); + template ::value,int>::type = 0> + constexpr explicit failure(E2&& error) + noexcept(std::is_nothrow_constructible::value); + /// \} + + /// \brief Constructs this failure by copying the contents of an existing + /// one + /// + /// \param other the other failure to copy + /* implicit */ failure(const failure& other) = default; + + /// \brief Constructs this failure by moving the contents of an existing + /// one + /// + /// \param other the other failure to move + /* implicit */ failure(failure&& other) = default; + + /// \brief Constructs this failure by copy-converting \p other + /// + /// \param other the other failure to copy + template ::value>::type> + constexpr /* implicit */ failure(const failure& other) + noexcept(std::is_nothrow_constructible::value); + + /// \brief Constructs this failure by move-converting \p other + /// + /// \param other the other failure to copy + template ::value>::type> + constexpr /* implicit */ failure(failure&& other) + noexcept(std::is_nothrow_constructible::value); + + //-------------------------------------------------------------------------- + + /// \brief Assigns the value of \p error to this failure through + /// move-assignment + /// + /// \param error the value to assign + /// \return reference to `(*this)` + template ::value>::type> + RESULT_CPP14_CONSTEXPR + auto operator=(E2&& error) + noexcept(std::is_nothrow_assignable::value || std::is_lvalue_reference::value) -> failure&; + + /// \brief Assigns the contents of \p other to this by copy-assignment + /// + /// \param other the other failure to copy + /// \return reference to `(*this)` + auto operator=(const failure& other) -> failure& = default; + + /// \brief Assigns the contents of \p other to this by move-assignment + /// + /// \param other the other failure to move + /// \return reference to `(*this)` + auto operator=(failure&& other) -> failure& = default; + + /// \brief Assigns the contents of \p other to this by copy conversion + /// + /// \param other the other failure to copy-convert + /// \return reference to `(*this)` + template ::value>::type> + RESULT_CPP14_CONSTEXPR + auto operator=(const failure& other) + noexcept(std::is_nothrow_assignable::value) -> failure&; + + /// \brief Assigns the contents of \p other to this by move conversion + /// + /// \param other the other failure to move-convert + /// \return reference to `(*this)` + template ::value>::type> + RESULT_CPP14_CONSTEXPR + auto operator=(failure&& other) + noexcept(std::is_nothrow_assignable::value) -> failure&; + + //-------------------------------------------------------------------------- + // Observers + //-------------------------------------------------------------------------- + public: + + /// \{ + /// \brief Gets the underlying error + /// + /// \return the underlying error + RESULT_CPP14_CONSTEXPR + auto error() & noexcept + -> typename std::add_lvalue_reference::type; + RESULT_CPP14_CONSTEXPR + auto error() && noexcept + -> typename std::add_rvalue_reference::type; + constexpr auto error() const & noexcept + -> typename std::add_lvalue_reference::type>::type; + constexpr auto error() const && noexcept + -> typename std::add_rvalue_reference::type>::type; + /// \} + + //------------------------------------------------------------------------- + // Private Member Types + //------------------------------------------------------------------------- + private: + + using underlying_type = detail::wrapped_result_type; + + //------------------------------------------------------------------------- + // Private Members + //------------------------------------------------------------------------- + private: + + underlying_type m_failure; + }; + +#if __cplusplus >= 201703L + template + failure(std::reference_wrapper) -> failure; + + template + failure(T&&) -> failure::type>; +#endif + + //=========================================================================== + // non-member functions : class : failure + //=========================================================================== + + //--------------------------------------------------------------------------- + // Comparison + //--------------------------------------------------------------------------- + + template + constexpr auto operator==(const failure& lhs, + const failure& rhs) noexcept -> bool; + template + constexpr auto operator!=(const failure& lhs, + const failure& rhs) noexcept -> bool; + template + constexpr auto operator<(const failure& lhs, + const failure& rhs) noexcept -> bool; + template + constexpr auto operator>(const failure& lhs, + const failure& rhs) noexcept -> bool; + template + constexpr auto operator<=(const failure& lhs, + const failure& rhs) noexcept -> bool; + template + constexpr auto operator>=(const failure& lhs, + const failure& rhs) noexcept -> bool; + + //--------------------------------------------------------------------------- + // Utilities + //--------------------------------------------------------------------------- + + /// \brief Deduces and constructs a failure type from \p e + /// + /// \param e the failure value + /// \return a constructed failure value + template + RESULT_WARN_UNUSED + constexpr auto fail(E&& e) + noexcept(std::is_nothrow_constructible::type,E>::value) + -> failure::type>; + + /// \brief Deduces a failure reference from a reverence_wrapper + /// + /// \param e the failure value + /// \return a constructed failure reference + template + RESULT_WARN_UNUSED + constexpr auto fail(std::reference_wrapper e) + noexcept -> failure; + + /// \brief Constructs a failure type from a series of arguments + /// + /// \tparam E the failure type + /// \param args the arguments to forward to E's constructor + /// \return a constructed failure type + template ::value>::type> + RESULT_WARN_UNUSED + constexpr auto fail(Args&&...args) + noexcept(std::is_nothrow_constructible::value) + -> failure; + + /// \brief Constructs a failure type from an initializer list and series of + /// arguments + /// + /// \tparam E the failure type + /// \param args the arguments to forward to E's constructor + /// \return a constructed failure type + template ,Args...>::value>::type> + RESULT_WARN_UNUSED + constexpr auto fail(std::initializer_list ilist, Args&&...args) + noexcept(std::is_nothrow_constructible, Args...>::value) + -> failure; + + /// \brief Swaps the contents of two failure values + /// + /// \param lhs the left failure + /// \param rhs the right failure + template + auto swap(failure& lhs, failure& rhs) +#if __cplusplus >= 201703L + noexcept(std::is_nothrow_swappable::value) -> void; +#else + noexcept(std::is_nothrow_move_constructible::value) -> void; +#endif + + namespace detail { + + //========================================================================= + // class : unit + //========================================================================= + + /// \brief A standalone monostate object (effectively std::monostate). This + /// exists to allow for `void` specializations + struct unit {}; + + //========================================================================= + // non-member functions : class : unit + //========================================================================= + + constexpr auto operator==(unit, unit) noexcept -> bool { return true; } + constexpr auto operator!=(unit, unit) noexcept -> bool { return false; } + constexpr auto operator<(unit, unit) noexcept -> bool { return false; } + constexpr auto operator>(unit, unit) noexcept -> bool { return false; } + constexpr auto operator<=(unit, unit) noexcept -> bool { return true; } + constexpr auto operator>=(unit, unit) noexcept -> bool { return true; } + + //========================================================================= + // class : detail::result_union + //========================================================================= + + /////////////////////////////////////////////////////////////////////////// + /// \brief A basic utility that acts as a union containing the T and E + /// types + /// + /// This is specialized on the case that both T and E are trivial, in which + /// case `result_union` is also trivial + /// + /// \tparam T the value type result to be returned + /// \tparam E the error type returned on failure + /// \tparam IsTrivial Whether or not both T and E are trivial + /////////////////////////////////////////////////////////////////////////// + template ::value && + std::is_trivially_destructible::value> + struct result_union + { + //----------------------------------------------------------------------- + // Public Member Types + //----------------------------------------------------------------------- + + using underlying_value_type = wrapped_result_type; + using underlying_error_type = E; + + //----------------------------------------------------------------------- + // Constructors / Assignment + //----------------------------------------------------------------------- + + /// \brief Constructs an empty object + /// + /// This is for use with conversion constructors, since it allows a + /// temporary unused object to be set + result_union(unit) noexcept; + + /// \brief Constructs the underlying value from the specified \p args + /// + /// \param args the arguments to forward to T's constructor + template + constexpr result_union(in_place_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value); + + /// \brief Constructs the underlying error from the specified \p args + /// + /// \param args the arguments to forward to E's constructor + template + constexpr result_union(in_place_error_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value); + + result_union(const result_union&) = default; + result_union(result_union&&) = default; + + //----------------------------------------------------------------------- + + auto operator=(const result_union&) -> result_union& = default; + auto operator=(result_union&&) -> result_union& = default; + + //----------------------------------------------------------------------- + // Modifiers + //----------------------------------------------------------------------- + + /// \brief A no-op for trivial types + auto destroy() const noexcept -> void; + + //----------------------------------------------------------------------- + // Public Members + //----------------------------------------------------------------------- + + union { + underlying_value_type m_value; + underlying_error_type m_error; + unit m_empty; + }; + bool m_has_value; + }; + + //------------------------------------------------------------------------- + + template + struct result_union + { + //----------------------------------------------------------------------- + // Public Member Types + //----------------------------------------------------------------------- + + using underlying_value_type = wrapped_result_type; + using underlying_error_type = E; + + //----------------------------------------------------------------------- + // Constructors / Assignment / Destructor + //----------------------------------------------------------------------- + + /// \brief Constructs an empty object + /// + /// This is for use with conversion constructors, since it allows a + /// temporary unused object to be set + result_union(unit) noexcept; + + /// \brief Constructs the underlying value from the specified \p args + /// + /// \param args the arguments to forward to T's constructor + template + constexpr result_union(in_place_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value); + + /// \brief Constructs the underlying error from the specified \p args + /// + /// \param args the arguments to forward to E's constructor + template + constexpr result_union(in_place_error_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value); + + result_union(const result_union&) = default; + result_union(result_union&&) = default; + + //----------------------------------------------------------------------- + + /// \brief Destroys the underlying stored object + ~result_union() + noexcept(std::is_nothrow_destructible::value && + std::is_nothrow_destructible::value); + + //----------------------------------------------------------------------- + + auto operator=(const result_union&) -> result_union& = default; + auto operator=(result_union&&) -> result_union& = default; + + //----------------------------------------------------------------------- + // Modifiers + //----------------------------------------------------------------------- + + /// \brief Destroys the underlying stored object + auto destroy() -> void; + + //----------------------------------------------------------------------- + // Public Members + //----------------------------------------------------------------------- + + union { + underlying_value_type m_value; + underlying_error_type m_error; + unit m_empty; + }; + bool m_has_value; + }; + + //========================================================================= + // class : result_construct_base + //========================================================================= + + /////////////////////////////////////////////////////////////////////////// + /// \brief Base class of assignment to enable construction and assignment + /// + /// This class is used with several pieces of construction to ensure + /// trivial constructibility and assignability: + /// + /// * `result_trivial_copy_ctor_base` + /// * `result_trivial_move_ctor_base` + /// * `result_copy_assign_base` + /// * `result_move_assign_base` + /// + /// \tparam T the value type + /// \tparam E the error type + /////////////////////////////////////////////////////////////////////////// + template + struct result_construct_base + { + //----------------------------------------------------------------------- + // Constructors / Assignment + //----------------------------------------------------------------------- + + /// \brief Constructs an empty object + /// + /// This is for use with conversion constructors, since it allows a + /// temporary unused object to be set + result_construct_base(unit) noexcept; + + /// \brief Constructs the underlying value from the specified \p args + /// + /// \param args the arguments to forward to T's constructor + template + constexpr result_construct_base(in_place_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value); + + /// \brief Constructs the underlying error from the specified \p args + /// + /// \param args the arguments to forward to E's constructor + template + constexpr result_construct_base(in_place_error_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value); + + result_construct_base(const result_construct_base&) = default; + result_construct_base(result_construct_base&&) = default; + + auto operator=(const result_construct_base&) -> result_construct_base& = default; + auto operator=(result_construct_base&&) -> result_construct_base& = default; + + //----------------------------------------------------------------------- + // Construction / Assignment + //----------------------------------------------------------------------- + + /// \brief Constructs the value type from \p args + /// + /// \note This is an implementation detail only meant to be used during + /// construction + /// + /// \pre there is no contained value or error at the time of construction + /// + /// \param args the arguments to forward to T's constructor + template + auto construct_value(Args&&...args) + noexcept(std::is_nothrow_constructible::value) -> void; + + /// \brief Constructs the error type from \p args + /// + /// \note This is an implementation detail only meant to be used during + /// construction + /// + /// \pre there is no contained value or error at the time of construction + /// + /// \param args the arguments to forward to E's constructor + template + auto construct_error(Args&&...args) + noexcept(std::is_nothrow_constructible::value) -> void; + + /// \brief Constructs the underlying error from the \p other result + /// + /// If \p other contains a value, then the T type will be + /// default-constructed. + /// + /// \note This is an implementation detail only meant to be used during + /// construction of `result` types + /// + /// \pre there is no contained value or error at the time of construction + /// + /// \param other the other result to construct + template + auto construct_error_from_result(Result&& other) -> void; + + /// \brief Constructs the underlying type from a result object + /// + /// \note This is an implementation detail only meant to be used during + /// construction + /// + /// \pre there is no contained value or error at the time of construction + /// + /// \param other the other result to construct + template + auto construct_from_result(Result&& other) -> void; + + //----------------------------------------------------------------------- + + template + auto assign_value(Value&& value) + noexcept(std::is_nothrow_assignable::value) -> void; + + template + auto assign_error(Error&& error) + noexcept(std::is_nothrow_assignable::value) -> void; + + template + auto assign_from_result(Result&& other) -> void; + + //----------------------------------------------------------------------- + + template + auto construct_value_from_result_impl(std::true_type, ReferenceWrapper&& reference) + noexcept -> void; + + template + auto construct_value_from_result_impl(std::false_type, Value&& value) + noexcept(std::is_nothrow_constructible::value) -> void; + + template + auto assign_value_from_result_impl(std::true_type, Result&& other) -> void; + + template + auto assign_value_from_result_impl(std::false_type, Result&& other) -> void; + + //----------------------------------------------------------------------- + // Public Members + //----------------------------------------------------------------------- + + using storage_type = result_union; + + storage_type storage; + }; + + //========================================================================= + // class : result_trivial_copy_ctor_base + //========================================================================= + + template + struct result_trivial_copy_ctor_base_impl : result_construct_base + { + using base_type = result_construct_base; + using base_type::base_type; + + result_trivial_copy_ctor_base_impl(const result_trivial_copy_ctor_base_impl& other) + noexcept(std::is_nothrow_copy_constructible::value && + std::is_nothrow_copy_constructible::value); + result_trivial_copy_ctor_base_impl(result_trivial_copy_ctor_base_impl&& other) = default; + + auto operator=(const result_trivial_copy_ctor_base_impl& other) -> result_trivial_copy_ctor_base_impl& = default; + auto operator=(result_trivial_copy_ctor_base_impl&& other) -> result_trivial_copy_ctor_base_impl& = default; + }; + + template + using conditionally_nest_type = typename std::conditional< + Condition, + typename Base::base_type, + Base + >::type; + + template + using result_trivial_copy_ctor_base = conditionally_nest_type< + std::is_trivially_copy_constructible::value && + std::is_trivially_copy_constructible::value, + result_trivial_copy_ctor_base_impl + >; + + //========================================================================= + // class : result_trivial_move_ctor_base + //========================================================================= + + template + struct result_trivial_move_ctor_base_impl : result_trivial_copy_ctor_base + { + using base_type = result_trivial_copy_ctor_base; + using base_type::base_type; + + result_trivial_move_ctor_base_impl(const result_trivial_move_ctor_base_impl& other) = default; + result_trivial_move_ctor_base_impl(result_trivial_move_ctor_base_impl&& other) + noexcept(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value); + + auto operator=(const result_trivial_move_ctor_base_impl& other) -> result_trivial_move_ctor_base_impl& = default; + auto operator=(result_trivial_move_ctor_base_impl&& other) -> result_trivial_move_ctor_base_impl& = default; + }; + + template + using result_trivial_move_ctor_base = conditionally_nest_type< + std::is_trivially_move_constructible::value && + std::is_trivially_move_constructible::value, + result_trivial_move_ctor_base_impl + >; + + //========================================================================= + // class : result_trivial_copy_assign_base + //========================================================================= + + template + struct result_trivial_copy_assign_base_impl + : result_trivial_move_ctor_base + { + using base_type = result_trivial_move_ctor_base; + using base_type::base_type; + + result_trivial_copy_assign_base_impl(const result_trivial_copy_assign_base_impl& other) = default; + result_trivial_copy_assign_base_impl(result_trivial_copy_assign_base_impl&& other) = default; + + auto operator=(const result_trivial_copy_assign_base_impl& other) + noexcept(std::is_nothrow_copy_constructible::value && + std::is_nothrow_copy_constructible::value && + std::is_nothrow_copy_assignable::value && + std::is_nothrow_copy_assignable::value) + -> result_trivial_copy_assign_base_impl&; + auto operator=(result_trivial_copy_assign_base_impl&& other) + -> result_trivial_copy_assign_base_impl& = default; + }; + + template + using result_trivial_copy_assign_base = conditionally_nest_type< + std::is_trivially_copy_constructible::value && + std::is_trivially_copy_constructible::value && + std::is_trivially_copy_assignable::value && + std::is_trivially_copy_assignable::value && + std::is_trivially_destructible::value && + std::is_trivially_destructible::value, + result_trivial_copy_assign_base_impl + >; + + //========================================================================= + // class : result_trivial_move_assign_base + //========================================================================= + + template + struct result_trivial_move_assign_base_impl + : result_trivial_copy_assign_base + { + using base_type = result_trivial_copy_assign_base; + using base_type::base_type; + + result_trivial_move_assign_base_impl(const result_trivial_move_assign_base_impl& other) = default; + result_trivial_move_assign_base_impl(result_trivial_move_assign_base_impl&& other) = default; + + auto operator=(const result_trivial_move_assign_base_impl& other) + -> result_trivial_move_assign_base_impl& = default; + auto operator=(result_trivial_move_assign_base_impl&& other) + noexcept(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value && + std::is_nothrow_move_assignable::value) + -> result_trivial_move_assign_base_impl&; + }; + + template + using result_trivial_move_assign_base = conditionally_nest_type< + std::is_trivially_move_constructible::value && + std::is_trivially_move_constructible::value && + std::is_trivially_move_assignable::value && + std::is_trivially_move_assignable::value && + std::is_trivially_destructible::value && + std::is_trivially_destructible::value, + result_trivial_move_assign_base_impl + >; + + //========================================================================= + // class : disable_copy_ctor + //========================================================================= + + template + struct disable_copy_ctor : result_trivial_move_assign_base + { + using base_type = result_trivial_move_assign_base; + using base_type::base_type; + + disable_copy_ctor(const disable_copy_ctor& other) = delete; + disable_copy_ctor(disable_copy_ctor&& other) = default; + + auto operator=(const disable_copy_ctor& other) + -> disable_copy_ctor& = default; + auto operator=(disable_copy_ctor&& other) + -> disable_copy_ctor& = default; + }; + + template + using result_copy_ctor_base = conditionally_nest_type< + std::is_copy_constructible::value && + std::is_copy_constructible::value, + disable_copy_ctor + >; + + //========================================================================= + // class : disable_move_ctor + //========================================================================= + + template + struct disable_move_ctor : result_copy_ctor_base + { + using base_type = result_copy_ctor_base; + using base_type::base_type; + + disable_move_ctor(const disable_move_ctor& other) = default; + disable_move_ctor(disable_move_ctor&& other) = delete; + + auto operator=(const disable_move_ctor& other) + -> disable_move_ctor& = default; + auto operator=(disable_move_ctor&& other) + -> disable_move_ctor& = default; + }; + + template + using result_move_ctor_base = conditionally_nest_type< + std::is_move_constructible::value && + std::is_move_constructible::value, + disable_move_ctor + >; + + //========================================================================= + // class : disable_move_assignment + //========================================================================= + + template + struct disable_move_assignment + : result_move_ctor_base + { + using base_type = result_move_ctor_base; + using base_type::base_type; + + disable_move_assignment(const disable_move_assignment& other) = default; + disable_move_assignment(disable_move_assignment&& other) = default; + + auto operator=(const disable_move_assignment& other) + -> disable_move_assignment& = delete; + auto operator=(disable_move_assignment&& other) + -> disable_move_assignment& = default; + }; + + template + using result_copy_assign_base = conditionally_nest_type< + std::is_nothrow_copy_constructible::value && + std::is_nothrow_copy_constructible::value && + std::is_copy_assignable>::value && + std::is_copy_assignable::value, + disable_move_assignment + >; + + //========================================================================= + // class : disable_copy_assignment + //========================================================================= + + template + struct disable_copy_assignment + : result_copy_assign_base + { + using base_type = result_copy_assign_base; + using base_type::base_type; + + disable_copy_assignment(const disable_copy_assignment& other) = default; + disable_copy_assignment(disable_copy_assignment&& other) = default; + + auto operator=(const disable_copy_assignment& other) + -> disable_copy_assignment& = default; + auto operator=(disable_copy_assignment&& other) + -> disable_copy_assignment& = delete; + }; + + template + using result_move_assign_base = conditionally_nest_type< + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value && + std::is_move_assignable>::value && + std::is_move_assignable::value, + disable_copy_assignment + >; + + //========================================================================= + // alias : result_storage + //========================================================================= + + template + using result_storage = result_move_assign_base; + + //========================================================================= + // traits : result + //========================================================================= + + template + using result_is_convertible = std::integral_constant + std::is_constructible&>:: value || + std::is_constructible&>:: value || + std::is_constructible&&>:: value || + std::is_constructible&&>:: value || + + // E1 constructible from result + std::is_constructible&>:: value || + std::is_constructible&>:: value || + std::is_constructible&&>:: value || + std::is_constructible&&>:: value || + + // result convertible to T1 + std::is_convertible&, T1>:: value || + std::is_convertible&, T1>:: value || + std::is_convertible&&, T1>::value || + std::is_convertible&&, T1>::value || + + // result convertible to E2 + std::is_convertible&, E1>:: value || + std::is_convertible&, E1>:: value || + std::is_convertible&&, E1>::value || + std::is_convertible&&, E1>::value + )>; + + //------------------------------------------------------------------------- + + template + using result_is_copy_convertible = std::integral_constant::value && + std::is_constructible::value && + std::is_constructible::value + )>; + + template + using result_is_implicit_copy_convertible = std::integral_constant::value && + std::is_convertible::value && + std::is_convertible::value + )>; + + template + using result_is_explicit_copy_convertible = std::integral_constant::value && + (!std::is_convertible::value || + !std::is_convertible::value) + )>; + + //------------------------------------------------------------------------- + + template + using result_is_move_convertible = std::integral_constant::value && + std::is_constructible::value && + std::is_constructible::value + )>; + + template + using result_is_implicit_move_convertible = std::integral_constant::value && + std::is_convertible::value && + std::is_convertible::value + )>; + + template + using result_is_explicit_move_convertible = std::integral_constant::value && + (!std::is_convertible::value || + !std::is_convertible::value) + )>; + + //------------------------------------------------------------------------- + + template + using result_is_value_convertible = std::integral_constant::value && + !std::is_same::type, in_place_t>::value && + !std::is_same::type, in_place_error_t>::value && + !is_result::type>::value + )>; + + template + using result_is_explicit_value_convertible = std::integral_constant::value && + !std::is_convertible::value + )>; + + template + using result_is_implicit_value_convertible = std::integral_constant::value && + std::is_convertible::value + )>; + + //------------------------------------------------------------------------- + + template + using result_is_convert_assignable = std::integral_constant::value && + + std::is_assignable&>::value && + std::is_assignable&>::value && + std::is_assignable&&>::value && + std::is_assignable&&>::value && + + std::is_assignable&>::value && + std::is_assignable&>::value && + std::is_assignable&&>::value && + std::is_assignable&&>::value + )>; + + template + using result_is_copy_convert_assignable = std::integral_constant::value && + + std::is_nothrow_constructible::value && + std::is_assignable&, const T2&>::value && + std::is_nothrow_constructible::value && + std::is_assignable::value + )>; + + template + using result_is_move_convert_assignable = std::integral_constant::value && + + std::is_nothrow_constructible::value && + std::is_assignable::value && + std::is_nothrow_constructible::value && + std::is_assignable::value + )>; + + //------------------------------------------------------------------------- + + template + using result_is_value_assignable = std::integral_constant::type>::value && + !is_failure::type>::value && + std::is_nothrow_constructible::value && + std::is_assignable&,U>::value && + ( + !std::is_same::type,typename std::decay::type>::value || + !std::is_scalar::value + ) + )>; + + template + using result_is_failure_assignable = std::integral_constant::value && + std::is_assignable::value + )>; + + // Friending 'extract_error" below was causing some compilers to incorrectly + // identify `exp.m_storage.m_error` as being an access violation despite the + // friendship. Using a type name instead seems to be ubiquitous across + // compilers + struct result_error_extractor + { + template + static constexpr auto get(const result& exp) noexcept -> const E&; + template + static constexpr auto get(result& exp) noexcept -> E&; + }; + + template + constexpr auto extract_error(const result& exp) noexcept -> const E&; + + template + [[noreturn]] + auto throw_bad_result_access(E&& error) -> void; + + template + [[noreturn]] + auto throw_bad_result_access_message(String&& message, E&& error) -> void; + + } // namespace detail + + ///////////////////////////////////////////////////////////////////////////// + /// \brief The class template `result` manages result results from APIs, + /// while encoding possible failure conditions. + /// + /// A common use-case for result is the return value of a function that + /// may fail. As opposed to other approaches, such as `std::pair` + /// or `std::optional`, `result` more accurately conveys the intent of the + /// user along with the failure condition to the caller. This effectively + /// produces an orthogonal error handling mechanism that allows for exception + /// safety while also allowing discrete testability of the return type. + /// + /// `result` types may contain a `T` value, which signifies that an + /// operation succeeded in producing the result value of type `T`. If an + /// `result` does not contain a `T` value, it will always contain an `E` + /// error condition instead. + /// + /// An `result` can always be queried for a possible error case by + /// calling the `error()` function, even if it contains a value. + /// In the case that a `result` contains a value object, this will + /// simply return an `E` object constructed through default aggregate + /// construction, as if through the expression `E{}`, which is assumed to be + /// a "valid" (no-error) state for an `E` type. + /// For example: + /// + /// * `std::error_code{}` produces a default-construct error-code, which is + /// the "no error" state, + /// * integral (or enum) error codes produce a `0` value (no error), thanks to + /// zero-initialization, + /// * `std::exception_ptr{}` produces a null-pointer, + /// * `std::string{}` produces an empty string `""`, + /// * etc. + /// + /// When a `result` contains either a value or error, the storage for + /// that object is guaranteed to be allocated as part of the result + /// object's footprint, i.e. no dynamic memory allocation ever takes place. + /// Thus, a result object models an object, not a pointer, even though the + /// `operator*()` and `operator->()` are defined. + /// + /// When an object of type `result` is contextually converted to + /// `bool`, the conversion returns `true` if the object contains a value and + /// `false` if it contains an error. + /// + /// `result` objects do not have a "valueless" state like `variant`s do. + /// Once a `result` has been constructed with a value or error, the + /// active underlying type can only be changed through assignment which may + /// is only enabled if construction is guaranteed to be *non-throwing*. This + /// ensures that a valueless state cannot occur naturally. + /// + /// Example Use: + /// \code + /// auto to_string(int x) -> result + /// { + /// try { + /// return std::stoi(x); + /// } catch (const std::invalid_argument&) { + /// return fail(std::errc::invalid_argument); + /// } catch (const std::std::out_of_range&) { + /// return fail(std::errc::result_out_of_range); + /// } + /// } + /// \endcode + /// + /// \note If using C++17 or above, `fail` can be replaced with + /// `failure{...}` thanks to CTAD. + /// + /// \tparam T the underlying value type + /// \tparam E the underlying error type + /////////////////////////////////////////////////////////////////////////// + template + class RESULT_NODISCARD result + { + // Type requirements + + static_assert( + !std::is_abstract::value, + "It is ill-formed for T to be abstract type" + ); + static_assert( + !std::is_same::type, in_place_t>::value, + "It is ill-formed for T to be a (possibly CV-qualified) in_place_t type" + ); + static_assert( + !is_result::type>::value, + "It is ill-formed for T to be a (possibly CV-qualified) 'result' type" + ); + static_assert( + !is_failure::type>::value, + "It is ill-formed for T to be a (possibly CV-qualified) 'failure' type" + ); + static_assert( + !std::is_rvalue_reference::value, + "It is ill-formed for T to be an rvalue 'result type. " + "Only lvalue references are valid." + ); + + static_assert( + !std::is_abstract::value, + "It is ill-formed for E to be abstract type" + ); + static_assert( + !std::is_void::type>::value, + "It is ill-formed for E to be (possibly CV-qualified) void type" + ); + static_assert( + !is_result::type>::value, + "It is ill-formed for E to be a (possibly CV-qualified) 'result' type" + ); + static_assert( + !is_failure::type>::value, + "It is ill-formed for E to be a (possibly CV-qualified) 'failure' type" + ); + static_assert( + !std::is_same::type, in_place_t>::value, + "It is ill-formed for E to be a (possibly CV-qualified) in_place_t type" + ); + static_assert( + !std::is_reference::value, + "It is ill-formed for E to be a reference type. " + "Only T types may be lvalue references" + ); + + // Friendship + + friend detail::result_error_extractor; + + template + friend class result; + + //------------------------------------------------------------------------- + // Public Member Types + //------------------------------------------------------------------------- + public: + + using value_type = T; ///< The value type of this result + using error_type = E; ///< The error type of this result + using failure_type = failure; ///< The failure type + + template + using rebind = result; ///< Rebinds the result type + + //------------------------------------------------------------------------- + // Constructor / Destructor / Assignment + //------------------------------------------------------------------------- + public: + + /// \brief Default-constructs a result with the underlying value type + /// active + /// + /// This constructor is only enabled if `T` is default-constructible + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// assert(cpp::result{} == std::string{}); + /// ``` + template ::value>::type> + constexpr result() + noexcept(std::is_nothrow_constructible::value); + + /// \brief Copy constructs this result + /// + /// If \p other contains a value, initializes the contained value as if + /// direct-initializing (but not direct-list-initializing) an object of + /// type `T` with the expression *other. + /// + /// If other contains an error, constructs an object that contains a copy + /// of that error. + /// + /// \note This constructor is defined as deleted if + /// `std::is_copy_constructible::value` or + /// `std::is_copy_constructible::value` is `false` + /// + /// \note This constructor is defined as trivial if both + /// `std::is_trivially_copy_constructible::value` and + /// `std::is_trivially_copy_constructible::value` are `true` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// const auto r = cpp::result{42}; + /// const auto s = r; + /// + /// assert(r == s); + /// ``` + /// + /// \param other the result to copy + constexpr result(const result& other) = default; + + /// \brief Move constructs a result + /// + /// If other contains a value, initializes the contained value as if + /// direct-initializing (but not direct-list-initializing) an object + /// of type T with the expression `std::move(*other)` and does not make + /// other empty: a moved-from result still contains a value, but the + /// value itself is moved from. + /// + /// If other contains an error, move-constructs this result from that + /// error. + /// + /// \note This constructor is defined as deleted if + /// `std::is_move_constructible::value` or + /// `std::is_move_constructible::value` is `false` + /// + /// \note This constructor is defined as trivial if both + /// `std::is_trivially_move_constructible::value` and + /// `std::is_trivially_move_constructible::value` are `true` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{"hello world"}; + /// auto s = std::move(r); + /// + /// assert(s == "hello world"); + /// ``` + /// + /// \param other the result to move + constexpr result(result&& other) = default; + + /// \{ + /// \brief Converting copy constructor + /// + /// If \p other contains a value, constructs a result object + /// that contains a value, initialized as if direct-initializing + /// (but not direct-list-initializing) an object of type `T` with the + /// expression `*other`. + /// + /// If \p other contains an error, constructs a result object that + /// contains an error, initialized as if direct-initializing + /// (but not direct-list-initializing) an object of type `E`. + /// + /// \note This constructor does not participate in overload resolution + /// unless the following conditions are met: + /// - `std::is_constructible_v` is `true` + /// - T is not constructible or convertible from any expression + /// of type (possibly const) `result` + /// - E is not constructible or convertible from any expression + /// of type (possible const) `result` + /// + /// \note This constructor is explicit if and only if + /// `std::is_convertible_v` or + /// `std::is_convertible_v` is `false` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// const auto r = cpp::result{42}; + /// const auto s = cpp::result{r}; + /// + /// assert(r == s); + /// ``` + /// + /// \param other the other type to convert + template ::value,int>::type = 0> + result(const result& other) + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_constructible::value); + template ::value,int>::type = 0> + explicit result(const result& other) + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_constructible::value); + /// \} + + /// \{ + /// \brief Converting move constructor + /// + /// If \p other contains a value, constructs a result object + /// that contains a value, initialized as if direct-initializing + /// (but not direct-list-initializing) an object of type T with the + /// expression `std::move(*other)`. + /// + /// If \p other contains an error, constructs a result object that + /// contains an error, initialized as if direct-initializing + /// (but not direct-list-initializing) an object of type E&&. + /// + /// \note This constructor does not participate in overload resolution + /// unless the following conditions are met: + /// - `std::is_constructible_v` is `true` + /// - T is not constructible or convertible from any expression + /// of type (possibly const) `result` + /// - E is not constructible or convertible from any expression + /// of type (possible const) `result` + /// + /// \note This constructor is explicit if and only if + /// `std::is_convertible_v` or + /// `std::is_convertible_v` is `false` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result,int>{ + /// std::make_unique() + /// }; + /// const auto s = cpp::result,long>{ + /// std::move(r) + /// }; + /// ``` + /// + /// \param other the other type to convert + template ::value,int>::type = 0> + result(result&& other) + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_constructible::value); + template ::value,int>::type = 0> + explicit result(result&& other) + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_constructible::value); + /// \} + + //------------------------------------------------------------------------- + + /// \brief Constructs a result object that contains a value + /// + /// The value is initialized as if direct-initializing (but not + /// direct-list-initializing) an object of type `T` from the arguments + /// `std::forward(args)...` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{ + /// cpp::in_place, "Hello world" + /// }; + /// ``` + /// + /// \param args the arguments to pass to T's constructor + template ::value>::type> + constexpr explicit result(in_place_t, Args&&... args) + noexcept(std::is_nothrow_constructible::value); + + /// \brief Constructs a result object that contains a value + /// + /// The value is initialized as if direct-initializing (but not + /// direct-list-initializing) an object of type `T` from the arguments + /// `std::forward>(ilist)`, + /// `std::forward(args)...` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{ + /// cpp::in_place, {'H','e','l','l','o'} + /// }; + /// ``` + /// + /// \param ilist An initializer list of entries to forward + /// \param args the arguments to pass to T's constructor + template &, Args...>::value>::type> + constexpr explicit result(in_place_t, + std::initializer_list ilist, + Args&&...args) + noexcept(std::is_nothrow_constructible, Args...>::value); + + //------------------------------------------------------------------------- + + /// \brief Constructs a result object that contains an error + /// + /// the value is initialized as if direct-initializing (but not + /// direct-list-initializing) an object of type `E` from the arguments + /// `std::forward(args)...` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{ + /// cpp::in_place_error, "Hello world" + /// }; + /// ``` + /// + /// \param args the arguments to pass to E's constructor + template ::value>::type> + constexpr explicit result(in_place_error_t, Args&&... args) + noexcept(std::is_nothrow_constructible::value); + + /// \brief Constructs a result object that contains an error + /// + /// The value is initialized as if direct-initializing (but not + /// direct-list-initializing) an object of type `E` from the arguments + /// `std::forward>(ilist)`, + /// `std::forward(args)...` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{ + /// cpp::in_place_error, {'H','e','l','l','o'} + /// }; + /// ``` + /// + /// \param ilist An initializer list of entries to forward + /// \param args the arguments to pass to Es constructor + template &, Args...>::value>::type> + constexpr explicit result(in_place_error_t, + std::initializer_list ilist, + Args&&...args) + noexcept(std::is_nothrow_constructible, Args...>::value); + + //------------------------------------------------------------------------- + + /// \{ + /// \brief Constructs the underlying error of this result + /// + /// \note This constructor only participates in overload resolution if + /// `E` is constructible from \p e + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// cpp::result r = cpp::fail(42); + /// + /// auto get_error_result() -> cpp::result { + /// return cpp::fail("hello world!"); + /// } + /// ``` + /// + /// \param e the failure error + template ::value>::type> + constexpr /* implicit */ result(const failure& e) + noexcept(std::is_nothrow_constructible::value); + template ::value>::type> + constexpr /* implicit */ result(failure&& e) + noexcept(std::is_nothrow_constructible::value); + /// \} + + /// \{ + /// \brief Constructs a result object that contains a value + /// + /// The value is initialized as if direct-initializing (but not + /// direct-list-initializing) an object of type T with the expression + /// value. + /// + /// \note This constructor is constexpr if the constructor of T + /// selected by direct-initialization is constexpr + /// + /// \note This constructor does not participate in overload + /// resolution unless `std::is_constructible_v` is true + /// and `decay_t` is neither `in_place_t`, `in_place_error_t`, + /// nor a `result` type. + /// + /// \note This constructor is explicit if and only if + /// `std::is_convertible_v` is `false` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// cpp::result r = 42; + /// + /// auto get_value() -> cpp::result { + /// return "hello world!"; // implicit conversion + /// } + /// ``` + /// + /// \param value the value to copy + template ::value,int>::type = 0> + constexpr explicit result(U&& value) + noexcept(std::is_nothrow_constructible::value); + template ::value,int>::type = 0> + constexpr /* implicit */ result(U&& value) + noexcept(std::is_nothrow_constructible::value); + /// \} + + //------------------------------------------------------------------------- + + /// \brief Copy assigns the result stored in \p other + /// + /// \note This assignment operator only participates in overload resolution + /// if the following conditions are met: + /// - `std::is_nothrow_copy_constructible_v` is `true`, and + /// - `std::is_nothrow_copy_constructible_v` is `true` + /// this restriction guarantees that no ' + /// + /// \note This assignment operator is defined as trivial if the following + /// conditions are all `true`: + /// - `std::is_trivially_copy_constructible::value` + /// - `std::is_trivially_copy_constructible::value` + /// - `std::is_trivially_copy_assignable::value` + /// - `std::is_trivially_copy_assignable::value` + /// - `std::is_trivially_destructible::value` + /// - `std::is_trivially_destructible::value` + /// + /// \param other the other result to copy + auto operator=(const result& other) -> result& = default; + + /// \brief Move assigns the result stored in \p other + /// + /// \note This assignment operator only participates in overload resolution + /// if the following conditions are met: + /// - `std::is_nothrow_copy_constructible_v` is `true`, and + /// - `std::is_nothrow_copy_constructible_v` is `true` + /// this restriction guarantees that no 'valueless_by_exception` state + /// may occur. + /// + /// \note This assignment operator is defined as trivial if the following + /// conditions are all `true`: + /// - `std::is_trivially_move_constructible::value` + /// - `std::is_trivially_move_constructible::value` + /// - `std::is_trivially_move_assignable::value` + /// - `std::is_trivially_move_assignable::value` + /// - `std::is_trivially_destructible::value` + /// - `std::is_trivially_destructible::value` + /// + /// \param other the other result to copy + auto operator=(result&& other) -> result& = default; + + /// \brief Copy-converts the state of \p other + /// + /// If both `*this` and \p other contain either values or errors, the + /// underlying value is constructed as if through assignment. + /// + /// Otherwise if `*this` contains a value, but \p other contains an error, + /// then the contained value is destroyed by calling its destructor. `*this` + /// will no longer contain a value after the call, and will now contain `E` + /// constructed as if direct-initializing (but not direct-list-initializing) + /// an object with an argument of `const E2&`. + /// + /// If \p other contains a value and `*this` contains an error, then the + /// contained error is destroyed by calling its destructor. `*this` now + /// contains a value constructed as if direct-initializing (but not + /// direct-list-initializing) an object with an argument of `const T2&`. + /// + /// \note The function does not participate in overload resolution unless + /// - `std::is_nothrow_constructible_v`, + /// `std::is_assignable_v`, + /// `std::is_nothrow_constructible_v`, + /// `std::is_assignable_v` are all true. + /// - T is not constructible, convertible, or assignable from any + /// expression of type (possibly const) `result` + /// + /// \param other the other result object to convert + /// \return reference to `(*this)` + template ::value>::type> + auto operator=(const result& other) + noexcept(std::is_nothrow_assignable::value && + std::is_nothrow_assignable::value) -> result&; + + /// \brief Move-converts the state of \p other + /// + /// If both `*this` and \p other contain either values or errors, the + /// underlying value is constructed as if through move-assignment. + /// + /// Otherwise if `*this` contains a value, but \p other contains an error, + /// then the contained value is destroyed by calling its destructor. `*this` + /// will no longer contain a value after the call, and will now contain `E` + /// constructed as if direct-initializing (but not direct-list-initializing) + /// an object with an argument of `E2&&`. + /// + /// If \p other contains a value and `*this` contains an error, then the + /// contained error is destroyed by calling its destructor. `*this` now + /// contains a value constructed as if direct-initializing (but not + /// direct-list-initializing) an object with an argument of `T2&&`. + /// + /// \note The function does not participate in overload resolution unless + /// - `std::is_nothrow_constructible_v`, + /// `std::is_assignable_v`, + /// `std::is_nothrow_constructible_v`, + /// `std::is_assignable_v` are all true. + /// - T is not constructible, convertible, or assignable from any + /// expression of type (possibly const) `result` + /// + /// \param other the other result object to convert + /// \return reference to `(*this)` + template ::value>::type> + auto operator=(result&& other) + noexcept(std::is_nothrow_assignable::value && + std::is_nothrow_assignable::value) -> result&; + + /// \brief Perfect-forwarded assignment + /// + /// Depending on whether `*this` contains a value before the call, the + /// contained value is either direct-initialized from std::forward(value) + /// or assigned from std::forward(value). + /// + /// \note The function does not participate in overload resolution unless + /// - `std::decay_t` is not a result type, + /// - `std::decay_t` is not a failure type + /// - `std::is_nothrow_constructible_v` is `true` + /// - `std::is_assignable_v` is `true` + /// - and at least one of the following is `true`: + /// - `T` is not a scalar type; + /// - `std::decay_t` is not `T`. + /// + /// \param value to assign to the contained value + /// \return reference to `(*this)` + template ::value>::type> + auto operator=(U&& value) + noexcept(std::is_nothrow_assignable::value) -> result&; + + /// \{ + /// \brief Perfect-forwarded assignment + /// + /// Depending on whether `*this` contains an error before the call, the + /// contained error is either direct-initialized via forwarding the error, + /// or assigned from forwarding the error + /// + /// \note The function does not participate in overload resolution unless + /// - `std::is_nothrow_constructible_v` is `true`, and + /// - `std::is_assignable_v` is `true` + /// + /// \param other the failure value to assign to this + /// \return reference to `(*this)` + template ::value>::type> + auto operator=(const failure& other) + noexcept(std::is_nothrow_assignable::value) -> result&; + template ::value>::type> + auto operator=(failure&& other) + noexcept(std::is_nothrow_assignable::value) -> result&; + /// \} + + //------------------------------------------------------------------------- + // Observers + //------------------------------------------------------------------------- + public: + + /// \{ + /// \brief Retrieves a pointer to the contained value + /// + /// This operator exists to give `result` an `optional`-like API for cases + /// where it's known that the `result` already contains a value. + /// + /// Care must be taken to ensure that this is only used in safe contexts + /// where a `T` value is active. + /// + /// \note The behavior is undefined if `*this` does not contain a value. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{ + /// make_widget() + /// }; + /// + /// r->do_something(); + /// ``` + /// + /// \return a pointer to the contained value + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto operator->() + noexcept -> typename std::remove_reference::type*; + RESULT_WARN_UNUSED + constexpr auto operator->() + const noexcept -> typename std::remove_reference::type>::type*; + /// \} + + /// \{ + /// \brief Retrieves a reference to the contained value + /// + /// This operator exists to give `result` an `optional`-like API for cases + /// where it's known that the `result` already contains a value. + /// + /// Care must be taken to ensure that this is only used in safe contexts + /// where a `T` value is active. + /// + /// \note The behaviour is undefined if `*this` does not contain a value + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{ + /// make_widget() + /// }; + /// + /// (*r).do_something(); + /// + /// consume(*r); + /// ``` + /// + /// \return a reference to the contained value + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto operator*() + & noexcept -> typename std::add_lvalue_reference::type; + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto operator*() + && noexcept -> typename std::add_rvalue_reference::type; + RESULT_WARN_UNUSED + constexpr auto operator*() + const& noexcept -> typename std::add_lvalue_reference::type>::type; + RESULT_WARN_UNUSED + constexpr auto operator*() + const&& noexcept -> typename std::add_rvalue_reference::type>::type; + /// \} + + //------------------------------------------------------------------------- + + /// \brief Contextually convertible to `true` if `*this` contains a value + /// + /// This function exists to allow for simple, terse checks for containing + /// a value. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto get_result() -> cpp::result; + /// auto r = get_result(); + /// if (r) { ... } + /// + /// assert(static_cast(cpp::result{42})); + /// + /// assert(!static_cast(cpp::result{cpp::fail(42)})); + /// ``` + /// + /// \return `true` if `*this` contains a value, `false` if `*this` + /// does not contain a value + RESULT_WARN_UNUSED + constexpr explicit operator bool() const noexcept; + + /// \brief Returns `true` if `*this` contains a value + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto get_result() -> cpp::result; + /// auto r = get_result(); + /// if (r.has_value()) { ... } + /// + /// assert(cpp::result{42}.has_value()); + /// + /// assert(!cpp::result{cpp::fail(42)}.has_value()); + /// ``` + /// + /// \return `true` if `*this` contains a value, `false` if `*this` + /// contains an error + RESULT_WARN_UNUSED + constexpr auto has_value() const noexcept -> bool; + + /// \brief Returns `true` if `*this` contains an error + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto get_result() -> cpp::result; + /// + /// auto r = get_result(); + /// if (r.has_error()) { ... } + /// + /// assert(!cpp::result{42}.has_error()); + /// + /// assert(cpp::result{cpp::fail(42)}.has_error()); + /// ``` + /// + /// \return `true` if `*this` contains an error, `false` if `*this` + /// contains a value + RESULT_WARN_UNUSED + constexpr auto has_error() const noexcept -> bool; + + //------------------------------------------------------------------------- + + /// \{ + /// \brief Returns a reference to the contained value + /// + /// This function provides checked (throwing) access to the underlying + /// value. The constness and refness of this result is propagated to the + /// underlying reference. + /// + /// If this contains an error, an exception is thrown containing the + /// underlying error. The error is consumed propagating the same constness + /// and refness of this result. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// assert(cpp::result{42}.value() == 42); + /// + /// auto r = cpp::result,int>{ + /// std::make_unique(42) + /// }; + /// auto s = std::move(r).value(); + /// + /// try { + /// auto r = cpp::result{ cpp::fail(42) }; + /// auto v = r.value(); + /// } catch (const cpp::bad_result_access& e) { + /// assert(e.error() == 42); + /// } + /// ``` + /// + /// \throws bad_result_access if `*this` does not contain a value. + /// + /// \return the value of `*this` + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto value() + & -> typename std::add_lvalue_reference::type; + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto value() + && -> typename std::add_rvalue_reference::type; + RESULT_WARN_UNUSED + constexpr auto value() + const & -> typename std::add_lvalue_reference::type>::type; + RESULT_WARN_UNUSED + constexpr auto value() + const && -> typename std::add_rvalue_reference::type>::type; + /// \} + + /// \{ + /// \brief Returns the contained error, if one exists, or a + /// default-constructed error value + /// + /// The `error()` function will not throw any exceptions if `E` does not + /// throw any exceptions for the copy or move construction. + /// + /// This is to limit the possible scope for exceptions, and to allow the + /// error type to be treated as a "status"-like type, where the + /// default-constructed case is considered the "good" state. + /// + /// If this function is invoked on an rvalue of a result, the error is + /// returned via move-construction + /// + /// ### Requires + /// + /// * `std::is_default_constructible::value` is `true` + /// * `std::is_copy_constructible::value` or + /// `std::is_move_constructible::value` is `true` + /// * `E{}` represents the "good" (non-error) state + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{ 42 }; + /// assert(r.error() == std::error_code{}); + /// + /// auto r = cpp::result{ + /// cpp::fail(std::io_errc::stream) + /// }; + /// + /// assert(r.error() == std::io_errc::stream); + /// ``` + /// + /// \return the error or a default-constructed error value + RESULT_WARN_UNUSED + constexpr auto error() const & + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_copy_constructible::value) -> E; + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto error() && + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_move_constructible::value) -> E; + /// } + + /// \{ + /// \brief Asserts an expectation that this result contains an error, + /// throwing a bad_result_access on failure + /// + /// If this function is invoked from an rvalue of `result`, then this will + /// consume the underlying error first, if there is one. + /// + /// \note This function exists as a means to allow for results to be marked + /// `used` without requiring directly inspecting the underlying value. + /// This is, in effect, equivalent to `assert(res.has_value())`, + /// however it uses exceptions to ensure the stack can be unwound, and + /// exceptions invoked. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto start_service() -> cpp::result; + /// + /// start_service().expect("Service failed to start!"); + /// ``` + /// + /// \param message the message to provide to this expectation + /// + /// \return the value of `*this` + template ::value && + std::is_copy_constructible::value + )>::type> + RESULT_CPP14_CONSTEXPR auto expect(String&& message) & -> typename std::add_lvalue_reference::type; + template ::value && + std::is_move_constructible::value + )>::type> + RESULT_CPP14_CONSTEXPR auto expect(String&& message) && -> typename std::add_rvalue_reference::type; + template ::value && + std::is_copy_constructible::value + )>::type> + RESULT_CPP14_CONSTEXPR auto expect(String&& message) const & -> typename std::add_lvalue_reference::type>::type; + template ::value && + std::is_copy_constructible::value + )>::type> + RESULT_CPP14_CONSTEXPR auto expect(String&& message) const && -> typename std::add_rvalue_reference::type>::type; + /// \} + + //------------------------------------------------------------------------- + // Monadic Functionalities + //------------------------------------------------------------------------- + public: + + /// \{ + /// \brief Returns the contained value if `*this` has a value, + /// otherwise returns \p default_value. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{42}; + /// assert(r.value_or(0) == 42); + /// + /// auto r = cpp::result{cpp::fail(42)}; + /// assert(r.value_or(0) == 0); + /// ``` + /// + /// \param default_value the value to use in case `*this` contains an error + /// \return the contained value or \p default_value + template + RESULT_WARN_UNUSED + constexpr auto value_or(U&& default_value) + const & -> typename std::remove_reference::type; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto value_or(U&& default_value) + && -> typename std::remove_reference::type; + /// \} + + /// \{ + /// \brief Returns the contained error if `*this` has an error, + /// otherwise returns \p default_error. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{42}; + /// assert(r.error_or(0) == cpp::fail(0)); + /// + /// auto r = cpp::result{cpp::fail(42)}; + /// assert(r.error_or(0) == cpp::fail(42)); + /// ``` + /// + /// \param default_error the error to use in case `*this` is empty + /// \return the contained value or \p default_error + template + RESULT_WARN_UNUSED + constexpr auto error_or(U&& default_error) const & -> error_type; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto error_or(U&& default_error) && -> error_type; + /// \} + + //------------------------------------------------------------------------- + + /// \brief Returns a result containing \p value if this result contains + /// a value, otherwise returns a result containing the current + /// error. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{42}; + /// assert(r.and_then(100) == 100); + /// + /// auto r = cpp::result{cpp::fail(42)}; + /// assert(r.and_then(100) == cpp::fail(42)); + /// ``` + /// + /// \param value the value to return as a result + /// \return a result of \p value if this contains a value + template + RESULT_WARN_UNUSED + constexpr auto and_then(U&& value) const -> result::type,E>; + + /// \{ + /// \brief Invokes the function \p fn with the value of this result as + /// the argument + /// + /// If this result contains an error, a result of the error is returned + /// + /// The function being called must return a `result` type or the program + /// is ill-formed + /// + /// If this is called on an rvalue of `result` which contains an error, + /// the returned `result` is constructed from an rvalue of that error. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto to_string(int) -> cpp::result; + /// auto r = cpp::result{42}; + /// assert(r.flat_map(to_string) == "42"); + /// + /// auto r = cpp::result{cpp::fail(42)}; + /// assert(r.flat_map(to_string) == cpp::fail(42)); + /// ``` + /// + /// \param fn the function to invoke with this + /// \return The result of the function being called + template + RESULT_WARN_UNUSED + constexpr auto flat_map(Fn&& fn) const & -> detail::invoke_result_t; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto flat_map(Fn&& fn) && -> detail::invoke_result_t; + /// \} + + /// \{ + /// \brief Invokes the function \p fn with the value of this result as + /// the argument + /// + /// If this result is an error, the result of this function is that + /// error. Otherwise this function wraps the result and returns it as an + /// result. + /// + /// If this is called on an rvalue of `result` which contains an error, + /// the returned `result` is constructed from an rvalue of that error. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto to_string(int) -> std::string; + /// auto r = cpp::result{42}; + /// assert(r.map(to_string) == "42"); + /// + /// auto r = cpp::result{cpp::fail(42)}; + /// assert(r.map(to_string) == cpp::fail(42)); + /// ``` + /// + /// \param fn the function to invoke with this + /// \return The result result of the function invoked + template + RESULT_WARN_UNUSED + constexpr auto map(Fn&& fn) const & -> result,E>; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto map(Fn&& fn) && -> result,E>; + /// \} + + /// \{ + /// \brief Invokes the function \p fn with the error of this result as + /// the argument + /// + /// If this result contains a value, the result of this function is that + /// value. Otherwise the function is called with that error and returns the + /// result as a result. + /// + /// If this is called on an rvalue of `result` which contains a value, + /// the returned `result` is constructed from an rvalue of that value. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto to_string(int) -> std::string; + /// auto r = cpp::result{42}; + /// assert(r.map_error(to_string) == 42); + /// + /// auto r = cpp::result{cpp::fail(42)}; + /// assert(r.map_error(to_string) == cpp::fail("42")); + /// + /// auto r = cpp::result{}; + /// auto s = r.map(std::string::size); // 's' contains 'result' + /// ``` + /// + /// \param fn the function to invoke with this + /// \return The result result of the function invoked + template + RESULT_WARN_UNUSED + constexpr auto map_error(Fn&& fn) + const & -> result>; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto map_error(Fn&& fn) + && -> result>; + /// \} + + /// \{ + /// \brief Invokes the function \p fn with the error of this result as + /// the argument + /// + /// If this result contains a value, a result of the value is returned + /// + /// The function being called must return a `result` type or the program + /// is ill-formed + /// + /// If this is called on an rvalue of `result` which contains an error, + /// the returned `result` is constructed from an rvalue of that error. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto to_string(int) -> cpp::result; + /// auto r = cpp::result{42}; + /// assert(r.flat_map(to_string) == 42); + /// + /// auto r = cpp::result{cpp::fail(42)}; + /// assert(r.flat_map(to_string) == cpp::fail("42")); + /// ``` + /// + /// \param fn the function to invoke with this + /// \return The result of the function being called + template + RESULT_WARN_UNUSED + constexpr auto flat_map_error(Fn&& fn) + const & -> detail::invoke_result_t; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto flat_map_error(Fn&& fn) + && -> detail::invoke_result_t; + /// \} + + //------------------------------------------------------------------------- + // Private Members + //------------------------------------------------------------------------- + private: + + detail::result_storage m_storage; + + //------------------------------------------------------------------------- + // Private Monadic Functions + //------------------------------------------------------------------------- + private: + + /// \{ + /// \brief Map implementations for void and non-void functions + /// + /// \param fn the function + template + constexpr auto map_impl(std::true_type, Fn&& fn) const & -> result; + template + constexpr auto map_impl(std::false_type, Fn&& fn) const & -> result,E>; + template + RESULT_CPP14_CONSTEXPR auto map_impl(std::true_type, Fn&& fn) && -> result; + template + RESULT_CPP14_CONSTEXPR auto map_impl(std::false_type, Fn&& fn) && -> result,E>; + /// \} + }; + + //=========================================================================== + // class : result + //=========================================================================== + + ///////////////////////////////////////////////////////////////////////////// + /// \brief Partial specialization of `result` + /// + /// \tparam E the underlying error type + ///////////////////////////////////////////////////////////////////////////// + template + class RESULT_NODISCARD result + { + // Type requirements + + static_assert( + !std::is_void::type>::value, + "It is ill-formed for E to be (possibly CV-qualified) void type" + ); + static_assert( + !std::is_abstract::value, + "It is ill-formed for E to be abstract type" + ); + static_assert( + !is_failure::type>::value, + "It is ill-formed for E to be a (possibly CV-qualified) 'failure' type" + ); + static_assert( + !std::is_reference::value, + "It is ill-formed for E to be a reference type. " + "Only T types may be lvalue references" + ); + + // Friendship + + friend detail::result_error_extractor; + + template + friend class result; + + //------------------------------------------------------------------------- + // Public Member Types + //------------------------------------------------------------------------- + public: + + using value_type = void; ///< The value type of this result + using error_type = E; ///< The error type of this result + using failure_type = failure; ///< The failure type + + template + using rebind = result; ///< Rebinds the result type + + //------------------------------------------------------------------------- + // Constructor / Assignment + //------------------------------------------------------------------------- + public: + + /// \brief Constructs a `result` object in a value state + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{}; + /// ``` + constexpr result() noexcept; + + /// \brief Copy constructs this result + /// + /// If other contains an error, constructs an object that contains a copy + /// of that error. + /// + /// \note This constructor is defined as deleted if + /// `std::is_copy_constructible::value` is `false` + /// + /// \note This constructor is defined as trivial if both + /// `std::is_trivially_copy_constructible::value` are `true` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// const auto r = cpp::result{}; + /// const auto s = r; + /// ``` + /// + /// \param other the result to copy + constexpr result(const result& other) = default; + + /// \brief Move constructs a result + /// + /// If other contains an error, move-constructs this result from that + /// error. + /// + /// \note This constructor is defined as deleted if + /// `std::is_move_constructible::value` is `false` + /// + /// \note This constructor is defined as trivial if both + /// `std::is_trivially_move_constructible::value` are `true` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{}; + /// auto s = std::move(r); + /// ``` + /// + /// \param other the result to move + constexpr result(result&& other) = default; + + /// \brief Converting copy constructor + /// + /// If \p other contains a value, constructs a result object that is not + /// in an error state -- ignoring the value. + /// + /// If \p other contains an error, constructs a result object that + /// contains an error, initialized as if direct-initializing + /// (but not direct-list-initializing) an object of type `E`. + /// + /// \note This constructor does not participate in overload resolution + /// unless the following conditions are met: + /// - `std::is_constructible_v` is `true` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// const auto r = cpp::result{42}; + /// const auto s = cpp::result{r}; + /// ``` + /// + /// \param other the other type to convert + template ::value>::type> + explicit result(const result& other) + noexcept(std::is_nothrow_constructible::value); + + /// \brief Converting move constructor + /// + /// If \p other contains an error, constructs a result object that + /// contains an error, initialized as if direct-initializing + /// (but not direct-list-initializing) an object of type E&&. + /// + /// \note This constructor does not participate in overload resolution + /// unless the following conditions are met: + /// - `std::is_constructible_v` is `true` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{42}; + /// auto s = cpp::result{ + /// std::move(r) + /// }; + /// ``` + /// + /// \param other the other type to convert + template ::value>::type> + explicit result(result&& other) + noexcept(std::is_nothrow_constructible::value); + + //------------------------------------------------------------------------- + + /// \brief Constructs a result object in a value state + /// + /// This constructor exists primarily for symmetry with the `result` + /// constructor. Unlike the `T` overload, no variadic arguments may be + /// supplied. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{cpp::in_place}; + /// ``` + constexpr explicit result(in_place_t) noexcept; + + /// \brief Constructs a result object that contains an error + /// + /// the value is initialized as if direct-initializing (but not + /// direct-list-initializing) an object of type `E` from the arguments + /// `std::forward(args)...` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{ + /// cpp::in_place_error, "Hello world" + /// }; + /// ``` + /// + /// \param args the arguments to pass to `E`'s constructor + template ::value>::type> + constexpr explicit result(in_place_error_t, Args&&... args) + noexcept(std::is_nothrow_constructible::value); + + /// \brief Constructs a result object that contains an error + /// + /// The value is initialized as if direct-initializing (but not + /// direct-list-initializing) an object of type `E` from the arguments + /// `std::forward>(ilist)`, + /// `std::forward(args)...` + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto r = cpp::result{ + /// cpp::in_place_error, {'H','e','l','l','o'} + /// }; + /// ``` + /// + /// \param ilist An initializer list of entries to forward + /// \param args the arguments to pass to Es constructor + template &, Args...>::value>::type> + constexpr explicit result(in_place_error_t, + std::initializer_list ilist, + Args&&...args) + noexcept(std::is_nothrow_constructible, Args...>::value); + + //------------------------------------------------------------------------- + + /// \{ + /// \brief Constructs the underlying error of this result + /// + /// \note This constructor only participates in overload resolution if + /// `E` is constructible from \p e + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// cpp::result r = cpp::fail(42); + /// + /// auto get_error_result() -> cpp::result { + /// return cpp::fail("hello world!"); + /// } + /// ``` + /// + /// \param e the failure error + template ::value>::type> + constexpr /* implicit */ result(const failure& e) + noexcept(std::is_nothrow_constructible::value); + template ::value>::type> + constexpr /* implicit */ result(failure&& e) + noexcept(std::is_nothrow_constructible::value); + /// \} + + //------------------------------------------------------------------------- + + /// \brief Copy assigns the result stored in \p other + /// + /// \note The function does not participate in overload resolution unless + /// - `std::is_nothrow_copy_constructible_v` is `true` + /// this restriction guarantees that no 'valueless_by_exception` state + /// may occur. + /// + /// \note This assignment operator is defined as trivial if the following + /// conditions are all `true`: + /// - `std::is_trivially_copy_constructible::value` + /// - `std::is_trivially_copy_assignable::value` + /// - `std::is_trivially_destructible::value` + /// + /// \param other the other result to copy + auto operator=(const result& other) -> result& = default; + + /// \brief Move assigns the result stored in \p other + /// + /// \note The function does not participate in overload resolution unless + /// - `std::is_nothrow_copy_constructible_v` is `true` + /// this restriction guarantees that no 'valueless_by_exception` state + /// may occur. + /// + /// \note This assignment operator is defined as trivial if the following + /// conditions are all `true`: + /// - `std::is_trivially_move_constructible::value` + /// - `std::is_trivially_move_assignable::value` + /// - `std::is_trivially_destructible::value` + /// + /// \param other the other result to copy + auto operator=(result&& other) -> result& = default; + + /// \brief Copy-converts the state of \p other + /// + /// If both this and \p other contain an error, the underlying error is + /// assigned through copy-assignment. + /// + /// If \p other contains a value state, this result is constructed in a + /// value state. + /// + /// If \p other contans an error state, and this contains a value state, + /// the underlying error is constructed through copy-construction. + /// + /// \note The function does not participate in overload resolution unless + /// - `std::is_nothrow_constructible_v`, + /// `std::is_assignable_v` are all `true`. + /// + /// \param other the other result object to convert + /// \return reference to `(*this)` + template ::value && + std::is_assignable::value>::type> + auto operator=(const result& other) + noexcept(std::is_nothrow_assignable::value) -> result&; + + /// \brief Move-converts the state of \p other + /// + /// If both this and \p other contain an error, the underlying error is + /// assigned through move-assignment. + /// + /// If \p other contains a value state, this result is constructed in a + /// value state. + /// + /// If \p other contans an error state, and this contains a value state, + /// the underlying error is constructed through move-construction. + /// + /// \note The function does not participate in overload resolution unless + /// - `std::is_nothrow_constructible_v`, + /// `std::is_assignable_v` are all `true`. + /// + /// \param other the other result object to convert + /// \return reference to `(*this)` + template ::value && + std::is_assignable::value>::type> + auto operator=(result&& other) + noexcept(std::is_nothrow_assignable::value) -> result&; + + /// \{ + /// \brief Perfect-forwarded assignment + /// + /// Depending on whether `*this` contains an error before the call, the + /// contained error is either direct-initialized via forwarding the error, + /// or assigned from forwarding the error + /// + /// \note The function does not participate in overload resolution unless + /// - `std::is_nothrow_constructible_v` is `true`, and + /// - `std::is_assignable_v` is `true` + /// + /// \param other the failure value to assign to this + /// \return reference to `(*this)` + template ::value>::type> + auto operator=(const failure& other) + noexcept(std::is_nothrow_assignable::value) -> result&; + template ::value>::type> + auto operator=(failure&& other) + noexcept(std::is_nothrow_assignable::value) -> result&; + /// \} + + //------------------------------------------------------------------------- + // Observers + //------------------------------------------------------------------------- + public: + + + /// \brief Contextually convertible to `true` if `*this` does not contain + /// an error + /// + /// This function exists to allow for simple, terse checks for containing + /// a value. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto get_result() -> cpp::result; + /// auto r = get_result(); + /// if (r) { ... } + /// + /// assert(static_cast(cpp::result{})); + /// + /// assert(!static_cast(cpp::result{cpp::fail(42)})); + /// ``` + /// + /// \return `true` if `*this` contains a value, `false` if `*this` + /// does not contain a value + RESULT_WARN_UNUSED + constexpr explicit operator bool() const noexcept; + + /// \copydoc result::has_value + RESULT_WARN_UNUSED + constexpr auto has_value() const noexcept -> bool; + + /// \copydoc result::has_error + RESULT_WARN_UNUSED + constexpr auto has_error() const noexcept -> bool; + + //------------------------------------------------------------------------- + + /// \{ + /// \brief Throws an exception if `(*this)` is in an error state + /// + /// This function exists for symmetry with `cpp::result` objects where + /// `T` contains a value. + /// + /// If this contains an error, an exception is thrown containing the + /// underlying error. The error is consumed propagating the same constness + /// and refness of this result. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// cpp::result{}.value(); // no exception + /// + /// auto r = cpp::result>{ + /// cpp::fail(std::make_unique(42)) + /// }; + /// std::move(r).value(); // throws bad_result_access> + /// + /// try { + /// auto r = cpp::result{ cpp::fail(42) }.value(); + /// } catch (const cpp::bad_result_access& e) { + /// assert(e.error() == 42); + /// } + /// ``` + /// + /// \throws bad_result_access if `*this` does not contain a value. + RESULT_CPP14_CONSTEXPR auto value() && -> void; + RESULT_CPP14_CONSTEXPR auto value() const & -> void; + /// \} + + /// \{ + /// \copydoc result::error + RESULT_WARN_UNUSED + constexpr auto error() const & + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_copy_constructible::value) -> E; + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto error() && + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_copy_constructible::value) -> E; + /// \} + + /// \{ + /// \copydoc result::expect + template ::value && + std::is_copy_constructible::value + )>::type> + RESULT_CPP14_CONSTEXPR auto expect(String&& message) const & -> void; + template ::value && + std::is_move_constructible::value + )>::type> + RESULT_CPP14_CONSTEXPR auto expect(String&& message) && -> void; + /// \} + + //------------------------------------------------------------------------- + // Monadic Functionalities + //------------------------------------------------------------------------- + public: + + /// \{ + /// \copydoc result::error_or + template + RESULT_WARN_UNUSED + constexpr auto error_or(U&& default_error) const & -> error_type; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto error_or(U&& default_error) && -> error_type; + /// \} + + //------------------------------------------------------------------------- + + /// \copydoc result::and_then + template + RESULT_WARN_UNUSED + constexpr auto and_then(U&& value) const -> result::type,E>; + + /// \{ + /// \brief Invokes the function \p fn if `(*this)` contains no value + /// + /// If this result contains an error, a result of the error is returned + /// + /// The function being called must return a `result` type or the program + /// is ill-formed + /// + /// If this is called on an rvalue of `result` which contains an error, + /// the returned `result` is constructed from an rvalue of that error. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto generate_int() -> cpp::result { return 42; } + /// auto r = cpp::result{}; + /// assert(r.flat_map(generate_int) == 42); + /// + /// auto r = cpp::result{cpp::fail(42)}; + /// assert(r.flat_map(generate_int) == cpp::fail(42)); + /// ``` + /// + /// \param fn the function to invoke with this + /// \return The result of the function being called + template + RESULT_WARN_UNUSED + constexpr auto flat_map(Fn&& fn) const & -> detail::invoke_result_t; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto flat_map(Fn&& fn) && -> detail::invoke_result_t; + /// \} + + /// \{ + /// \brief Invokes the function \p fn if `(*this)` contains no value + /// + /// If this result is an error, the result of this function is that + /// error. Otherwise this function wraps the result and returns it as an + /// result. + /// + /// If this is called on an rvalue of `result` which contains an error, + /// the returned `result` is constructed from an rvalue of that error. + /// + /// ### Examples + /// + /// Basic Usage: + /// + /// ```cpp + /// auto generate_int() -> int { return 42; } + /// auto r = cpp::result{}; + /// assert(r.map(generate_int) == 42); + /// + /// auto r = cpp::result{cpp::fail(42)}; + /// assert(r.map(generate_int) == cpp::fail(42)); + /// ``` + /// + /// \param fn the function to invoke with this + /// \return The result result of the function invoked + template + RESULT_WARN_UNUSED + constexpr auto map(Fn&& fn) const & -> result,E>; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto map(Fn&& fn) && -> result,E>; + /// \} + + /// \{ + /// \copydoc result::map_error + template + constexpr auto map_error(Fn&& fn) const & -> result>; + template + RESULT_CPP14_CONSTEXPR + auto map_error(Fn&& fn) && -> result>; + /// \} + + + /// \{ + /// \copydoc result::flat_map_error + template + RESULT_WARN_UNUSED + constexpr auto flat_map_error(Fn&& fn) const & -> detail::invoke_result_t; + template + RESULT_WARN_UNUSED + RESULT_CPP14_CONSTEXPR auto flat_map_error(Fn&& fn) && -> detail::invoke_result_t; + /// \} + + //------------------------------------------------------------------------- + // Private Members + //------------------------------------------------------------------------- + private: + + detail::result_storage m_storage; + + //------------------------------------------------------------------------- + // Private Monadic Functions + //------------------------------------------------------------------------- + private: + + /// \{ + /// \brief Map implementations for void and non-void functions + /// + /// \param fn the function + template + constexpr auto map_impl(std::true_type, Fn&& fn) const & -> result; + template + constexpr auto map_impl(std::false_type, Fn&& fn) const & -> result,E>; + template + RESULT_CPP14_CONSTEXPR auto map_impl(std::true_type, Fn&& fn) && -> result; + template + RESULT_CPP14_CONSTEXPR auto map_impl(std::false_type, Fn&& fn) && -> result,E>; + /// \} + }; + + //=========================================================================== + // non-member functions : class : result + //=========================================================================== + + //--------------------------------------------------------------------------- + // Comparison + //--------------------------------------------------------------------------- + + template + constexpr auto operator==(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator!=(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator>=(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator<=(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator>(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator<(const result& lhs, const result& rhs) + noexcept -> bool; + + //--------------------------------------------------------------------------- + + template + constexpr auto operator==(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator!=(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator>=(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator<=(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator>(const result& lhs, const result& rhs) + noexcept -> bool; + template + constexpr auto operator<(const result& lhs, const result& rhs) + noexcept -> bool; + + //--------------------------------------------------------------------------- + + template ::value>::type> + constexpr auto operator==(const result& exp, const U& value) + noexcept -> bool; + template ::value>::type> + constexpr auto operator==(const T& value, const result& exp) + noexcept -> bool; + template ::value>::type> + constexpr auto operator!=(const result& exp, const U& value) + noexcept -> bool; + template ::value>::type> + constexpr auto operator!=(const T& value, const result& exp) + noexcept -> bool; + template ::value>::type> + constexpr auto operator<=(const result& exp, const U& value) + noexcept -> bool; + template ::value>::type> + constexpr auto operator<=(const T& value, const result& exp) + noexcept -> bool; + template ::value>::type> + constexpr auto operator>=(const result& exp, const U& value) + noexcept -> bool; + template ::value>::type> + constexpr auto operator>=(const T& value, const result& exp) + noexcept -> bool; + template ::value>::type> + constexpr auto operator<(const result& exp, const U& value) + noexcept -> bool; + template ::value>::type> + constexpr auto operator<(const T& value, const result& exp) + noexcept -> bool; + template ::value>::type> + constexpr auto operator>(const result& exp, const U& value) + noexcept -> bool; + template ::value>::type> + constexpr auto operator>(const T& value, const result& exp) + noexcept -> bool; + + //--------------------------------------------------------------------------- + + template + constexpr auto operator==(const result& exp, const failure& value) + noexcept -> bool; + template + constexpr auto operator==(const failure& value, const result& exp) + noexcept -> bool; + template + constexpr auto operator!=(const result& exp, const failure& value) + noexcept -> bool; + template + constexpr auto operator!=(const failure& value, const result& exp) + noexcept -> bool; + template + constexpr auto operator<=(const result& exp, const failure& value) + noexcept -> bool; + template + constexpr auto operator<=(const failure& value, const result& exp) + noexcept -> bool; + template + constexpr auto operator>=(const result& exp, const failure& value) + noexcept -> bool; + template + constexpr auto operator>=(const failure& value, const result& exp) + noexcept -> bool; + template + constexpr auto operator<(const result& exp, const failure& value) + noexcept -> bool; + template + constexpr auto operator<(const failure& value, const result& exp) + noexcept -> bool; + template + constexpr auto operator>(const result& exp, const failure& value) + noexcept -> bool; + template + constexpr auto operator>(const failure& value, const result& exp) + noexcept -> bool; + + //--------------------------------------------------------------------------- + // Utilities + //--------------------------------------------------------------------------- + + /// \{ + /// \brief Swaps the contents of \p lhs with \p rhs + /// + /// \param lhs the left result + /// \param rhs the right result + template + auto swap(result& lhs, result& rhs) +#if __cplusplus >= 201703L + noexcept(std::is_nothrow_move_constructible>::value && + std::is_nothrow_move_assignable>::value && + std::is_nothrow_swappable::value && + std::is_nothrow_swappable::value) +#else + noexcept(std::is_nothrow_move_constructible>::value && + std::is_nothrow_move_assignable>::value) +#endif + -> void; + template + auto swap(result& lhs, result& rhs) +#if __cplusplus >= 201703L + noexcept(std::is_nothrow_move_constructible>::value && + std::is_nothrow_move_assignable>::value && + std::is_nothrow_swappable::value) +#else + noexcept(std::is_nothrow_move_constructible>::value && + std::is_nothrow_move_assignable>::value) +#endif + -> void; + /// \} + +} // inline namespace bitwizeshift +} // namespace EXPECTED_NAMESPACE + +namespace std { + + template + struct hash<::RESULT_NS_IMPL::result> + { + auto operator()(const RESULT_NS_IMPL::result& x) const -> std::size_t + { + if (x.has_value()) { + return std::hash{}(*x) + 1; // add '1' to differentiate from error case + } + return std::hash{}(::RESULT_NS_IMPL::detail::extract_error(x)); + } + }; + + template + struct hash<::RESULT_NS_IMPL::result> + { + auto operator()(const RESULT_NS_IMPL::result& x) const -> std::size_t + { + if (x.has_value()) { + return 0; + } + return std::hash{}(::RESULT_NS_IMPL::detail::extract_error(x)); + } + }; + +} // namespace std + +#if !defined(RESULT_DISABLE_EXCEPTIONS) + +//============================================================================= +// class : bad_result_access +//============================================================================= + +//----------------------------------------------------------------------------- +// Constructors +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::bad_result_access::bad_result_access(E2&& error) + : logic_error{"error attempting to access value from result containing error"}, + m_error(detail::forward(error)) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::bad_result_access::bad_result_access( + const char* what_arg, + E2&& error +) : logic_error{what_arg}, + m_error(detail::forward(error)) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::bad_result_access::bad_result_access( + const std::string& what_arg, + E2&& error +) : logic_error{what_arg}, + m_error(detail::forward(error)) +{ + +} + +//----------------------------------------------------------------------------- +// Observers +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::bad_result_access::error() + & noexcept -> E& +{ + return m_error; +} + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::bad_result_access::error() + && noexcept -> E&& +{ + return static_cast(m_error); +} + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::bad_result_access::error() + const & noexcept -> const E& +{ + return m_error; +} + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::bad_result_access::error() + const && noexcept -> const E&& +{ + return static_cast(m_error); +} + +#endif + +//============================================================================= +// class : failure +//============================================================================= + +//----------------------------------------------------------------------------- +// Constructors +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::failure::failure(in_place_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value) + : m_failure(detail::forward(args)...) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::failure::failure( + in_place_t, + std::initializer_list ilist, + Args&&...args +) noexcept(std::is_nothrow_constructible, Args...>::value) + : m_failure(ilist, detail::forward(args)...) +{ + +} + +template +template ::value,int>::type> +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::failure::failure(E2&& error) + noexcept(std::is_nothrow_constructible::value) + : m_failure(detail::forward(error)) +{ + +} + +template +template ::value,int>::type> +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::failure::failure(E2&& error) + noexcept(std::is_nothrow_constructible::value) + : m_failure(detail::forward(error)) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::failure::failure(const failure& other) + noexcept(std::is_nothrow_constructible::value) + : m_failure(other.error()) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::failure::failure(failure&& other) + noexcept(std::is_nothrow_constructible::value) + : m_failure(static_cast&&>(other).error()) +{ + +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::failure::operator=(E2&& error) + noexcept( + std::is_nothrow_assignable::value || + std::is_lvalue_reference::value + ) -> failure& +{ + m_failure = detail::forward(error); + + return (*this); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::failure::operator=(const failure& other) + noexcept(std::is_nothrow_assignable::value) + -> failure& +{ + m_failure = other.error(); + + return (*this); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::failure::operator=(failure&& other) + noexcept(std::is_nothrow_assignable::value) + -> failure& +{ + m_failure = static_cast&&>(other).error(); + + return (*this); +} + +//----------------------------------------------------------------------------- +// Observers +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::failure::error() + & noexcept -> typename std::add_lvalue_reference::type +{ + return m_failure; +} + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::failure::error() + && noexcept -> typename std::add_rvalue_reference::type +{ + using reference = typename std::add_rvalue_reference::type; + + return static_cast(m_failure); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::failure::error() + const & noexcept + -> typename std::add_lvalue_reference::type>::type +{ + return m_failure; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::failure::error() + const && noexcept + -> typename std::add_rvalue_reference::type>::type +{ + using reference = typename std::add_rvalue_reference::type>::type; + + return static_cast(m_failure); +} + +//============================================================================= +// non-member functions : class : failure +//============================================================================= + +//----------------------------------------------------------------------------- +// Comparison +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator==(const failure& lhs, const failure& rhs) + noexcept -> bool +{ + return lhs.error() == rhs.error(); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator!=(const failure& lhs, const failure& rhs) + noexcept -> bool +{ + return lhs.error() != rhs.error(); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<(const failure& lhs, const failure& rhs) + noexcept -> bool +{ + return lhs.error() < rhs.error(); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>(const failure& lhs, const failure& rhs) + noexcept -> bool +{ + return lhs.error() > rhs.error(); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<=(const failure& lhs, const failure& rhs) + noexcept -> bool +{ + return lhs.error() <= rhs.error(); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>=(const failure& lhs, const failure& rhs) + noexcept -> bool +{ + return lhs.error() >= rhs.error(); +} + +//----------------------------------------------------------------------------- +// Utilities +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::fail(E&& e) + noexcept(std::is_nothrow_constructible::type,E>::value) + -> failure::type> +{ + using result_type = failure::type>; + + return result_type( + detail::forward(e) + ); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::fail(std::reference_wrapper e) + noexcept -> failure +{ + using result_type = failure; + + return result_type{e.get()}; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::fail(Args&&...args) + noexcept(std::is_nothrow_constructible::value) + -> failure +{ + return failure(in_place, detail::forward(args)...); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::fail(std::initializer_list ilist, Args&&...args) + noexcept(std::is_nothrow_constructible, Args...>::value) + -> failure +{ + return failure(in_place, ilist, detail::forward(args)...); +} + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::swap(failure& lhs, failure& rhs) +#if __cplusplus >= 201703L + noexcept(std::is_nothrow_swappable::value) -> void +#else + noexcept(std::is_nothrow_move_constructible::value) + -> void +#endif +{ + using std::swap; + + swap(lhs.error(), rhs.error()); +} + +//============================================================================= +// class : detail::result_union +//============================================================================= + +//----------------------------------------------------------------------------- +// Constructors / Assignment +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::detail::result_union + ::result_union(unit) + noexcept + : m_empty{} +{ + // m_has_value intentionally not set +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::detail::result_union + ::result_union(in_place_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value) + : m_value(detail::forward(args)...), + m_has_value{true} +{ +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::detail::result_union + ::result_union(in_place_error_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value) + : m_error(detail::forward(args)...), + m_has_value{false} +{ +} + +//----------------------------------------------------------------------------- +// Modifiers +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_union::destroy() + const noexcept -> void +{ + // do nothing +} + +//============================================================================= +// class : detail::result_union +//============================================================================= + +//----------------------------------------------------------------------------- +// Constructors / Destructor / Assignment +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::detail::result_union + ::result_union(unit) + noexcept + : m_empty{} +{ + // m_has_value intentionally not set +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::detail::result_union + ::result_union(in_place_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value) + : m_value(detail::forward(args)...), + m_has_value{true} +{ +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::detail::result_union + ::result_union(in_place_error_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value) + : m_error(detail::forward(args)...), + m_has_value{false} +{ +} + +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::detail::result_union + ::~result_union() + noexcept(std::is_nothrow_destructible::value && std::is_nothrow_destructible::value) +{ + destroy(); +} + +//----------------------------------------------------------------------------- +// Modifiers +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_union::destroy() + -> void +{ + if (m_has_value) { + m_value.~underlying_value_type(); + } else { + m_error.~underlying_error_type(); + } +} + +//============================================================================= +// class : result_construct_base +//============================================================================= + +//----------------------------------------------------------------------------- +// Constructors / Assignment +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::detail::result_construct_base::result_construct_base(unit) + noexcept + : storage{unit{}} +{ +} + +template +template +inline constexpr RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::detail::result_construct_base::result_construct_base( + in_place_t, + Args&&...args +) noexcept(std::is_nothrow_constructible::value) + : storage{in_place, detail::forward(args)...} +{ +} + +template +template +inline constexpr RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::detail::result_construct_base::result_construct_base( + in_place_error_t, + Args&&...args +) noexcept(std::is_nothrow_constructible::value) + : storage(in_place_error, detail::forward(args)...) +{ +} + +//----------------------------------------------------------------------------- +// Construction / Assignment +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::construct_value(Args&&...args) + noexcept(std::is_nothrow_constructible::value) + -> void +{ + using value_type = typename storage_type::underlying_value_type; + + auto* p = static_cast(std::addressof(storage.m_value)); + new (p) value_type(detail::forward(args)...); + storage.m_has_value = true; +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::construct_error(Args&&...args) + noexcept(std::is_nothrow_constructible::value) + -> void +{ + using error_type = typename storage_type::underlying_error_type; + + auto* p = static_cast(std::addressof(storage.m_error)); + new (p) error_type(detail::forward(args)...); + storage.m_has_value = false; +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::construct_error_from_result( + Result&& other +) -> void +{ + if (other.storage.m_has_value) { + construct_value(); + } else { + construct_error(detail::forward(other).storage.m_error); + } +} + + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::construct_from_result( + Result&& other +) -> void +{ + if (other.storage.m_has_value) { + construct_value_from_result_impl( + std::is_lvalue_reference{}, + detail::forward(other).storage.m_value + ); + } else { + construct_error(detail::forward(other).storage.m_error); + } +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::assign_value(Value&& value) + noexcept(std::is_nothrow_assignable::value) + -> void +{ + if (!storage.m_has_value) { + storage.destroy(); + construct_value(detail::forward(value)); + } else { + storage.m_value = detail::forward(value); + } +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::assign_error(Error&& error) + noexcept(std::is_nothrow_assignable::value) + -> void +{ + if (storage.m_has_value) { + storage.destroy(); + construct_error(detail::forward(error)); + } else { + storage.m_error = detail::forward(error); + } +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::assign_from_result(Result&& other) + -> void +{ + if (other.storage.m_has_value != storage.m_has_value) { + storage.destroy(); + construct_from_result(detail::forward(other)); + } else if (storage.m_has_value) { + assign_value_from_result_impl( + std::is_lvalue_reference{}, + detail::forward(other) + ); + } else { + storage.m_error = detail::forward(other).storage.m_error; + } +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::construct_value_from_result_impl( + std::true_type, + ReferenceWrapper&& reference +) noexcept -> void +{ + using value_type = typename storage_type::underlying_value_type; + + auto* p = static_cast(std::addressof(storage.m_value)); + new (p) value_type(reference.get()); + storage.m_has_value = true; +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::construct_value_from_result_impl( + std::false_type, + Value&& value +) noexcept(std::is_nothrow_constructible::value) -> void +{ + using value_type = typename storage_type::underlying_value_type; + + auto* p = static_cast(std::addressof(storage.m_value)); + new (p) value_type(detail::forward(value)); + storage.m_has_value = true; +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::assign_value_from_result_impl( + std::true_type, + Result&& other +) -> void +{ + // T is a reference; unwrap it + storage.m_value = other.storage.m_value.get(); +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_construct_base::assign_value_from_result_impl( + std::false_type, + Result&& other +) -> void +{ + storage.m_value = detail::forward(other).storage.m_value; +} + + +//============================================================================= +// class : result_trivial_copy_ctor_base_impl +//============================================================================= + +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::detail::result_trivial_copy_ctor_base_impl + ::result_trivial_copy_ctor_base_impl(const result_trivial_copy_ctor_base_impl& other) + noexcept(std::is_nothrow_copy_constructible::value && + std::is_nothrow_copy_constructible::value) + : base_type(unit{}) +{ + using ctor_base = result_construct_base; + + ctor_base::construct_from_result(static_cast(other)); +} + +//============================================================================= +// class : result_trivial_move_ctor_base +//============================================================================= + +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::detail::result_trivial_move_ctor_base_impl + ::result_trivial_move_ctor_base_impl(result_trivial_move_ctor_base_impl&& other) + noexcept(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value) + : base_type(unit{}) +{ + using ctor_base = result_construct_base; + + ctor_base::construct_from_result(static_cast(other)); +} + +//============================================================================= +// class : result_copy_assign_base +//============================================================================= + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_trivial_copy_assign_base_impl + ::operator=(const result_trivial_copy_assign_base_impl& other) + noexcept(std::is_nothrow_copy_constructible::value && + std::is_nothrow_copy_constructible::value && + std::is_nothrow_copy_assignable::value && + std::is_nothrow_copy_assignable::value) + -> result_trivial_copy_assign_base_impl& +{ + using ctor_base = result_construct_base; + + ctor_base::assign_from_result(static_cast(other)); + return (*this); +} + +//========================================================================= +// class : result_move_assign_base +//========================================================================= + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::result_trivial_move_assign_base_impl + ::operator=(result_trivial_move_assign_base_impl&& other) + noexcept(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value && + std::is_nothrow_move_assignable::value) + -> result_trivial_move_assign_base_impl& +{ + using ctor_base = result_construct_base; + + ctor_base::assign_from_result(static_cast(other)); + return (*this); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::detail::result_error_extractor::get(const result& exp) + noexcept -> const E& +{ + return exp.m_storage.storage.m_error; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::detail::result_error_extractor::get(result& exp) + noexcept -> E& +{ + return exp.m_storage.storage.m_error; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::detail::extract_error(const result& exp) noexcept -> const E& +{ + return result_error_extractor::get(exp); +} + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::throw_bad_result_access(E&& error) -> void +{ +#if defined(RESULT_DISABLE_EXCEPTIONS) + std::fprintf( + stderr, + "error attempting to access value from result containing error\n" + ); + std::abort(); +#else + using exception_type = bad_result_access< + typename std::remove_const< + typename std::remove_reference::type + >::type + >; + + throw exception_type{ + detail::forward(error) + }; +#endif +} + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::detail::throw_bad_result_access_message( + String&& message, + E&& error +) -> void +{ +#if defined(RESULT_DISABLE_EXCEPTIONS) + const auto message_string = std::string{ + detail::forward(message) + }; + std::fprintf(stderr, "%s\n", message_string.c_str()); + std::abort(); +#else + using exception_type = bad_result_access< + typename std::remove_const< + typename std::remove_reference::type + >::type + >; + + throw exception_type{ + detail::forward(message), + detail::forward(error) + }; +#endif +} + +//============================================================================= +// class : result +//============================================================================= + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result() + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place) +{ + +} + +template +template ::value,int>::type> +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::result::result(const result& other) + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_constructible::value) + : m_storage(detail::unit{}) +{ + m_storage.construct_from_result( + static_cast&>(other).m_storage + ); +} + +template +template ::value,int>::type> +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::result::result(const result& other) + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_constructible::value) + : m_storage(detail::unit{}) +{ + m_storage.construct_from_result( + static_cast&>(other).m_storage + ); +} + +template +template ::value,int>::type> +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::result::result(result&& other) + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_constructible::value) + : m_storage(detail::unit{}) +{ + m_storage.construct_from_result( + static_cast&&>(other).m_storage + ); +} + +template +template ::value,int>::type> +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::result::result(result&& other) + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_constructible::value) + : m_storage(detail::unit{}) +{ + m_storage.construct_from_result( + static_cast&&>(other).m_storage + ); +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(in_place_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place, detail::forward(args)...) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result( + in_place_t, + std::initializer_list ilist, + Args&&...args +) noexcept(std::is_nothrow_constructible, Args...>::value) + : m_storage(in_place, ilist, detail::forward(args)...) +{ + +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(in_place_error_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place_error, detail::forward(args)...) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result( + in_place_error_t, + std::initializer_list ilist, + Args&&...args +) noexcept(std::is_nothrow_constructible, Args...>::value) + : m_storage(in_place_error, ilist, detail::forward(args)...) +{ + +} + +//------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(const failure& e) + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place_error, e.error()) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(failure&& e) + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place_error, static_cast(e.error())) +{ + +} + +template +template ::value,int>::type> +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(U&& value) + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place, detail::forward(value)) +{ + +} + +template +template ::value,int>::type> +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(U&& value) + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place, detail::forward(value)) +{ + +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::result::operator=(const result& other) + noexcept(std::is_nothrow_assignable::value && + std::is_nothrow_assignable::value) + -> result& +{ + m_storage.assign_from_result( + static_cast&>(other).m_storage + ); + return (*this); +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::result::operator=(result&& other) + noexcept(std::is_nothrow_assignable::value && + std::is_nothrow_assignable::value) + -> result& +{ + m_storage.assign_from_result( + static_cast&&>(other).m_storage + ); + return (*this); +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::result::operator=(U&& value) + noexcept(std::is_nothrow_assignable::value) + -> result& +{ + m_storage.assign_value(detail::forward(value)); + return (*this); +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::result::operator=(const failure& other) + noexcept(std::is_nothrow_assignable::value) + -> result& +{ + m_storage.assign_error(other.error()); + return (*this); +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::result::operator=(failure&& other) + noexcept(std::is_nothrow_assignable::value) + -> result& +{ + m_storage.assign_error(static_cast(other.error())); + return (*this); +} + +//----------------------------------------------------------------------------- +// Observers +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::operator->() + noexcept -> typename std::remove_reference::type* +{ + // Prior to C++17, std::addressof was not `constexpr`. + // Since `addressof` fixes a relatively obscure issue where users define a + // custom `operator&`, the pre-C++17 implementation has been defined to be + // `&(**this)` so that it may exist in constexpr contexts. +#if __cplusplus >= 201703L + return std::addressof(**this); +#else + return &(**this); +#endif +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::operator->() + const noexcept -> typename std::remove_reference::type>::type* +{ +#if __cplusplus >= 201703L + return std::addressof(**this); +#else + return &(**this); +#endif +} + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::operator*() + & noexcept -> typename std::add_lvalue_reference::type +{ + return m_storage.storage.m_value; +} + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::operator*() + && noexcept -> typename std::add_rvalue_reference::type +{ + using reference = typename std::add_rvalue_reference::type; + + return static_cast(m_storage.storage.m_value); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::operator*() + const& noexcept -> typename std::add_lvalue_reference::type>::type +{ + return m_storage.storage.m_value; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::operator*() + const&& noexcept -> typename std::add_rvalue_reference::type>::type +{ + using reference = typename std::add_rvalue_reference::type>::type; + + return static_cast(m_storage.storage.m_value); +} + +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::operator bool() + const noexcept +{ + return m_storage.storage.m_has_value; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::has_value() + const noexcept -> bool +{ + return m_storage.storage.m_has_value; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::has_error() + const noexcept -> bool +{ + return !m_storage.storage.m_has_value; +} + +//----------------------------------------------------------------------------- + +// The `has_value()` expression below is incorrectly identified as an unused +// value, which results in the `-Wunused-value` warning. This is suppressed +// to prevent false-positives +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-value" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-value" +#elif defined(_MSC_VER) +// Older MSVC versions incorrectly warn on returning a reference to a temporary. +// This has been suppressed +# pragma warning(push) +# pragma warning(disable:4172) +#endif + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::value() + & -> typename std::add_lvalue_reference::type +{ + return (has_value() || + (detail::throw_bad_result_access(m_storage.storage.m_error), false), + m_storage.storage.m_value + ); +} + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::value() + && -> typename std::add_rvalue_reference::type +{ + using reference = typename std::add_rvalue_reference::type; + + return (has_value() || + (detail::throw_bad_result_access(static_cast(m_storage.storage.m_error)), true), + static_cast(m_storage.storage.m_value) + ); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::value() + const & -> typename std::add_lvalue_reference::type>::type +{ + return (has_value() || + (detail::throw_bad_result_access(m_storage.storage.m_error), true), + m_storage.storage.m_value + ); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::value() + const && -> typename std::add_rvalue_reference::type>::type +{ + using reference = typename std::add_rvalue_reference::type>::type; + + return (has_value() || + (detail::throw_bad_result_access(static_cast(m_storage.storage.m_error)), true), + (static_cast(m_storage.storage.m_value)) + ); +} + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::error() const & + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_copy_constructible::value) -> E +{ + static_assert( + std::is_default_constructible::value, + "E must be default-constructible if 'error()' checks are used. " + "This is to allow for default-constructed error states to represent the " + "'good' state" + ); + + return m_storage.storage.m_has_value + ? E{} + : m_storage.storage.m_error; +} + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::error() && + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_move_constructible::value) -> E +{ + static_assert( + std::is_default_constructible::value, + "E must be default-constructible if 'error()' checks are used. " + "This is to allow for default-constructed error states to represent the " + "'good' state" + ); + + return m_storage.storage.m_has_value + ? E{} + : static_cast(m_storage.storage.m_error); +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::expect(String&& message) + & -> typename std::add_lvalue_reference::type +{ + return (has_value() || + (detail::throw_bad_result_access_message( + detail::forward(message), + m_storage.storage.m_error + ), true), + m_storage.storage.m_value + ); +} + +template +template +inline RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::expect(String&& message) + && -> typename std::add_rvalue_reference::type +{ + using reference = typename std::add_rvalue_reference::type; + + return (has_value() || + (detail::throw_bad_result_access_message( + detail::forward(message), + static_cast(m_storage.storage.m_error) + ), true), + static_cast(m_storage.storage.m_value) + ); +} + +template +template +inline RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::expect(String&& message) + const & -> typename std::add_lvalue_reference::type>::type +{ + return (has_value() || + (detail::throw_bad_result_access_message( + detail::forward(message), + m_storage.storage.m_error + ), true), + m_storage.storage.m_value + ); +} + +template +template +inline RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::expect(String&& message) + const && -> typename std::add_rvalue_reference::type>::type +{ + using reference = typename std::add_rvalue_reference::type>::type; + + return (has_value() || + (detail::throw_bad_result_access_message( + detail::forward(message), + static_cast(m_storage.storage.m_error) + ), true), + (static_cast(m_storage.storage.m_value)) + ); +} + +//----------------------------------------------------------------------------- +// Monadic Functionalities +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::value_or(U&& default_value) + const& -> typename std::remove_reference::type +{ + return m_storage.storage.m_has_value + ? m_storage.storage.m_value + : detail::forward(default_value); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::value_or(U&& default_value) + && -> typename std::remove_reference::type +{ + return m_storage.storage.m_has_value + ? static_cast(**this) + : detail::forward(default_value); +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::error_or(U&& default_error) + const& -> error_type +{ + return m_storage.storage.m_has_value + ? detail::forward(default_error) + : m_storage.storage.m_error; +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::error_or(U&& default_error) + && -> error_type +{ + return m_storage.storage.m_has_value + ? detail::forward(default_error) + : static_cast(m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::and_then(U&& value) + const -> result::type,E> +{ + return map([&value](const T&){ + return detail::forward(value); + }); +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::flat_map(Fn&& fn) + const & -> detail::invoke_result_t +{ + using result_type = detail::invoke_result_t; + + static_assert( + is_result::value, + "flat_map must return a result type or the program is ill-formed" + ); + + return has_value() + ? detail::invoke(detail::forward(fn), m_storage.storage.m_value) + : result_type(in_place_error, m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::flat_map(Fn&& fn) + && -> detail::invoke_result_t +{ + using result_type = detail::invoke_result_t; + + static_assert( + is_result::value, + "flat_map must return a result type or the program is ill-formed" + ); + + return has_value() + ? detail::invoke(detail::forward(fn), static_cast(m_storage.storage.m_value)) + : result_type(in_place_error, static_cast(m_storage.storage.m_error)); +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::map(Fn&& fn) + const & -> result,E> +{ + using result_type = detail::invoke_result_t; + + return map_impl(std::is_void{}, detail::forward(fn)); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::map(Fn&& fn) + && -> result,E> +{ + using result_type = detail::invoke_result_t; + + return static_cast&&>(*this).map_impl( + std::is_void{}, + detail::forward(fn) + ); +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::map_error(Fn&& fn) + const & -> result> +{ + using result_type = result>; + + return has_error() + ? result_type(in_place_error, detail::invoke( + detail::forward(fn), m_storage.storage.m_error + )) + : result_type(in_place, m_storage.storage.m_value); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::map_error(Fn&& fn) + && -> result> +{ + using result_type = result>; + + return has_error() + ? result_type(in_place_error, detail::invoke( + detail::forward(fn), static_cast(m_storage.storage.m_error) + )) + : result_type(static_cast(m_storage.storage.m_value)); +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::flat_map_error(Fn&& fn) + const & -> detail::invoke_result_t +{ + using result_type = detail::invoke_result_t; + + static_assert( + is_result::value, + "flat_map_error must return a result type or the program is ill-formed" + ); + + return has_value() + ? result_type(in_place, m_storage.storage.m_value) + : detail::invoke(detail::forward(fn), m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::flat_map_error(Fn&& fn) + && -> detail::invoke_result_t +{ + using result_type = detail::invoke_result_t; + + static_assert( + is_result::value, + "flat_map_error must return a result type or the program is ill-formed" + ); + + return has_value() + ? result_type(in_place, static_cast(m_storage.storage.m_value)) + : detail::invoke(detail::forward(fn), static_cast(m_storage.storage.m_error)); +} + +//----------------------------------------------------------------------------- +// Private Monadic Functions +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::map_impl(std::true_type, Fn&& fn) + const & -> result +{ + using result_type = result; + + return has_value() + ? (detail::invoke(detail::forward(fn), m_storage.storage.m_value), result_type{}) + : result_type(in_place_error, m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::map_impl(std::false_type, Fn&& fn) + const & -> result,E> +{ + using invoke_result_type = detail::invoke_result_t; + using result_type = result; + + return has_value() + ? result_type(in_place, detail::invoke( + detail::forward(fn), m_storage.storage.m_value + )) + : result_type(in_place_error, m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::map_impl(std::true_type, Fn&& fn) + && -> result +{ + using result_type = result; + + return has_value() + ? (detail::invoke( + detail::forward(fn), static_cast(m_storage.storage.m_value) + ), result_type{}) + : result_type(in_place_error, static_cast(m_storage.storage.m_error)); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::map_impl(std::false_type, Fn&& fn) + && -> result,E> +{ + using invoke_result_type = detail::invoke_result_t; + using result_type = result; + + return has_value() + ? result_type(in_place, detail::invoke( + detail::forward(fn), static_cast(m_storage.storage.m_value) + )) + : result_type(in_place_error, static_cast(m_storage.storage.m_error)); +} + +//============================================================================= +// class : result +//============================================================================= + +//----------------------------------------------------------------------------- +// Constructor / Assignment +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result() + noexcept + : m_storage(in_place) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::result::result(const result& other) + noexcept(std::is_nothrow_constructible::value) + : m_storage(detail::unit{}) +{ + m_storage.construct_error_from_result( + static_cast&>(other).m_storage + ); +} + +template +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::result::result(result&& other) + noexcept(std::is_nothrow_constructible::value) + : m_storage(detail::unit{}) +{ + m_storage.construct_error_from_result( + static_cast&&>(other).m_storage + ); +} + + +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(in_place_t) + noexcept + : m_storage(in_place) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(in_place_error_t, Args&&...args) + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place_error, detail::forward(args)...) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(in_place_error_t, + std::initializer_list ilist, + Args&&...args) + noexcept(std::is_nothrow_constructible, Args...>::value) + : m_storage(in_place_error, ilist, detail::forward(args)...) +{ + +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(const failure& e) + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place_error, e.error()) +{ + +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::result(failure&& e) + noexcept(std::is_nothrow_constructible::value) + : m_storage(in_place_error, static_cast(e.error())) +{ + +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::result::operator=(const result& other) + noexcept(std::is_nothrow_assignable::value) + -> result& +{ + m_storage.assign_from_result(other.m_storage); + return (*this); +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::result::operator=(result&& other) + noexcept(std::is_nothrow_assignable::value) + -> result& +{ + m_storage.assign_from_result(static_cast&&>(other).m_storage); + return (*this); +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::result::operator=(const failure& other) + noexcept(std::is_nothrow_assignable::value) + -> result& +{ + m_storage.assign_error(other.error()); + return (*this); +} + +template +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::result::operator=(failure&& other) + noexcept(std::is_nothrow_assignable::value) + -> result& +{ + m_storage.assign_error(static_cast(other.error())); + return (*this); +} + +//----------------------------------------------------------------------------- +// Observers +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +RESULT_NS_IMPL::result::operator bool() + const noexcept +{ + return has_value(); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::has_value() + const noexcept -> bool +{ + return m_storage.storage.m_has_value; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::has_error() + const noexcept -> bool +{ + return !has_value(); +} + +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::value() + const & -> void +{ + static_cast( + has_value() || + (detail::throw_bad_result_access(m_storage.storage.m_error), true) + ); +} + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::value() + && -> void +{ + static_cast( + has_value() || + (detail::throw_bad_result_access(static_cast(m_storage.storage.m_error)), true) + ); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::error() + const & + noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_copy_constructible::value) -> E +{ + return has_value() ? E{} : m_storage.storage.m_error; +} + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::error() + && noexcept(std::is_nothrow_constructible::value && + std::is_nothrow_copy_constructible::value) -> E +{ + return has_value() ? E{} : static_cast(m_storage.storage.m_error); +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::expect(String&& message) + const & -> void +{ + if (has_error()) { + detail::throw_bad_result_access_message( + detail::forward(message), + m_storage.storage.m_error + ); + } +} + +template +template +inline RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::expect(String&& message) + && -> void +{ + if (has_error()) { + detail::throw_bad_result_access_message( + detail::forward(message), + static_cast(m_storage.storage.m_error) + ); + } +} + +//----------------------------------------------------------------------------- +// Monadic Functionalities +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::error_or(U&& default_error) + const & -> error_type +{ + return has_value() + ? detail::forward(default_error) + : m_storage.storage.m_error; +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::error_or(U&& default_error) + && -> error_type +{ + return has_value() + ? detail::forward(default_error) + : static_cast(m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::and_then(U&& value) + const -> result::type,E> +{ + return map([&value]{ + return detail::forward(value); + }); +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::flat_map(Fn&& fn) + const & -> detail::invoke_result_t +{ + using result_type = detail::invoke_result_t; + + static_assert( + is_result::value, + "flat_map must return a result type or the program is ill-formed" + ); + + return has_value() + ? detail::invoke(detail::forward(fn)) + : result_type(in_place_error, m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::flat_map(Fn&& fn) + && -> detail::invoke_result_t +{ + using result_type = detail::invoke_result_t; + + static_assert( + is_result::value, + "flat_map must return a result type or the program is ill-formed" + ); + + return has_value() + ? detail::invoke(detail::forward(fn)) + : result_type(in_place_error, static_cast(m_storage.storage.m_error)); +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::map(Fn&& fn) + const & -> result,E> +{ + using result_type = detail::invoke_result_t; + + return map_impl(std::is_void{}, detail::forward(fn)); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::map(Fn&& fn) + && -> result,E> +{ + using result_type = detail::invoke_result_t; + + return static_cast&&>(*this).map_impl( + std::is_void{}, + detail::forward(fn) + ); +} + +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::map_error(Fn&& fn) + const & -> result> +{ + using result_type = result>; + + return has_value() + ? result_type{} + : result_type(in_place_error, detail::invoke( + detail::forward(fn), m_storage.storage.m_error + )); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::map_error(Fn&& fn) + && -> result> +{ + using result_type = result>; + + return has_value() + ? result_type{} + : result_type(in_place_error, + detail::invoke(detail::forward(fn), static_cast(m_storage.storage.m_error) + )); +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::flat_map_error(Fn&& fn) + const & -> detail::invoke_result_t +{ + using result_type = detail::invoke_result_t; + + static_assert( + is_result::value, + "flat_map_error must return a result type or the program is ill-formed" + ); + static_assert( + std::is_default_constructible::value, + "flat_map_error for result requires the new T type to be default-" + "constructible" + ); + + return has_value() + ? result_type{} + : detail::invoke(detail::forward(fn), m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::flat_map_error(Fn&& fn) + && -> detail::invoke_result_t +{ + using result_type = detail::invoke_result_t; + + static_assert( + is_result::value, + "flat_map_error must return a result type or the program is ill-formed" + ); + static_assert( + std::is_default_constructible::value, + "flat_map_error for result requires the new T type to be default-" + "constructible" + ); + + return has_value() + ? result_type{} + : detail::invoke(detail::forward(fn), static_cast(m_storage.storage.m_error)); +} + +//----------------------------------------------------------------------------- +// Private Monadic Functions +//----------------------------------------------------------------------------- + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::map_impl(std::true_type, Fn&& fn) + const & -> result +{ + using result_type = result; + + return has_value() + ? (detail::invoke(detail::forward(fn)), result_type{}) + : result_type(in_place_error, m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::result::map_impl(std::false_type, Fn&& fn) + const & -> result,E> +{ + using invoke_result_type = detail::invoke_result_t; + using result_type = result; + + return has_value() + ? result_type(in_place, detail::invoke(detail::forward(fn))) + : result_type(in_place_error, m_storage.storage.m_error); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::map_impl(std::true_type, Fn&& fn) + && -> result +{ + using result_type = result; + + return has_value() + ? (detail::invoke(detail::forward(fn)), result_type{}) + : result_type(in_place_error, static_cast(m_storage.storage.m_error)); +} + +template +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::map_impl(std::false_type, Fn&& fn) + && -> result,E> +{ + using invoke_result_type = detail::invoke_result_t; + using result_type = result; + + return has_value() + ? result_type(in_place, detail::invoke(detail::forward(fn))) + : result_type(in_place_error, static_cast(m_storage.storage.m_error)); +} + +//============================================================================= +// non-member functions : class : result +//============================================================================= + +//----------------------------------------------------------------------------- +// Comparison +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator==(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return (lhs.has_value() == rhs.has_value()) + ? ( + lhs.has_value() + ? *lhs == *rhs + : detail::extract_error(lhs) == detail::extract_error(rhs) + ) + : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator!=(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return (lhs.has_value() == rhs.has_value()) + ? ( + lhs.has_value() + ? *lhs != *rhs + : detail::extract_error(lhs) != detail::extract_error(rhs) + ) + : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>=(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return (lhs.has_value() == rhs.has_value()) + ? ( + lhs.has_value() + ? *lhs >= *rhs + : detail::extract_error(lhs) >= detail::extract_error(rhs) + ) + : static_cast(static_cast(lhs)) >= static_cast(static_cast(rhs)); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<=(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return (lhs.has_value() == rhs.has_value()) + ? ( + lhs.has_value() + ? *lhs <= *rhs + : detail::extract_error(lhs) <= detail::extract_error(rhs) + ) + : static_cast(static_cast(lhs)) <= static_cast(static_cast(rhs)); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return (lhs.has_value() == rhs.has_value()) + ? ( + lhs.has_value() + ? *lhs > *rhs + : detail::extract_error(lhs) > detail::extract_error(rhs) + ) + : static_cast(static_cast(lhs)) > static_cast(static_cast(rhs)); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return (lhs.has_value() == rhs.has_value()) + ? ( + lhs.has_value() + ? *lhs < *rhs + : detail::extract_error(lhs) < detail::extract_error(rhs) + ) + : static_cast(static_cast(lhs)) < static_cast(static_cast(rhs)); +} + + +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator==(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return lhs.has_value() == rhs.has_value() + ? ( + lhs.has_value() + ? true + : detail::extract_error(lhs) == detail::extract_error(rhs) + ) + : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator!=(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return lhs.has_value() == rhs.has_value() + ? ( + lhs.has_value() + ? false + : detail::extract_error(lhs) != detail::extract_error(rhs) + ) + : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>=(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return lhs.has_value() == rhs.has_value() + ? ( + lhs.has_value() + ? true + : detail::extract_error(lhs) >= detail::extract_error(rhs) + ) + : static_cast(static_cast(lhs)) >= static_cast(static_cast(rhs)); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<=(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return lhs.has_value() == rhs.has_value() + ? ( + lhs.has_value() + ? true + : detail::extract_error(lhs) <= detail::extract_error(rhs) + ) + : static_cast(static_cast(lhs)) <= static_cast(static_cast(rhs)); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return lhs.has_value() == rhs.has_value() + ? ( + lhs.has_value() + ? false + : detail::extract_error(lhs) > detail::extract_error(rhs) + ) + : static_cast(static_cast(lhs)) > static_cast(static_cast(rhs)); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<(const result& lhs, + const result& rhs) + noexcept -> bool +{ + return lhs.has_value() == rhs.has_value() + ? ( + lhs.has_value() + ? false + : detail::extract_error(lhs) < detail::extract_error(rhs) + ) + : static_cast(static_cast(lhs)) < static_cast(static_cast(rhs)); +} + + +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator==(const result& exp, const U& value) + noexcept -> bool +{ + return (exp.has_value() && *exp == value); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator==(const T& value, const result& exp) + noexcept -> bool +{ + return (exp.has_value() && *exp == value); +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator!=(const result& exp, const U& value) + noexcept -> bool +{ + return exp.has_value() ? *exp != value : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator!=(const T& value, const result& exp) + noexcept -> bool +{ + return exp.has_value() ? value != *exp : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<=(const result& exp, const U& value) + noexcept -> bool +{ + return exp.has_value() ? *exp <= value : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<=(const T& value, const result& exp) + noexcept -> bool +{ + return exp.has_value() ? value <= *exp : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>=(const result& exp, const U& value) + noexcept -> bool +{ + return exp.has_value() ? *exp >= value : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>=(const T& value, const result& exp) + noexcept -> bool +{ + return exp.has_value() ? value >= *exp : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<(const result& exp, const U& value) + noexcept -> bool +{ + return exp.has_value() ? *exp < value : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<(const T& value, const result& exp) + noexcept -> bool +{ + return exp.has_value() ? value < *exp : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>(const result& exp, const U& value) + noexcept -> bool +{ + return exp.has_value() ? *exp > value : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>(const T& value, const result& exp) + noexcept -> bool +{ + return exp.has_value() ? value > *exp : true; +} + +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator==(const result& exp, const failure& error) + noexcept -> bool +{ + return exp.has_error() ? detail::extract_error(exp) == error.error() : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator==(const failure& error, const result& exp) + noexcept -> bool +{ + return exp.has_error() ? error.error() == detail::extract_error(exp) : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator!=(const result& exp, const failure& error) + noexcept -> bool +{ + return exp.has_error() ? detail::extract_error(exp) != error.error() : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator!=(const failure& error, const result& exp) + noexcept -> bool +{ + return exp.has_error() ? error.error() != detail::extract_error(exp) : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<=(const result& exp, const failure& error) + noexcept -> bool +{ + return exp.has_error() ? detail::extract_error(exp) <= error.error() : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<=(const failure& error, const result& exp) + noexcept -> bool +{ + return exp.has_error() ? error.error() <= detail::extract_error(exp) : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>=(const result& exp, const failure& error) + noexcept -> bool +{ + return exp.has_error() ? detail::extract_error(exp) >= error.error() : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>=(const failure& error, const result& exp) + noexcept -> bool +{ + return exp.has_error() ? error.error() >= detail::extract_error(exp) : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<(const result& exp, const failure& error) + noexcept -> bool +{ + return exp.has_error() ? detail::extract_error(exp) < error.error() : true; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator<(const failure& error, const result& exp) + noexcept -> bool +{ + return exp.has_error() ? error.error() < detail::extract_error(exp) : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>(const result& exp, const failure& error) + noexcept -> bool +{ + return exp.has_error() ? detail::extract_error(exp) > error.error() : false; +} + +template +inline RESULT_INLINE_VISIBILITY constexpr +auto RESULT_NS_IMPL::operator>(const failure& error, const result& exp) + noexcept -> bool +{ + return exp.has_error() ? error.error() > detail::extract_error(exp) : true; +} + +//----------------------------------------------------------------------------- +// Utilities +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::swap(result& lhs, result& rhs) +#if __cplusplus >= 201703L + noexcept(std::is_nothrow_move_constructible>::value && + std::is_nothrow_move_assignable>::value && + std::is_nothrow_swappable::value && + std::is_nothrow_swappable::value) +#else + noexcept(std::is_nothrow_move_constructible>::value && + std::is_nothrow_move_assignable>::value) +#endif + -> void +{ + using std::swap; + + if (lhs.has_value() == rhs.has_value()) { + if (lhs.has_value()) { + swap(*lhs, *rhs); + } else { + auto& lhs_error = detail::result_error_extractor::get(lhs); + auto& rhs_error = detail::result_error_extractor::get(rhs); + + swap(lhs_error, rhs_error); + } + // If both `result`s contain values, do nothing + } else { + auto temp = static_cast&&>(lhs); + lhs = static_cast&&>(rhs); + rhs = static_cast&&>(temp); + } +} + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::swap(result& lhs, result& rhs) +#if __cplusplus >= 201703L + noexcept(std::is_nothrow_move_constructible>::value && + std::is_nothrow_move_assignable>::value && + std::is_nothrow_swappable::value) +#else + noexcept(std::is_nothrow_move_constructible>::value && + std::is_nothrow_move_assignable>::value) +#endif + -> void +{ + using std::swap; + + if (lhs.has_value() == rhs.has_value()) { + if (lhs.has_error()) { + auto& lhs_error = detail::result_error_extractor::get(lhs); + auto& rhs_error = detail::result_error_extractor::get(rhs); + + swap(lhs_error, rhs_error); + } + // If both `result`s contain values, do nothing + } else { + auto temp = static_cast&&>(lhs); + lhs = static_cast&&>(rhs); + rhs = static_cast&&>(temp); + } +} + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +#undef RESULT_NAMESPACE_INTERNAL +#undef RESULT_NS_IMPL +#undef RESULT_CPP14_CONSTEXPR +#undef RESULT_CPP17_INLINE +#undef RESULT_INLINE_VISIBILITY +#undef RESULT_NODISCARD +#undef RESULT_WARN_UNUSED + +#endif /* RESULT_RESULT_HPP */ \ No newline at end of file diff --git a/loader/include/Geode/loader/Event.hpp b/loader/include/Geode/loader/Event.hpp index 1ccc3348..b7892388 100644 --- a/loader/include/Geode/loader/Event.hpp +++ b/loader/include/Geode/loader/Event.hpp @@ -1,5 +1,6 @@ #pragma once +#include "../utils/casts.hpp" #include "Mod.hpp" #include @@ -25,9 +26,9 @@ namespace geode { template struct to_member; - template + template struct to_member { - using value = R(C::*)(Args...); + using value = R (C::*)(Args...); }; template @@ -43,10 +44,9 @@ namespace geode { return fn(e); } }; - + template - concept is_filter = - std::is_base_of_v, T> && + concept is_filter = std::is_base_of_v, T> && requires(T a) { a.handle(std::declval(), std::declval()); }; @@ -55,11 +55,13 @@ namespace geode { class EventListener : public EventListenerProtocol { public: using Callback = typename T::Callback; - template requires std::is_class_v + template + requires std::is_class_v using MemberFn = typename to_member::value; ListenerResult passThrough(Event* e) override { - if (auto myev = dynamic_cast(e)) { + // it is so silly to use dynamic cast in an interbinary context + if (auto myev = cast::typeinfo_cast(e)) { return m_filter.handle(m_callback, myev); } return ListenerResult::Propagate; @@ -68,25 +70,31 @@ namespace geode { EventListener(T filter = T()) { this->enable(); } - EventListener(std::function fn, T filter = T()) : m_callback(fn), m_filter(filter) { + + EventListener(std::function fn, T filter = T()) : + m_callback(fn), m_filter(filter) { this->enable(); } + EventListener(Callback* fnptr, T filter = T()) : m_callback(fnptr), m_filter(filter) { this->enable(); } template - EventListener(C* cls, MemberFn fn, T filter = T()) : EventListener(std::bind(fn, cls, std::placeholders::_1), filter) { + EventListener(C* cls, MemberFn fn, T filter = T()) : + EventListener(std::bind(fn, cls, std::placeholders::_1), filter) { this->enable(); } void bind(std::function fn) { m_callback = fn; } + template void bind(C* cls, MemberFn fn) { m_callback = std::bind(fn, cls, std::placeholders::_1); } + protected: std::function m_callback; T m_filter; @@ -96,6 +104,7 @@ namespace geode { static std::unordered_set s_listeners; Mod* m_sender; friend EventListenerProtocol; + public: void postFrom(Mod* sender); diff --git a/loader/include/Geode/loader/ModEvent.hpp b/loader/include/Geode/loader/ModEvent.hpp index 016464da..70fc22a4 100644 --- a/loader/include/Geode/loader/ModEvent.hpp +++ b/loader/include/Geode/loader/ModEvent.hpp @@ -1,9 +1,10 @@ #pragma once #include "Event.hpp" -#include #include "Mod.hpp" +#include + namespace geode { enum class ModEventType { Loaded, @@ -18,40 +19,44 @@ namespace geode { protected: ModEventType m_type; Mod* m_mod; - + public: ModStateEvent(Mod* mod, ModEventType type); ModEventType getType() const; Mod* getMod() const; }; - class GEODE_DLL ModStateFilter : public EventFilter { - public: - using Callback = void(ModStateEvent*); - - protected: + class GEODE_DLL ModStateFilter : public EventFilter { + public: + using Callback = void(ModStateEvent*); + + protected: ModEventType m_type; Mod* m_mod; - - public: + + public: ListenerResult handle(std::function fn, ModStateEvent* event); - ModStateFilter(Mod* mod, ModEventType type); - }; + ModStateFilter(Mod* mod, ModEventType type); + }; } -#define $on_mod(type) \ -template \ -void GEODE_CONCAT(geodeExecFunction, __LINE__)(ModStateEvent*); \ -namespace { \ - struct GEODE_CONCAT(ExecFuncUnique, __LINE__) {}; \ -} \ -static inline auto GEODE_CONCAT(Exec, __LINE__) = (geode::Loader::get()->scheduleOnModLoad(\ - geode::Mod::get(), []() { \ - static auto _ = geode::EventListener( \ - &GEODE_CONCAT(geodeExecFunction, __LINE__),\ - geode::ModStateFilter(geode::Mod::get(), geode::ModEventType::type)\ - ); \ - } \ -), 0); \ -template \ -void GEODE_CONCAT(geodeExecFunction, __LINE__)(ModStateEvent*) +#define $on_mod(type) \ + template \ + void GEODE_CONCAT(geodeExecFunction, __LINE__)(ModStateEvent*); \ + namespace { \ + struct GEODE_CONCAT(ExecFuncUnique, __LINE__) {}; \ + } \ + static inline auto GEODE_CONCAT(Exec, __LINE__) = \ + (geode::Loader::get()->scheduleOnModLoad( \ + geode::Mod::get(), \ + []() { \ + static auto _ = geode::EventListener( \ + &GEODE_CONCAT(geodeExecFunction, __LINE__) < \ + GEODE_CONCAT(ExecFuncUnique, __LINE__) >, \ + geode::ModStateFilter(geode::Mod::get(), geode::ModEventType::type) \ + ); \ + } \ + ), \ + 0); \ + template \ + void GEODE_CONCAT(geodeExecFunction, __LINE__)(ModStateEvent*) diff --git a/loader/include/Geode/modify/Field.hpp b/loader/include/Geode/modify/Field.hpp index 32eb1ade..99824404 100644 --- a/loader/include/Geode/modify/Field.hpp +++ b/loader/include/Geode/modify/Field.hpp @@ -41,17 +41,14 @@ namespace geode::modifier { return node->getFieldContainer(); } }; + + GEODE_DLL size_t getFieldIndexForClass(size_t hash); template class FieldIntermediate { // Padding used for guaranteeing any member of parents // will be in between sizeof(Intermediate) and sizeof(Parent) uintptr_t m_padding; - static inline std::unordered_map nextIndex; - - static size_t getFieldIndexForClass(size_t hash) { - return nextIndex[hash]++; - } public: static void fieldConstructor(void* offsetField) { diff --git a/loader/include/Geode/modify/InternalMacros.hpp b/loader/include/Geode/modify/InternalMacros.hpp index 90ad0975..8ddde752 100644 --- a/loader/include/Geode/modify/InternalMacros.hpp +++ b/loader/include/Geode/modify/InternalMacros.hpp @@ -55,21 +55,3 @@ * Useful for callbacks */ #define $cls std::remove_pointer::type - -#define GEODE_ONLY_FIELD(type, field_, default_) private: field field_ = default_; public: -#define GEODE_INTERNAL_FIELD(type, field, name) inline type& name() { return this->*field; } -//#define GEODE_EXTERNAL_FIELD(type, field, name) static inline type& name##From(void* self) { return reinterpret_cast(self)->*field; } -#define GEODE_FIELD(type, field, name, default_) GEODE_ONLY_FIELD(type, field, default_) GEODE_INTERNAL_FIELD(type, field, name) //GEODE_EXTERNAL_FIELD(type, field, name) - - -#define $execute \ -template \ -void GEODE_CONCAT(geodeExecFunction, __LINE__)(); \ -namespace { \ - struct GEODE_CONCAT(ExecFuncUnique, __LINE__) {}; \ -} \ -static inline auto GEODE_CONCAT(Exec, __LINE__) = (Loader::get()->scheduleOnModLoad(\ - nullptr, &GEODE_CONCAT(geodeExecFunction, __LINE__) \ -), 0); \ -template \ -void GEODE_CONCAT(geodeExecFunction, __LINE__)() diff --git a/loader/include/Geode/utils/JsonValidation.hpp b/loader/include/Geode/utils/JsonValidation.hpp index db9c011b..9de90ac1 100644 --- a/loader/include/Geode/utils/JsonValidation.hpp +++ b/loader/include/Geode/utils/JsonValidation.hpp @@ -1,7 +1,7 @@ #pragma once -#include "../loader/Log.hpp" #include "../external/json/json.hpp" +#include "../loader/Log.hpp" #include #include @@ -15,9 +15,7 @@ namespace geode { template struct is_iterable< - T, - std::void_t< - decltype(std::begin(std::declval())), decltype(std::end(std::declval()))>> : + T, std::void_t())), decltype(std::end(std::declval()))>> : std::true_type {}; template @@ -79,7 +77,7 @@ namespace geode { } template - using JsonValueValidator = bool (*)(T const&); + using JsonValueValidator = std::function; template struct JsonMaybeObject; @@ -175,6 +173,11 @@ namespace geode { return *this; } + template + JsonMaybeValue& validate(bool (*validator)(T const&)) { + return this->validate(std::function(validator)); + } + template JsonMaybeValue& inferType() { if (this->isError() || !m_inferType) return *this; diff --git a/loader/include/Geode/utils/Result.hpp b/loader/include/Geode/utils/Result.hpp index 56c800b4..5ca31b4e 100644 --- a/loader/include/Geode/utils/Result.hpp +++ b/loader/include/Geode/utils/Result.hpp @@ -1,322 +1,230 @@ #pragma once #include "../DefaultInclude.hpp" +#include "../external/result/result.hpp" +#include #include #include +#include #include -#include - -// clang-format off namespace geode { - namespace { - struct AnyType { - explicit AnyType() = delete; - }; - - template - concept ConvertibleToResult = - std::is_convertible_v, std::remove_reference_t> || - std::is_same_v, std::remove_reference_t>; - + namespace impl { using DefaultValue = std::monostate; using DefaultError = std::string; + template + using WrappedResult = std::conditional_t< + std::is_lvalue_reference_v, std::reference_wrapper>, + std::remove_const_t>; - template - struct Storage { - std::variant m_value; + template + class [[nodiscard]] Failure { + public: + WrappedResult m_error; - bool holdsOk() const { - return std::holds_alternative(m_value); + Failure() = default; + + template + requires(std::is_constructible_v) + explicit constexpr Failure(E2 const& e) : m_error(e) {} + + template + requires(std::is_constructible_v) + explicit constexpr Failure(E2&& e) : m_error(std::move(e)) {} + + E& error() & noexcept { + return m_error; } - T getOk() const requires std::is_copy_constructible_v { - return std::get(m_value); + E const& error() const& noexcept { + return m_error; } - T&& getOk() requires(!std::is_copy_constructible_v) { - return std::move(std::get(m_value)); + E&& error() && noexcept { + return static_cast(m_error); } - E getErr() const requires std::is_copy_constructible_v { - return std::get(m_value); + E const&& error() const&& noexcept { + return static_cast(m_error); } - - E&& getErr() requires(!std::is_copy_constructible_v) { - return std::move(std::get(m_value)); - } - - template - requires - std::is_convertible_v && - std::is_convertible_v - Storage(Storage const& other) - requires - std::is_copy_constructible_v && - std::is_copy_constructible_v - { - if (other.holdsOk()) { - m_value = other.getOk(); - } else { - m_value = other.getErr(); - } - } - - Storage(T const& value) - requires std::is_copy_constructible_v - : m_value(value) {} - - Storage(T&& value) - requires std::is_move_constructible_v - : m_value(std::forward(value)) {} - - Storage(E const& value, std::monostate) - requires std::is_copy_constructible_v - : m_value(value) {} - - Storage(E&& value, std::monostate) - requires std::is_move_constructible_v - : m_value(std::forward(value)) {} }; - template - struct Storage { - bool m_holdsOk; - T m_value; + template + class [[nodiscard]] Success { + public: + WrappedResult m_value; - bool holdsOk() const { - return m_holdsOk; - } + Success() = default; - T getOk() const requires std::is_copy_constructible_v { + template + requires(std::is_constructible_v) + explicit constexpr Success(T2 const& v) : m_value(v) {} + + template + requires(std::is_constructible_v) + explicit constexpr Success(T2&& v) : m_value(std::forward(v)) {} + + T& value() & noexcept { return m_value; } - T&& getOk() requires(!std::is_copy_constructible_v) { - return std::move(m_value); - } - - T getErr() const requires std::is_copy_constructible_v { + T const& value() const& noexcept { return m_value; } - T&& getErr() requires(!std::is_copy_constructible_v) { - return std::move(m_value); + T&& value() && noexcept { + return static_cast(m_value); } - template - requires - std::is_convertible_v && - std::is_convertible_v - Storage(Storage const& other) - requires - std::is_copy_constructible_v && - std::is_copy_constructible_v - : m_value(other.holdsOk() ? other.getOk() : other.getErr()), - m_holdsOk(other.holdsOk()) {} - - Storage(T const& value) - requires std::is_copy_constructible_v - : m_value(value), - m_holdsOk(true) {} - - Storage(T&& value) - requires std::is_move_constructible_v - : m_value(std::forward(value)), - m_holdsOk(true) {} - - Storage(T const& value, std::monostate) - requires std::is_copy_constructible_v - : m_value(value), - m_holdsOk(false) {} - - Storage(T&& value, std::monostate) - requires std::is_move_constructible_v - : m_value(std::forward(value)), - m_holdsOk(false) {} + T const&& value() const&& noexcept { + return static_cast(m_value); + } }; } - template - class [[nodiscard]] Result final { + template + class [[nodiscard]] Result : public cpp::result { public: - using value_type = std::remove_reference_t; - using error_type = std::remove_reference_t; + using Base = cpp::result; + using ValueType = typename Base::value_type; + using ErrorType = typename Base::error_type; - // for some reason doing requires causes errors with pch... - static_assert( - std::is_copy_constructible_v || std::is_move_constructible_v, - "T must be copiable or movable!" - ); - static_assert( - std::is_copy_constructible_v || std::is_move_constructible_v, - "E must be copiable or movable!" - ); + using Base::result; - protected: - Storage m_value; + template + requires(cpp::detail::result_is_implicit_value_convertible::value) + constexpr Result(U&& value) = delete; - public: - Storage const& _Raw_Storage() const { - return m_value; + template + requires(std::is_constructible_v) + constexpr Result(impl::Failure const& e) : Base(cpp::failure(e.error())) {} + + template + requires(std::is_constructible_v) + constexpr Result(impl::Failure&& e) : Base(cpp::failure(std::move(e.error()))) {} + + template + requires(std::is_constructible_v) + constexpr Result(impl::Success const& s) : Base(s.value()) {} + + template + requires(std::is_constructible_v) + constexpr Result(impl::Success&& s) : Base(std::move(s.value())) {} + + [[nodiscard]] constexpr explicit operator bool() const noexcept { + return this->Base::operator bool(); } - bool isOk() const { - return m_value.holdsOk(); + [[nodiscard]] constexpr bool isOk() const noexcept { + return this->Base::has_value(); } - bool isErr() const { - return !m_value.holdsOk(); + [[nodiscard]] constexpr bool isErr() const noexcept { + return this->Base::has_error(); } - template - Result expect(const char* str, Args&&... args) { - if (isErr()) { - return Result(fmt::format( - str, - std::forward(args)..., - fmt::arg("error", unwrapErr()) - ), std::monostate()); - } else { + [[nodiscard]] constexpr decltype(auto) unwrap() & { + return this->Base::value(); + } + + [[nodiscard]] constexpr decltype(auto) unwrap() const& { + return this->Base::value(); + } + + [[nodiscard]] constexpr decltype(auto) unwrap() && { + return this->Base::value(); + } + + [[nodiscard]] constexpr decltype(auto) unwrap() const&& { + return this->Base::value(); + } + + [[nodiscard]] constexpr decltype(auto) unwrapErr() & { + return this->Base::error(); + } + + [[nodiscard]] constexpr decltype(auto) unwrapErr() const& { + return this->Base::error(); + } + + [[nodiscard]] constexpr decltype(auto) unwrapErr() && { + return this->Base::error(); + } + + [[nodiscard]] constexpr decltype(auto) unwrapErr() const&& { + return this->Base::error(); + } + + template + [[nodiscard]] Result expect(std::string const& format, Args&&... args) + const { + if (this->isErr()) { + return impl::Failure(fmt::format( + fmt::runtime(format), std::forward(args)..., + fmt::arg("error", this->unwrapErr()) + )); + } + else { return *this; } } - explicit Result(value_type const& value) - requires std::is_copy_constructible_v - : m_value(value) {} - - explicit Result(value_type&& value) - requires std::is_move_constructible_v - : m_value(std::forward(value)) {} - - explicit Result(error_type const& value, std::monostate) - requires std::is_copy_constructible_v - : m_value(value, std::monostate()) {} - - explicit Result(error_type&& value, std::monostate) - requires std::is_move_constructible_v - : m_value(std::forward(value), std::monostate()) {} - - Result(Result const& other) - requires - std::is_copy_constructible_v && - std::is_copy_constructible_v - = default; - - Result(Result&& other) - requires( - !std::is_copy_constructible_v || - !std::is_copy_constructible_v - ) = default; - - template - requires - std::is_convertible_v && - std::is_convertible_v - Result(Result const& other) - requires - std::is_copy_constructible_v && - std::is_copy_constructible_v - : m_value(other._Raw_Storage()) {} - - template - requires ConvertibleToResult - Result(Result const& other) - requires - std::is_copy_constructible_v && - std::is_copy_constructible_v - : Result(value_type(other.unwrap())) {} - - template - requires ConvertibleToResult - Result(Result const& other) - requires - std::is_copy_constructible_v && - std::is_copy_constructible_v - : m_value(std::forward(other.unwrapErr()), std::monostate()) {} - - template - requires ConvertibleToResult - Result(Result&& other) - requires( - !std::is_copy_constructible_v || - !std::is_copy_constructible_v - ) - : m_value(other.unwrap()) {} - - template - requires ConvertibleToResult - Result(Result&& other) - requires( - !std::is_copy_constructible_v || - !std::is_copy_constructible_v - ) - : Result(std::forward(other.unwrapErr()), std::monostate()) {} - - value_type unwrap() const requires std::is_copy_constructible_v { - return m_value.getOk(); + template + [[nodiscard]] constexpr decltype(auto) unwrapOr(U&& val) && { + return this->Base::value_or(std::forward(val)); } - value_type&& unwrap() requires(!std::is_copy_constructible_v) { - return std::forward(m_value.getOk()); + template + [[nodiscard]] constexpr decltype(auto) unwrapOr(U&& val) const& { + return this->Base::value_or(std::forward(val)); } - error_type unwrapErr() const requires std::is_copy_constructible_v { - return m_value.getErr(); + template + [[nodiscard]] constexpr decltype(auto) errorOr(U&& val) && { + return this->Base::error_or(std::forward(val)); } - error_type&& unwrapErr() requires(!std::is_copy_constructible_v) { - return std::forward(m_value.getErr()); - } - - explicit operator bool() const requires( - !std::is_same_v && - !std::is_same_v - ) { - return this->isOk(); + template + [[nodiscard]] constexpr decltype(auto) errorOr(U&& val) const& { + return this->Base::error_or(std::forward(val)); } }; - template - requires std::is_copy_constructible_v - Result Ok(T value = T()) { - return Result(value); + template + constexpr impl::Success Ok() { + return impl::Success(); } - template - requires(!std::is_copy_constructible_v) - Result Ok(T&& value) { - return Result(std::forward(value)); + template + constexpr impl::Success Ok(T&& value) { + return impl::Success(std::forward(value)); } - template - requires std::is_copy_constructible_v - Result Err(E error) { - return Result(error, std::monostate()); + template + constexpr impl::Failure Err(E&& error) { + return impl::Failure(std::forward(error)); } - template - requires(!std::is_copy_constructible_v) - Result Err(E&& error) { - return Result(std::forward(error), std::monostate()); + template + inline impl::Failure Err(std::string const& format, Args&&... args) { + return impl::Failure( + fmt::format(fmt::runtime(format), std::forward(args)...) + ); } - #define GEODE_UNWRAP_INTO(into, ...) \ +#define GEODE_UNWRAP_INTO(into, ...) \ + auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \ + if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \ + return Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \ + } \ + into = std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrap()) + +#define GEODE_UNWRAP(...) \ + { \ auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \ if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \ return Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \ } \ - into = std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrap()) - - #define GEODE_UNWRAP(...) \ - { \ - auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \ - if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \ - return Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \ - } \ - } + } } - -// clang-format on diff --git a/loader/include/Geode/utils/cocos.hpp b/loader/include/Geode/utils/cocos.hpp index acbc9603..a20233bb 100644 --- a/loader/include/Geode/utils/cocos.hpp +++ b/loader/include/Geode/utils/cocos.hpp @@ -1,8 +1,9 @@ #pragma once -#include "casts.hpp" #include "../external/json/json.hpp" +#include "casts.hpp" #include "general.hpp" + #include #include #include @@ -139,11 +140,11 @@ namespace geode { } static cocos2d::CCSize operator-(cocos2d::CCSize const& size, float f) { - return { size.width - f, size.height - f }; + return {size.width - f, size.height - f}; } static cocos2d::CCSize operator-(cocos2d::CCSize const& size) { - return { -size.width, -size.height }; + return {-size.width, -size.height}; } static bool operator==(cocos2d::CCPoint const& p1, cocos2d::CCPoint const& p2) { @@ -283,6 +284,7 @@ namespace geode { bool operator==(T* other) const { return m_obj == other; } + bool operator==(Ref const& other) const { return m_obj == other.m_obj; } @@ -290,6 +292,7 @@ namespace geode { bool operator!=(T* other) const { return m_obj != other; } + bool operator!=(Ref const& other) const { return m_obj != other.m_obj; } @@ -298,6 +301,7 @@ namespace geode { bool operator<(Ref const& other) const { return m_obj < other.m_obj; } + bool operator>(Ref const& other) const { return m_obj > other.m_obj; } @@ -435,13 +439,13 @@ namespace geode::cocos { */ GEODE_DLL cocos2d::CCScene* switchToScene(cocos2d::CCLayer* layer); - using CreateLayerFunc = cocos2d::CCLayer*(*)(); + using CreateLayerFunc = std::function; /** - * Reload textures, overwriting the scene to return to after the loading + * Reload textures, overwriting the scene to return to after the loading * screen is finished - * @param returnTo A function that returns a new layer. After loading is - * finished, the game switches to the given layer instead of MenuLayer. + * @param returnTo A function that returns a new layer. After loading is + * finished, the game switches to the given layer instead of MenuLayer. * Leave nullptr to enable default behaviour */ GEODE_DLL void reloadTextures(CreateLayerFunc returnTo = nullptr); @@ -453,9 +457,7 @@ namespace geode::cocos { * @param def Default size * @param min Minimum size */ - GEODE_DLL void limitNodeSize( - cocos2d::CCNode* node, cocos2d::CCSize const& size, float def, float min - ); + GEODE_DLL void limitNodeSize(cocos2d::CCNode* node, cocos2d::CCSize const& size, float def, float min); /** * Checks if a node is visible (recursively @@ -478,12 +480,12 @@ namespace geode::cocos { GEODE_DLL cocos2d::CCNode* getChildByTagRecursive(cocos2d::CCNode* node, int tag); /** - * Get first node that conforms to the predicate + * Get first node that conforms to the predicate * by traversing children recursively - * + * * @param node Parent node * @param predicate Predicate used to evaluate nodes - * @return Child node if one is found, or null if + * @return Child node if one is found, or null if * there is none */ template @@ -497,8 +499,7 @@ namespace geode::cocos { for (int i = 0; i < children->count(); ++i) { auto newParent = static_cast(children->objectAtIndex(i)); auto child = findFirstChildRecursive(newParent, predicate); - if (child) - return child; + if (child) return child; } return nullptr; @@ -602,13 +603,18 @@ namespace geode::cocos { } inline cocos2d::ccColor4B invert4B(cocos2d::ccColor4B const& color) { - return { static_cast(255 - color.r), static_cast(255 - color.g), - static_cast(255 - color.b), color.a }; + return { + static_cast(255 - color.r), + static_cast(255 - color.g), + static_cast(255 - color.b), + color.a}; } inline cocos2d::ccColor3B invert3B(cocos2d::ccColor3B const& color) { - return { static_cast(255 - color.r), static_cast(255 - color.g), - static_cast(255 - color.b) }; + return { + static_cast(255 - color.r), + static_cast(255 - color.g), + static_cast(255 - color.b)}; } inline cocos2d::ccColor3B lighten3B(cocos2d::ccColor3B const& color, int amount) { @@ -624,34 +630,38 @@ namespace geode::cocos { } inline cocos2d::ccColor3B to3B(cocos2d::ccColor4B const& color) { - return { color.r, color.g, color.b }; + return {color.r, color.g, color.b}; } inline cocos2d::ccColor4B to4B(cocos2d::ccColor3B const& color, GLubyte alpha = 255) { - return { color.r, color.g, color.b, alpha }; + return {color.r, color.g, color.b, alpha}; } inline cocos2d::ccColor4F to4F(cocos2d::ccColor4B const& color) { - return { color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f }; + return {color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f}; } constexpr cocos2d::ccColor3B cc3x(int hexValue) { if (hexValue <= 0xf) - return cocos2d::ccColor3B { static_cast(hexValue * 17), - static_cast(hexValue * 17), - static_cast(hexValue * 17) }; + return cocos2d::ccColor3B{ + static_cast(hexValue * 17), + static_cast(hexValue * 17), + static_cast(hexValue * 17)}; if (hexValue <= 0xff) - return cocos2d::ccColor3B { static_cast(hexValue), - static_cast(hexValue), - static_cast(hexValue) }; + return cocos2d::ccColor3B{ + static_cast(hexValue), + static_cast(hexValue), + static_cast(hexValue)}; if (hexValue <= 0xfff) - return cocos2d::ccColor3B { static_cast((hexValue >> 8 & 0xf) * 17), - static_cast((hexValue >> 4 & 0xf) * 17), - static_cast((hexValue >> 0 & 0xf) * 17) }; + return cocos2d::ccColor3B{ + static_cast((hexValue >> 8 & 0xf) * 17), + static_cast((hexValue >> 4 & 0xf) * 17), + static_cast((hexValue >> 0 & 0xf) * 17)}; else - return cocos2d::ccColor3B { static_cast(hexValue >> 16 & 0xff), - static_cast(hexValue >> 8 & 0xff), - static_cast(hexValue >> 0 & 0xff) }; + return cocos2d::ccColor3B{ + static_cast(hexValue >> 16 & 0xff), + static_cast(hexValue >> 8 & 0xff), + static_cast(hexValue >> 0 & 0xff)}; } GEODE_DLL Result cc3bFromHexString(std::string const& hexValue); @@ -668,9 +678,7 @@ namespace geode::cocos { } template >> - static cocos2d::CCArray* vectorToCCArray( - std::vector const& vec, std::function convFunc - ) { + static cocos2d::CCArray* vectorToCCArray(std::vector const& vec, std::function convFunc) { auto res = cocos2d::CCArray::createWithCapacity(vec.size()); for (auto const& item : vec) res->addObject(convFunc(item)); @@ -680,8 +688,7 @@ namespace geode::cocos { template >> std::vector ccArrayToVector(cocos2d::CCArray* arr) { return std::vector( - reinterpret_cast(arr->data->arr), - reinterpret_cast(arr->data->arr) + arr->data->num + reinterpret_cast(arr->data->arr), reinterpret_cast(arr->data->arr) + arr->data->num ); } @@ -698,9 +705,7 @@ namespace geode::cocos { template < typename K, typename V, typename C, typename = std::enable_if_t || std::is_same_v>> - static cocos2d::CCDictionary* mapToCCDict( - std::map const& map, std::function convFunc - ) { + static cocos2d::CCDictionary* mapToCCDict(std::map const& map, std::function convFunc) { auto res = cocos2d::CCDictionary::create(); for (auto const& [key, value] : map) res->setObject(value, convFunc(key)); @@ -725,7 +730,7 @@ namespace geode::cocos { // std specializations namespace std { // enables using Ref as the key in unordered_map etc. - template + template struct hash> { size_t operator()(geode::Ref const& ref) const { return std::hash()(ref.data()); @@ -797,6 +802,7 @@ namespace geode::cocos { } return CCArrayIterator(reinterpret_cast(m_arr->data->arr) + m_arr->count()); } + size_t size() const { return m_arr ? m_arr->count() : 0; } @@ -829,10 +835,10 @@ namespace geode::cocos { std::pair operator*() { if constexpr (std::is_same::value) { - return { m_ptr->getStrKey(), static_cast(m_ptr->getObject()) }; + return {m_ptr->getStrKey(), static_cast(m_ptr->getObject())}; } else { - return { m_ptr->getIntKey(), static_cast(m_ptr->getObject()) }; + return {m_ptr->getIntKey(), static_cast(m_ptr->getObject())}; } } @@ -997,7 +1003,7 @@ namespace geode::cocos { using Selector = Ret (Base::*)(Args...); using Holder = LambdaHolder; - static inline Holder s_selector {}; + static inline Holder s_selector{}; Ret selector(Args... args) { return s_selector(std::forward(args)...); diff --git a/loader/include/Geode/utils/web.hpp b/loader/include/Geode/utils/web.hpp index 94b08f4a..8cbe6935 100644 --- a/loader/include/Geode/utils/web.hpp +++ b/loader/include/Geode/utils/web.hpp @@ -1,8 +1,8 @@ #pragma once #include "../DefaultInclude.hpp" -#include "Result.hpp" #include "../external/json/json.hpp" +#include "Result.hpp" #include "general.hpp" #include @@ -10,7 +10,7 @@ namespace geode::utils::web { GEODE_DLL void openLinkInBrowser(std::string const& url); - + using FileProgressCallback = std::function; /** @@ -38,8 +38,7 @@ namespace geode::utils::web { * @returns Returned data as JSON, or error on error */ GEODE_DLL Result<> fetchFile( - std::string const& url, ghc::filesystem::path const& into, - FileProgressCallback prog = nullptr + std::string const& url, ghc::filesystem::path const& into, FileProgressCallback prog = nullptr ); /** diff --git a/loader/src/cocos2d-ext/zip/zip.cpp b/loader/src/cocos2d-ext/zip/zip.cpp index ad37b22e..30490bce 100644 --- a/loader/src/cocos2d-ext/zip/zip.cpp +++ b/loader/src/cocos2d-ext/zip/zip.cpp @@ -2,7 +2,8 @@ Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) - Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( + http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) @@ -12,15 +13,17 @@ Changes Oct-2009 - Mathias Svensson - Remove old C style function prototypes Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives - Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. - Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data - It is used when recreting zip archive with RAW when deleting items from a zip. - ZIP64 data is automatically added to items that needs it, and existing ZIP64 data need to be removed. - Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) - Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of + some functions. Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field + data from its ZIP64 data It is used when recreting zip archive with RAW when deleting items from + a zip. ZIP64 data is automatically added to items that needs it, and existing ZIP64 data need to + be removed. Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib + is required) Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer */ +// clang-format off + #ifndef NOCRYPT #define NOCRYPT #endif @@ -1071,7 +1074,7 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, int err = ZIP_OK; # ifdef NOCRYPT - (crcForCrypting); + (void)(crcForCrypting); if (password != NULL) return ZIP_PARAMERROR; # endif @@ -2006,3 +2009,5 @@ extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHe } } // end of namespace cocos2d + +// clang-format on diff --git a/loader/src/hooks/GeodeNodeMetadata.cpp b/loader/src/hooks/GeodeNodeMetadata.cpp index 70a0f1a8..777635ab 100644 --- a/loader/src/hooks/GeodeNodeMetadata.cpp +++ b/loader/src/hooks/GeodeNodeMetadata.cpp @@ -64,7 +64,6 @@ public: }; // proxy forwards -// clang-format off #include struct ProxyCCNode : Modify { virtual CCObject* getUserObject() { @@ -80,7 +79,10 @@ struct ProxyCCNode : Modify { } }; -// clang-format on +static inline std::unordered_map s_nextIndex; +size_t modifier::getFieldIndexForClass(size_t hash) { + return s_nextIndex[hash]++; +} // not const because might modify contents FieldContainer* CCNode::getFieldContainer() { diff --git a/loader/src/loader/Event.cpp b/loader/src/loader/Event.cpp index 2b4244e5..1275dd9a 100644 --- a/loader/src/loader/Event.cpp +++ b/loader/src/loader/Event.cpp @@ -13,7 +13,7 @@ void EventListenerProtocol::disable() { } EventListenerProtocol::~EventListenerProtocol() { - this->disable(); + this->disable(); } Event::~Event() {} diff --git a/loader/src/loader/Hook.cpp b/loader/src/loader/Hook.cpp index c7d1564e..79d666f9 100644 --- a/loader/src/loader/Hook.cpp +++ b/loader/src/loader/Hook.cpp @@ -20,10 +20,10 @@ Result<> Hook::enable() { m_enabled = true; m_handle = res.unwrap(); return Ok(); - } else { + } + else { return Err( - "Unable to create hook at " + - std::to_string(reinterpret_cast(m_address)) + "Unable to create hook at " + std::to_string(reinterpret_cast(m_address)) ); } return Err("Hook already has a handle"); @@ -33,8 +33,7 @@ Result<> Hook::enable() { Result<> Hook::disable() { if (m_enabled) { - if (!geode::core::hook::remove(m_handle)) - return Err("Unable to remove hook"); + if (!geode::core::hook::remove(m_handle)) return Err("Unable to remove hook"); log::debug("Disabling hook at function {}", m_displayName); m_enabled = false; diff --git a/loader/src/loader/Mod.cpp b/loader/src/loader/Mod.cpp index b7f98eac..bc97503e 100644 --- a/loader/src/loader/Mod.cpp +++ b/loader/src/loader/Mod.cpp @@ -1,20 +1,17 @@ -#include -#include -#include - -#include #include +#include #include - #include #include +#include +#include +#include USE_GEODE_NAMESPACE(); Mod::Mod(ModInfo const& info) { m_info = info; - m_saveDirPath = Loader::get()->getGeodeSaveDirectory() / - GEODE_MOD_DIRECTORY / info.m_id; + m_saveDirPath = Loader::get()->getGeodeSaveDirectory() / GEODE_MOD_DIRECTORY / info.m_id; ghc::filesystem::create_directories(m_saveDirPath); } @@ -27,57 +24,75 @@ Mod::~Mod() { ghc::filesystem::path Mod::getSaveDir() const { return m_saveDirPath; } + std::string Mod::getID() const { return m_info.m_id; } + std::string Mod::getName() const { return m_info.m_name; } + std::string Mod::getDeveloper() const { return m_info.m_developer; } + std::optional Mod::getDescription() const { return m_info.m_description; } + std::optional Mod::getDetails() const { return m_info.m_details; } + ModInfo Mod::getModInfo() const { return m_info; } + ghc::filesystem::path Mod::getTempDir() const { return m_tempDirName; } + ghc::filesystem::path Mod::getBinaryPath() const { return m_tempDirName / m_info.m_binaryName; } + ghc::filesystem::path Mod::getPackagePath() const { return m_info.m_path; } + VersionInfo Mod::getVersion() const { return m_info.m_version; } + bool Mod::isEnabled() const { return m_enabled; } + bool Mod::isLoaded() const { return m_binaryLoaded; } + bool Mod::supportsDisabling() const { return m_info.m_supportsDisabling; } + bool Mod::supportsUnloading() const { return m_info.m_supportsUnloading; } + bool Mod::wasSuccesfullyLoaded() const { return !this->isEnabled() || this->isLoaded(); } + std::vector Mod::getHooks() const { return m_hooks; } + bool Mod::hasSettings() const { return m_info.m_settings.size(); } + decltype(ModInfo::m_settings) Mod::getSettings() const { return m_info.m_settings; } @@ -104,16 +119,19 @@ Result<> Mod::loadData() { // load its value if (!setting->load(value.json())) return Err("Unable to load value for setting \"" + key + "\""); - } else { + } + else { log::log( - Severity::Warning, this, + Severity::Warning, + this, "Encountered unknown setting \"{}\" while loading " "settings", key ); } } - } catch (std::exception& e) { + } + catch (std::exception& e) { return Err(std::string("Unable to parse settings: ") + e.what()); } } @@ -124,7 +142,8 @@ Result<> Mod::loadData() { GEODE_UNWRAP_INTO(auto data, utils::file::readString(savedPath)); try { m_saved = nlohmann::json::parse(data); - } catch(std::exception& e) { + } + catch (std::exception& e) { return Err(std::string("Unable to parse saved values: ") + e.what()); } } @@ -138,20 +157,13 @@ Result<> Mod::saveData() { // Settings auto json = nlohmann::json::object(); for (auto& [key, value] : m_info.m_settings) { - if (!value->save(json[key])) - return Err("Unable to save setting \"" + key + "\""); + if (!value->save(json[key])) return Err("Unable to save setting \"" + key + "\""); } - GEODE_UNWRAP(utils::file::writeString( - m_saveDirPath / "settings.json", - json.dump(4) - )); + GEODE_UNWRAP(utils::file::writeString(m_saveDirPath / "settings.json", json.dump(4))); // Saved values - GEODE_UNWRAP(utils::file::writeString( - m_saveDirPath / "saved.json", - m_saved.dump(4) - )); + GEODE_UNWRAP(utils::file::writeString(m_saveDirPath / "saved.json", m_saved.dump(4))); return Ok(); } @@ -180,8 +192,7 @@ Result<> Mod::loadBinary() { if (!m_binaryLoaded) { GEODE_UNWRAP(this->createTempDir()); - if (this->hasUnresolvedDependencies()) - return Err("Mod has unresolved dependencies"); + if (this->hasUnresolvedDependencies()) return Err("Mod has unresolved dependencies"); GEODE_UNWRAP(this->loadPlatformBinary()); m_binaryLoaded = true; @@ -206,9 +217,8 @@ Result<> Mod::loadBinary() { Result<> Mod::unloadBinary() { if (m_binaryLoaded) { - if (!m_info.m_supportsUnloading) - return Err("Mod does not support unloading"); - + if (!m_info.m_supportsUnloading) return Err("Mod does not support unloading"); + GEODE_UNWRAP(this->saveData()); GEODE_UNWRAP(this->disable()); @@ -235,8 +245,7 @@ Result<> Mod::unloadBinary() { } Result<> Mod::enable() { - if (!m_binaryLoaded) - return this->loadBinary(); + if (!m_binaryLoaded) return this->loadBinary(); for (auto const& hook : m_hooks) { GEODE_UNWRAP(this->enableHook(hook)); @@ -255,8 +264,7 @@ Result<> Mod::enable() { Result<> Mod::disable() { if (m_enabled) { - if (!m_info.m_supportsDisabling) - return Err("Mod does not support disabling"); + if (!m_info.m_supportsDisabling) return Err("Mod does not support disabling"); ModStateEvent(this, ModEventType::Disabled).post(); @@ -278,8 +286,7 @@ Result<> Mod::uninstall() { if (m_info.m_supportsDisabling) { GEODE_UNWRAP(this->disable()); - if (m_info.m_supportsUnloading) - GEODE_UNWRAP(this->unloadBinary()); + if (m_info.m_supportsUnloading) GEODE_UNWRAP(this->unloadBinary()); } if (!ghc::filesystem::remove(m_info.m_path)) { @@ -308,15 +315,15 @@ bool Dependency::isUnresolved() const { bool Mod::updateDependencyStates() { bool hasUnresolved = false; for (auto& dep : m_info.m_dependencies) { - if (!dep.m_mod) - dep.m_mod = Loader::get()->getLoadedMod(dep.m_id); + if (!dep.m_mod) dep.m_mod = Loader::get()->getLoadedMod(dep.m_id); if (dep.m_mod) { dep.m_mod->updateDependencyStates(); if (dep.m_mod->hasUnresolvedDependencies()) { dep.m_state = ModResolveState::Unresolved; - } else { + } + else { if (!dep.m_mod->m_resolved) { dep.m_mod->m_resolved = true; dep.m_state = ModResolveState::Resolved; @@ -325,15 +332,18 @@ bool Mod::updateDependencyStates() { dep.m_state = ModResolveState::Unloaded; log::log(Severity::Error, dep.m_mod, "{}", r.unwrapErr()); } - } else { + } + else { if (dep.m_mod->isEnabled()) { dep.m_state = ModResolveState::Loaded; - } else { + } + else { dep.m_state = ModResolveState::Disabled; } } } - } else { + } + else { dep.m_state = ModResolveState::Unloaded; } if (dep.isUnresolved()) { @@ -352,7 +362,8 @@ bool Mod::updateDependencyStates() { if (!r) { log::error("{} Error loading: {}", this, r.unwrapErr()); } - } else { + } + else { log::debug("Resolved {}, however not loading it as it is disabled", m_info.m_id); } } @@ -361,8 +372,7 @@ bool Mod::updateDependencyStates() { bool Mod::hasUnresolvedDependencies() const { for (auto const& dep : m_info.m_dependencies) { - if (dep.isUnresolved()) - return true; + if (dep.isUnresolved()) return true; } return false; } @@ -370,8 +380,7 @@ bool Mod::hasUnresolvedDependencies() const { std::vector Mod::getUnresolvedDependencies() { std::vector unresolved; for (auto const& dep : m_info.m_dependencies) { - if (dep.isUnresolved()) - unresolved.push_back(dep); + if (dep.isUnresolved()) unresolved.push_back(dep); } return unresolved; } @@ -386,9 +395,8 @@ bool Mod::depends(std::string const& id) const { Result<> Mod::enableHook(Hook* hook) { auto res = hook->enable(); - if (res) - m_hooks.push_back(hook); - + if (res) m_hooks.push_back(hook); + return res; } @@ -403,11 +411,12 @@ Result Mod::addHook(Hook* hook) { delete hook; return Err("Can't create hook"); } - } else { + } + else { InternalLoader::get()->addInternalHook(hook, this); } - return Ok(hook); + return Ok(hook); } Result<> Mod::removeHook(Hook* hook) { @@ -440,7 +449,7 @@ Result Mod::patch(void* address, byte_array data) { return Err("Unable to enable patch at " + std::to_string(p->getAddress())); } m_patches.push_back(p); - return Ok(p); + return Ok(p); } Result<> Mod::unpatch(Patch* patch) { @@ -473,8 +482,7 @@ Result<> Mod::createTempDir() { GEODE_UNWRAP_INTO(auto unzip, file::Unzip::create(m_info.m_path)); if (!unzip.hasEntry(m_info.m_binaryName)) { return Err(fmt::format( - "Unable to find platform binary under the name \"{}\"", - m_info.m_binaryName + "Unable to find platform binary under the name \"{}\"", m_info.m_binaryName )); } GEODE_UNWRAP(unzip.extractAllTo(tempPath)); @@ -495,10 +503,9 @@ ghc::filesystem::path Mod::getConfigDir(bool create) const { return dir; } -const char* Mod::expandSpriteName(const char* name) { - static std::unordered_map expanded = {}; - if (expanded.count(name)) - return expanded[name]; +char const* Mod::expandSpriteName(char const* name) { + static std::unordered_map expanded = {}; + if (expanded.count(name)) return expanded[name]; auto exp = new char[strlen(name) + 2 + m_info.m_id.size()]; auto exps = m_info.m_id + "/" + name; diff --git a/loader/src/loader/ModEvent.cpp b/loader/src/loader/ModEvent.cpp index 85ed083f..753d8e5d 100644 --- a/loader/src/loader/ModEvent.cpp +++ b/loader/src/loader/ModEvent.cpp @@ -19,7 +19,4 @@ ListenerResult ModStateFilter::handle(std::function fn, ModStateEvent* return ListenerResult::Propagate; } -ModStateFilter::ModStateFilter( - Mod* mod, - ModEventType type -) : m_mod(mod), m_type(type) {} +ModStateFilter::ModStateFilter(Mod* mod, ModEventType type) : m_mod(mod), m_type(type) {} diff --git a/loader/src/loader/ModInfo.cpp b/loader/src/loader/ModInfo.cpp index a5f2bc4e..c2c9f1e9 100644 --- a/loader/src/loader/ModInfo.cpp +++ b/loader/src/loader/ModInfo.cpp @@ -123,7 +123,7 @@ Result ModInfo::create(ModJson const& json) { if (schema < Loader::minModVersion()) { return Err( "[mod.json] is built for an older version (" + schema.toString() + - ") of Geode (current: " + Loader::minModVersion().toString() + + ") of Geode (current: " + Loader::getVersion().toString() + "). Please update the mod to the latest version, " "and if the problem persists, contact the developer " "to update it." @@ -132,7 +132,7 @@ Result ModInfo::create(ModJson const& json) { if (schema > Loader::maxModVersion()) { return Err( "[mod.json] is built for a newer version (" + schema.toString() + - ") of Geode (current: " + Loader::maxModVersion().toString() + + ") of Geode (current: " + Loader::getVersion().toString() + "). You need to update Geode in order to use " "this mod." ); diff --git a/loader/src/main.cpp b/loader/src/main.cpp index 9a26ba3b..2a57c3a6 100644 --- a/loader/src/main.cpp +++ b/loader/src/main.cpp @@ -1,11 +1,11 @@ #include "../core/Core.hpp" +#include #include #include #include -#include #include -#include +#include #include #include #include @@ -102,27 +102,25 @@ BOOL WINAPI DllMain(HINSTANCE lib, DWORD reason, LPVOID) { #define $_ GEODE_CONCAT(unnamedVar_, __LINE__) -static auto $_ = listenForSettingChanges( - "show-platform-console", - [](BoolSetting* setting) { +static auto $_ = + listenForSettingChanges("show-platform-console", [](BoolSetting* setting) { if (setting->getValue()) { Loader::get()->openPlatformConsole(); } else { Loader::get()->closePlatfromConsole(); } - } -); + }); -static auto $_ = listenForIPC("ipc-test", +[](IPCEvent* event) -> nlohmann::json { +static auto $_ = listenForIPC("ipc-test", [](IPCEvent* event) -> nlohmann::json { return "Hello from Geode!"; }); -static auto $_ = listenForIPC("loader-info", +[](IPCEvent* event) -> nlohmann::json { +static auto $_ = listenForIPC("loader-info", [](IPCEvent* event) -> nlohmann::json { return Loader::get()->getInternalMod()->getModInfo(); }); -static auto $_ = listenForIPC("list-mods", +[](IPCEvent* event) -> nlohmann::json { +static auto $_ = listenForIPC("list-mods", [](IPCEvent* event) -> nlohmann::json { std::vector res; auto args = event->getMessageData(); @@ -133,18 +131,14 @@ static auto $_ = listenForIPC("list-mods", +[](IPCEvent* event) -> nlohmann::jso auto dontIncludeLoader = root.has("dont-include-loader").template get(); if (!dontIncludeLoader) { - res.push_back(includeRunTimeInfo ? - Loader::get()->getInternalMod()->getRuntimeInfo() : - Loader::get()->getInternalMod()->getModInfo().toJSON() + res.push_back( + includeRunTimeInfo ? Loader::get()->getInternalMod()->getRuntimeInfo() : + Loader::get()->getInternalMod()->getModInfo().toJSON() ); } for (auto& mod : Loader::get()->getAllMods()) { - res.push_back( - includeRunTimeInfo ? - mod->getRuntimeInfo() : - mod->getModInfo().toJSON() - ); + res.push_back(includeRunTimeInfo ? mod->getRuntimeInfo() : mod->getModInfo().toJSON()); } return res; diff --git a/loader/src/platform/mac/Cocos2d.cpp b/loader/src/platform/mac/Cocos2d.cpp index 1eabd25c..16af07d2 100644 --- a/loader/src/platform/mac/Cocos2d.cpp +++ b/loader/src/platform/mac/Cocos2d.cpp @@ -5,10 +5,6 @@ #include using namespace cocos2d; -void CCFileUtils::removeAllPaths() { - m_searchPathArray.clear(); -} - CCObject* CCObject::copy() { return copyWithZone(0); diff --git a/loader/src/platform/mac/InternalLoader.cpp b/loader/src/platform/mac/InternalLoader.cpp index ca54d58e..8c791e2a 100644 --- a/loader/src/platform/mac/InternalLoader.cpp +++ b/loader/src/platform/mac/InternalLoader.cpp @@ -1,12 +1,12 @@ -#include -#include -#include -#include #include +#include +#include +#include +#include #ifdef GEODE_IS_MACOS -#include + #include void InternalLoader::platformMessageBox(char const* title, std::string const& info) { CFStringRef cfTitle = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); @@ -29,46 +29,37 @@ void InternalLoader::closePlatformConsole() { m_platformConsoleOpen = false; } -CFDataRef msgPortCallback( - CFMessagePortRef port, - SInt32 messageID, - CFDataRef data, - void* info -) { - if(!CFDataGetLength(data)) - return NULL; +CFDataRef msgPortCallback(CFMessagePortRef port, SInt32 messageID, CFDataRef data, void* info) { + if (!CFDataGetLength(data)) return NULL; - std::string cdata( - reinterpret_cast(CFDataGetBytePtr(data)), - CFDataGetLength(data) - ); + std::string cdata(reinterpret_cast(CFDataGetBytePtr(data)), CFDataGetLength(data)); std::string reply = InternalLoader::processRawIPC(port, cdata); - return CFDataCreate(NULL, (const UInt8*)reply.data(), reply.size()); + return CFDataCreate(NULL, (UInt8 const*)reply.data(), reply.size()); } void InternalLoader::setupIPC() { std::thread([]() { CFStringRef portName = CFStringCreateWithCString(NULL, IPC_PORT_NAME, kCFStringEncodingUTF8); - CFMessagePortRef localPort = CFMessagePortCreateLocal( - NULL, - portName, - msgPortCallback, - NULL, - NULL - ); + CFMessagePortRef localPort = + CFMessagePortCreateLocal(NULL, portName, msgPortCallback, NULL, NULL); + if (localPort == NULL) { + log::warn("Unable to create port, quitting IPC"); + return; + } CFRunLoopSourceRef runLoopSource = CFMessagePortCreateRunLoopSource(NULL, localPort, 0); - CFRunLoopAddSource( - CFRunLoopGetCurrent(), - runLoopSource, - kCFRunLoopCommonModes - ); + if (runLoopSource == NULL) { + log::warn("Unable to create loop source, quitting IPC"); + return; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes); CFRunLoopRun(); CFRelease(localPort); }).detach(); - log::log(Severity::Warning, InternalMod::get(), "IPC is not supported on this platform"); + log::debug("IPC set up"); } #endif diff --git a/loader/src/platform/windows/InternalLoader.cpp b/loader/src/platform/windows/InternalLoader.cpp index 638b084f..ad07754f 100644 --- a/loader/src/platform/windows/InternalLoader.cpp +++ b/loader/src/platform/windows/InternalLoader.cpp @@ -1,8 +1,8 @@ -#include #include #include -#include +#include #include +#include USE_GEODE_NAMESPACE(); @@ -77,10 +77,10 @@ void InternalLoader::setupIPC() { nullptr ); if (pipe == INVALID_HANDLE_VALUE) { - // todo: Rn this quits IPC, but we might wanna change that later - // to just continue trying. however, I'm assuming that if - // CreateNamedPipeA fails, then it will probably fail again if - // you try right after, so changing the break; to continue; might + // todo: Rn this quits IPC, but we might wanna change that later + // to just continue trying. however, I'm assuming that if + // CreateNamedPipeA fails, then it will probably fail again if + // you try right after, so changing the break; to continue; might // just result in the console getting filled with error messages log::warn("Unable to create pipe, quitting IPC"); break; @@ -89,14 +89,15 @@ void InternalLoader::setupIPC() { if (ConnectNamedPipe(pipe, nullptr)) { // log::debug("Got connection, creating thread"); std::thread(&ipcPipeThread, pipe).detach(); - } else { + } + else { // log::debug("No connection, cleaning pipe"); CloseHandle(pipe); } } }).detach(); - log::log(Severity::Debug, InternalMod::get(), "IPC set up"); + log::debug("IPC set up"); } #endif diff --git a/loader/src/ui/nodes/Notification.cpp b/loader/src/ui/nodes/Notification.cpp index 92f6ffeb..ff53d95a 100644 --- a/loader/src/ui/nodes/Notification.cpp +++ b/loader/src/ui/nodes/Notification.cpp @@ -1,20 +1,19 @@ #include -#include #include +#include USE_GEODE_NAMESPACE(); -constexpr auto NOTIFICATION_FADEIN = .3f; +constexpr auto NOTIFICATION_FADEIN = .3f; constexpr auto NOTIFICATION_FADEOUT = 1.f; Ref Notification::s_queue = CCArray::create(); bool Notification::init(std::string const& text, CCSprite* icon, float time) { - if (!CCNodeRGBA::init()) - return false; - + if (!CCNodeRGBA::init()) return false; + m_time = time; - + m_bg = CCScale9Sprite::create("square02b_small.png", { 0, 0, 40, 40 }); m_bg->setColor({ 0, 0, 0 }); this->addChild(m_bg); @@ -48,7 +47,8 @@ void Notification::updateLayout() { if (m_icon) { m_icon->setPosition({ size.height / 2, size.height / 2 }); m_label->setPosition(size / 2 + CCSize { spaceForIcon / 2, .0f }); - } else { + } + else { m_label->setPosition(size / 2); } } @@ -73,9 +73,7 @@ CCSprite* Notification::createIcon(NotificationIcon icon) { case NotificationIcon::Loading: { auto icon = CCSprite::create("loadingCircle.png"); - icon->runAction(CCRepeatForever::create( - CCRotateBy::create(1.f, 360.f) - )); + icon->runAction(CCRepeatForever::create(CCRotateBy::create(1.f, 360.f))); icon->setBlendFunc({ GL_ONE, GL_ONE }); return icon; } break; @@ -94,11 +92,7 @@ CCSprite* Notification::createIcon(NotificationIcon icon) { } } -Notification* Notification::create( - std::string const& text, - NotificationIcon icon, - float time -) { +Notification* Notification::create(std::string const& text, NotificationIcon icon, float time) { return Notification::create(text, createIcon(icon), time); } @@ -125,7 +119,8 @@ void Notification::setIcon(cocos2d::CCSprite* icon) { if (m_icon) { m_icon->removeFromParent(); } - if (m_icon = icon) { + m_icon = icon; + if (icon) { m_bg->addChild(icon); } this->updateLayout(); @@ -171,8 +166,7 @@ void Notification::show() { CCCallFunc::create(this, callfunc_selector(Notification::animateIn)), // wait for fade-in to finish CCDelayTime::create(NOTIFICATION_FADEIN), - CCCallFunc::create(this, callfunc_selector(Notification::wait)), - nullptr + CCCallFunc::create(this, callfunc_selector(Notification::wait)), nullptr )); } @@ -181,8 +175,7 @@ void Notification::wait() { if (m_time) { this->runAction(CCSequence::create( CCDelayTime::create(m_time), - CCCallFunc::create(this, callfunc_selector(Notification::hide)), - nullptr + CCCallFunc::create(this, callfunc_selector(Notification::hide)), nullptr )); } } @@ -193,8 +186,6 @@ void Notification::hide() { CCCallFunc::create(this, callfunc_selector(Notification::animateOut)), // wait for fade-out to finish CCDelayTime::create(NOTIFICATION_FADEOUT), - CCCallFunc::create(this, callfunc_selector(Notification::showNextNotification)), - nullptr + CCCallFunc::create(this, callfunc_selector(Notification::showNextNotification)), nullptr )); } - diff --git a/loader/src/utils/JsonValidation.cpp b/loader/src/utils/JsonValidation.cpp index 044ab481..3303e564 100644 --- a/loader/src/utils/JsonValidation.cpp +++ b/loader/src/utils/JsonValidation.cpp @@ -205,14 +205,12 @@ JsonMaybeValue JsonMaybeValue::at(size_t i) { return *this; } return JsonMaybeValue( - self().m_checker, self().m_json.at(i), self().m_hierarchy + "." + std::to_string(i), - self().m_hasValue + self().m_checker, self().m_json.at(i), self().m_hierarchy + "." + std::to_string(i), self().m_hasValue ); } template -typename JsonMaybeValue::template Iterator> JsonMaybeValue< - Json>::iterate() { +typename JsonMaybeValue::template Iterator> JsonMaybeValue::iterate() { this->as(); Iterator> iter; if (this->isError()) return iter; @@ -226,8 +224,8 @@ typename JsonMaybeValue::template Iterator> JsonMaybe } template -typename JsonMaybeValue::template Iterator>> -JsonMaybeValue::items() { +typename JsonMaybeValue::template Iterator>> JsonMaybeValue< + Json>::items() { this->as(); Iterator>> iter; if (this->isError()) return iter; @@ -235,9 +233,7 @@ JsonMaybeValue::items() { for (auto& [k, v] : self().m_json.items()) { iter.m_values.emplace_back( k, - JsonMaybeValue( - self().m_checker, v, self().m_hierarchy + "." + k, self().m_hasValue - ) + JsonMaybeValue(self().m_checker, v, self().m_hierarchy + "." + k, self().m_hasValue) ); } diff --git a/loader/src/utils/addresser.cpp b/loader/src/utils/addresser.cpp index a7c102f2..d9a4c9b8 100644 --- a/loader/src/utils/addresser.cpp +++ b/loader/src/utils/addresser.cpp @@ -14,8 +14,7 @@ macro(GEODE_CONCAT(begin, 7)), macro(GEODE_CONCAT(begin, 8)), \ macro(GEODE_CONCAT(begin, 9)), macro(GEODE_CONCAT(begin, a)), \ macro(GEODE_CONCAT(begin, b)), macro(GEODE_CONCAT(begin, c)), \ - macro(GEODE_CONCAT(begin, d)), macro(GEODE_CONCAT(begin, e)), \ - macro(GEODE_CONCAT(begin, f)) + macro(GEODE_CONCAT(begin, d)), macro(GEODE_CONCAT(begin, e)), macro(GEODE_CONCAT(begin, f)) #define GEODE_ADDRESSER_NEST2(macro, begin) \ GEODE_ADDRESSER_NEST1(macro, GEODE_CONCAT(begin, 0)), \ @@ -53,13 +52,13 @@ namespace { using table_table_t = intptr_t[0x100]; struct GEODE_HIDDEN ThunkTable { - static inline thunk0_table_t table = { GEODE_ADDRESSER_THUNK0_SET() }; + static inline thunk0_table_t table = {GEODE_ADDRESSER_THUNK0_SET()}; }; class GEODE_HIDDEN TableTable { friend class geode::addresser::Addresser; - static inline table_table_t table = { GEODE_ADDRESSER_TABLE_SET() }; + static inline table_table_t table = {GEODE_ADDRESSER_TABLE_SET()}; }; } diff --git a/loader/src/utils/cocos.cpp b/loader/src/utils/cocos.cpp index 36cb10f0..7f79ef30 100644 --- a/loader/src/utils/cocos.cpp +++ b/loader/src/utils/cocos.cpp @@ -1,13 +1,13 @@ -#include #include +#include USE_GEODE_NAMESPACE(); void cocos2d::to_json(nlohmann::json& json, ccColor3B const& color) { - json = nlohmann::json { - { "r", color.r }, - { "g", color.g }, - { "b", color.b }, + json = nlohmann::json{ + {"r", color.r}, + {"g", color.g}, + {"b", color.b}, }; } @@ -20,9 +20,7 @@ void cocos2d::from_json(nlohmann::json const& json, ccColor3B& color) { json.at(2).get_to(color.b); } else { - throw nlohmann::json::type_error::create( - 0, "Expected color array to have 3 items", json - ); + throw nlohmann::json::type_error::create(0, "Expected color array to have 3 items", json); } } // object @@ -57,11 +55,11 @@ void cocos2d::from_json(nlohmann::json const& json, ccColor3B& color) { } void cocos2d::to_json(nlohmann::json& json, ccColor4B const& color) { - json = nlohmann::json { - { "r", color.r }, - { "g", color.g }, - { "b", color.b }, - { "a", color.a }, + json = nlohmann::json{ + {"r", color.r}, + {"g", color.g}, + {"b", color.b}, + {"a", color.a}, }; } @@ -75,9 +73,7 @@ void cocos2d::from_json(nlohmann::json const& json, ccColor4B& color) { json.at(3).get_to(color.a); } else { - throw nlohmann::json::type_error::create( - 0, "Expected color array to have 4 items", json - ); + throw nlohmann::json::type_error::create(0, "Expected color array to have 4 items", json); } } // object @@ -245,7 +241,7 @@ CCRect geode::cocos::calculateNodeCoverage(std::vector const& nodes) { for (auto child : nodes) { auto pos = child->getPosition() - child->getScaledContentSize() * child->getAnchorPoint(); auto csize = child->getPosition() + - child->getScaledContentSize() * (CCPoint { 1.f, 1.f } - child->getAnchorPoint()); + child->getScaledContentSize() * (CCPoint{1.f, 1.f} - child->getAnchorPoint()); if (pos.x < coverage.origin.x) { coverage.origin.x = pos.x; } @@ -267,7 +263,7 @@ CCRect geode::cocos::calculateNodeCoverage(CCArray* nodes) { for (auto child : CCArrayExt(nodes)) { auto pos = child->getPosition() - child->getScaledContentSize() * child->getAnchorPoint(); auto csize = child->getPosition() + - child->getScaledContentSize() * (CCPoint { 1.f, 1.f } - child->getAnchorPoint()); + child->getScaledContentSize() * (CCPoint{1.f, 1.f} - child->getAnchorPoint()); if (pos.x < coverage.origin.x) { coverage.origin.x = pos.x; } @@ -288,9 +284,7 @@ CCRect geode::cocos::calculateChildCoverage(CCNode* parent) { return calculateNodeCoverage(parent->getChildren()); } -void geode::cocos::limitNodeSize( - cocos2d::CCNode* spr, cocos2d::CCSize const& size, float def, float min -) { +void geode::cocos::limitNodeSize(cocos2d::CCNode* spr, cocos2d::CCSize const& size, float def, float min) { spr->setScale(1.f); auto [cwidth, cheight] = spr->getContentSize(); @@ -346,7 +340,7 @@ void geode::cocos::reloadTextures(CreateLayerFunc returnTo) { GameManager::get()->reloadAll(false, false, true); } -class $modify(LoadingLayer) { +struct LoadingFinished : Modify { void loadAssets() { // loadFinished is inlined on Macchew OS :sob: diff --git a/loader/src/utils/file.cpp b/loader/src/utils/file.cpp index e44b025c..e2a295b8 100644 --- a/loader/src/utils/file.cpp +++ b/loader/src/utils/file.cpp @@ -1,11 +1,11 @@ -#include -#include -#include -#include -#include #include <../support/zip_support/ZipUtils.h> #include <../support/zip_support/ioapi.h> #include <../support/zip_support/unzip.h> +#include +#include +#include +#include +#include USE_GEODE_NAMESPACE(); using namespace geode::utils::file; @@ -143,9 +143,7 @@ public: unz_file_info64 fileInfo; // Read first file - if (unzGoToFirstFile64( - m_zip, &fileInfo, fileName, sizeof(fileName) - 1 - )) { + if (unzGoToFirstFile64(m_zip, &fileInfo, fileName, sizeof(fileName) - 1)) { return false; } // Loop over all files @@ -154,17 +152,16 @@ public: unz_file_pos pos; if (unzGetFilePos(m_zip, &pos) == UNZ_OK) { m_entries.insert({ - fileName, ZipEntry { + fileName, + ZipEntry { .m_pos = pos, .m_compressedSize = fileInfo.compressed_size, .m_uncompressedSize = fileInfo.uncompressed_size, - } + }, }); } // Read next file, or break on error - if (unzGoToNextFile64( - m_zip, &fileInfo, fileName, sizeof(fileName) - 1 - ) != UNZ_OK) { + if (unzGoToNextFile64(m_zip, &fileInfo, fileName, sizeof(fileName) - 1) != UNZ_OK) { break; } } @@ -204,6 +201,7 @@ public: } UnzipImpl(unzFile zip, Path const& path) : m_zip(zip), m_zipPath(path) {} + ~UnzipImpl() { unzClose(m_zip); } diff --git a/loader/src/utils/string.cpp b/loader/src/utils/string.cpp index 27a7ecd1..1200acc5 100644 --- a/loader/src/utils/string.cpp +++ b/loader/src/utils/string.cpp @@ -91,9 +91,7 @@ std::wstring utils::string::toUpper(std::wstring const& str) { return utils::string::toUpperIP(ret); } -std::string& utils::string::replaceIP( - std::string& str, std::string const& orig, std::string const& repl -) { +std::string& utils::string::replaceIP(std::string& str, std::string const& orig, std::string const& repl) { std::string::size_type n = 0; while ((n = str.find(orig, n)) != std::string::npos) { str.replace(n, orig.size(), repl); @@ -248,7 +246,8 @@ std::wstring& utils::string::trimLeftIP(std::wstring& str) { std::string& utils::string::trimRightIP(std::string& str) { str.erase( std::find_if( - str.rbegin(), str.rend(), + str.rbegin(), + str.rend(), [](auto ch) { return !std::isspace(ch); } @@ -261,7 +260,8 @@ std::string& utils::string::trimRightIP(std::string& str) { std::wstring& utils::string::trimRightIP(std::wstring& str) { str.erase( std::find_if( - str.rbegin(), str.rend(), + str.rbegin(), + str.rend(), [](auto ch) { return !std::isspace(ch); } diff --git a/loader/src/utils/web.cpp b/loader/src/utils/web.cpp index dc362d9b..8d25333b 100644 --- a/loader/src/utils/web.cpp +++ b/loader/src/utils/web.cpp @@ -125,7 +125,7 @@ Result web::fetch(std::string const& url) { return Err("Error getting info: " + std::string(curl_easy_strerror(res))); } -static std::unordered_map RUNNING_REQUESTS {}; +static std::unordered_map RUNNING_REQUESTS{}; static std::mutex RUNNING_REQUESTS_MUTEX; SentAsyncWebRequest::SentAsyncWebRequest(AsyncWebRequest const& req, std::string const& id) : @@ -190,10 +190,11 @@ SentAsyncWebRequest::SentAsyncWebRequest(AsyncWebRequest const& req, std::string struct ProgressData { SentAsyncWebRequest* self; std::ofstream* file; - } data { this, file.get() }; + } data{this, file.get()}; curl_easy_setopt( - curl, CURLOPT_PROGRESSFUNCTION, + curl, + CURLOPT_PROGRESSFUNCTION, +[](void* ptr, double total, double now, double, double) -> int { auto data = static_cast(ptr); while (data->self->m_paused) {} @@ -350,7 +351,7 @@ SentAsyncWebRequestHandle AsyncWebRequest::send() { else { auto id = m_joinID.value_or("__anon_request_" + std::to_string(COUNTER++)); ret = std::make_shared(*this, id); - RUNNING_REQUESTS.insert({ id, ret }); + RUNNING_REQUESTS.insert({id, ret}); } // resume all running requests