diff --git a/include/continuable/detail/expected.hpp b/include/continuable/detail/expected.hpp index 514fb93..6b02524 100644 --- a/include/continuable/detail/expected.hpp +++ b/include/continuable/detail/expected.hpp @@ -31,8 +31,9 @@ #ifndef CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED__ #define CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED__ -#include #include +#include +#include #include #include @@ -40,85 +41,86 @@ namespace cti { namespace detail { -namespace expected { +namespace util { namespace detail { enum class slot_t { empty, value, error }; -template -struct expected_base_util { - slot_t slot_; +template +using storage_of_t = // + std::aligned_storage_t<(sizeof(types::error_type) > sizeof(T) + ? sizeof(types::error_type) + : sizeof(T))>; -protected: - template - void init(T&& value, slot_t slot) { - set(slot); - using type = std::decay_t; - auto storage = &base()->storage_; - new (storage) type(std::forward(value)); - } - void destroy() { - weak_destroy(); - set(slot_t::empty); - } - void weak_destroy() { - base()->visit([&](auto&& value) { - using type = std::decay_t; - value.~type(); - }); - } - slot_t get() const noexcept { - return slot_; - } - bool is(slot_t slot) const noexcept { - return get() == slot; - } - void set(slot_t slot) { - slot_ = slot; - } - slot_t consume(slot_t slot) { - auto current = get(); - destroy(); - return current; - } - Base* base() noexcept { - return static_cast(this); - } - Base const* base() const noexcept { - return static_cast(this); - } -}; +template +struct expected_copy_base { + constexpr expected_copy_base() = default; -template -struct expected_base : expected_base_util { - explicit expected_base(expected_base const& right) { - right.visit([&](auto&& value) { - this->init(std::forward(value)); + expected_copy_base(expected_copy_base const&) = default; + explicit expected_copy_base(expected_copy_base&& right) + // TODO noexcept(Base::is_nothrow_move_constructible) + { + Base& me = *static_cast(this); + Base& other = *static_cast(&right); + + other.visit([&](auto&& value) { + // ... + me.init(std::move(value)); }); - set(right.consume()); + set(other.get()); } - expected_base& operator=(expected_base const& right) { - this->weak_destroy(); - right.visit([&](auto&& value) { - this->init(std::forward(value)); + expected_copy_base& operator=(expected_copy_base const&) = default; + expected_copy_base& operator=(expected_copy_base&& right) + // TODO noexcept(Base::is_nothrow_move_constructible) + { + Base& me = *static_cast(this); + Base& other = *static_cast(&right); + + me.weak_destroy(); + + other.visit([&](auto&& value) { + // ... + me.init(std::move(value)); }); - set(right.consume()); + set(other.get()); return *this; } }; -template -struct expected_base : expected_base_util { - explicit expected_base(expected_base const& right) { - right.visit([&](auto&& value) { - this->init(std::forward(value)); +template +struct expected_copy_base { + constexpr expected_copy_base() = default; + + expected_copy_base(expected_copy_base const&) = default; + explicit expected_copy_base(expected_copy_base&& right) = delete; + expected_copy_base& operator=(expected_copy_base const&) = default; + expected_copy_base& operator=(expected_copy_base&& right) = delete; +}; +template +struct expected_move_base { + constexpr expected_move_base() = default; + + expected_move_base(expected_move_base&&) = default; + explicit expected_move_base(expected_move_base const& right) { + Base& me = *static_cast(this); + Base const& other = *static_cast(&right); + + other.visit([&](auto&& value) { + // ... + me.init(std::move(value)); }); - set(right.consume()); + set(other.consume()); } - expected_base& operator=(expected_base const& right) { - this->weak_destroy(); - right.visit([&](auto&& value) { - this->init(std::forward(value)); + expected_move_base& operator=(expected_move_base&&) = default; + expected_move_base& operator=(expected_move_base const& right) { + Base& me = *static_cast(this); + Base const& other = *static_cast(&right); + + me.weak_destroy(); + + other.visit([&](auto&& value) { + // ... + me.init(std::move(value)); }); - set(right.consume()); + set(other.consume()); return *this; } }; @@ -127,59 +129,121 @@ struct expected_base : expected_base_util { /// A class similar to the one in the expected proposal, /// however it is capable of carrying an exception_ptr if /// exceptions are used. -template > -class expected { - friend class expected_base; +template +class expected + : detail::expected_move_base>, + detail::expected_copy_base< + expected, std::is_copy_constructible::value && + std::is_copy_constructible::value> { - Storage storage_; + template + friend class expected_move_base; + template + friend class expected_copy_base; + + detail::storage_of_t storage_; + detail::slot_t slot_ = detail::slot_t::empty; + + template + expected(V&& value, detail::slot_t const slot) { + using type = std::decay_t; + new (&storage_) type(std::forward(value)); + set(slot); + } public: - explicit expected() : slot_(slot_t::empty) { + explicit expected(T value) // + : expected(std::move(value), detail::slot_t::value) { + } + explicit expected(types::error_type error) // + : expected(std::move(error), detail::slot_t::error) { } - explicit expected(T value) : slot_(slot_t::value) { - } + expected(expected const&) = default; + expected(expected&& right) = default; + expected& operator=(expected const&) = default; + expected& operator=(expected&& right) = default; - explicit expected(types::error_type error) : slot_(slot_t::value) { - } - - bool is_empty() const noexcept { - return slot_ == slot_t::empty; - } bool is_value() const noexcept { - return slot_ == slot_t::value; + return slot_ == detail::slot_t::value; } bool is_error() const noexcept { - return slot_ == slot_t::error; + return slot_ == detail::slot_t::error; + } + +protected: + bool is_empty() const noexcept { + return slot_ == detail::slot_t::empty; } template void visit(V&& visitor) { switch (slot_) { - case slot_t::value: + case detail::slot_t::value: return std::forward(visitor)(static_cast(&storage_)); - case slot_t::error: + case detail::slot_t::error: return std::forward(visitor)( static_cast(&storage_)); + default: + // We don't visit when there is no value + break; } - - util::unreachable(); } template void visit(V&& visitor) const { switch (slot_) { - case slot_t::value: + case detail::slot_t::value: return std::forward(visitor)(static_cast(&storage_)); - case slot_t::error: + case detail::slot_t::error: return std::forward(visitor)( static_cast(&storage_)); + default: + // We don't visit when there is no value + break; } + } - util::unreachable(); + template + void init(V&& value, detail::slot_t const slot) { + assert(is(slot_t::empty)); + set(slot); + using type = std::decay_t; + auto storage = &storage_; + new (storage) type(std::forward(value)); + } + void destroy() { + weak_destroy(); + +#ifdef NDEBUG + set(detail::slot_t::empty); +#endif + } + void weak_destroy() { + visit([&](auto&& value) { + using type = std::decay_t; + value.~type(); + }); + +#ifndef NDEBUG + set(detail::slot_t::empty); +#endif + } + detail::slot_t get() const noexcept { + return slot_; + } + bool is(detail::slot_t const slot) const noexcept { + return get() == slot; + } + void set(detail::slot_t const slot) { + slot_ = slot; + } + detail::slot_t consume(detail::slot_t slot) { + auto const current = get(); + destroy(); + return current; } }; -} // namespace expected +} // namespace util } // namespace detail } // namespace cti diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index 218aa2e..9e3f554 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -13,6 +13,7 @@ foreach(STEP RANGE 4) ${CMAKE_CURRENT_LIST_DIR}/test-continuable-connection-all.cpp ${CMAKE_CURRENT_LIST_DIR}/test-continuable-connection-any.cpp ${CMAKE_CURRENT_LIST_DIR}/test-continuable-connection-seq.cpp + ${CMAKE_CURRENT_LIST_DIR}/test-continuable-expected.cpp ${CMAKE_CURRENT_LIST_DIR}/test-continuable-erasure.cpp ${CMAKE_CURRENT_LIST_DIR}/test-continuable-regression.cpp ${CMAKE_CURRENT_LIST_DIR}/test-continuable-transforms.cpp) diff --git a/test/unit-test/test-continuable-expected.cpp b/test/unit-test/test-continuable-expected.cpp new file mode 100644 index 0000000..9926db0 --- /dev/null +++ b/test/unit-test/test-continuable-expected.cpp @@ -0,0 +1,33 @@ + +/* + Copyright(c) 2015 - 2017 Denis Blank + + 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. +**/ + +#include "test-continuable.hpp" + +using cti::detail::util::expected; +using cti::detail::types::error_type; + +TEST(expected_tests, can_carry_errors) { + expected e(1); + + auto i = e; +}