diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/cxx/expected.hpp b/iceoryx_hoofs/include/iceoryx_hoofs/cxx/expected.hpp index 7c2038a9d4..2a374cceed 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/cxx/expected.hpp +++ b/iceoryx_hoofs/include/iceoryx_hoofs/cxx/expected.hpp @@ -181,8 +181,8 @@ class IOX_NO_DISCARD expected : public FunctionalInterface : public FunctionalInterface&& store) noexcept; variant m_store; static constexpr uint64_t ERROR_INDEX = 0U; @@ -295,8 +296,8 @@ class IOX_NO_DISCARD expected /// @brief calls the move assignment operator of the contained success value /// or the error value - depending on what is stored in the expected - /// @note The move assign operator does not explicitly invalidate the moved-from object but relies on the move - /// assign operator of ValueType or ErrorType to correctly invalidate the stored object + /// @note The move assignment operator does not explicitly invalidate the moved-from object but relies on the move + /// assignment operator of ValueType or ErrorType to correctly invalidate the stored object expected& operator=(expected&& rhs) noexcept; /// @brief constructs an expected which is signaling success and uses the value @@ -358,38 +359,37 @@ class IOX_NO_DISCARD expected bool has_error() const noexcept; /// @brief returns a reference to the contained error value, if the expected - /// does not contain an error this is undefined behavior + /// does not contain an error the error handler is called /// @return reference to the internally contained error ErrorType& get_error() & noexcept; /// @brief returns a const reference to the contained error value, if the expected - /// does not contain an error this is undefined behavior + /// does not contain an error the error handler is called /// @return const reference to the internally contained error const ErrorType& get_error() const& noexcept; /// @brief returns a rvalue reference to the contained error value, if the expected - /// does not contain an error this is undefined behavior + /// does not contain an error the error handler is called /// @return rvalue reference to the internally contained error ErrorType&& get_error() && noexcept; /// @brief returns a reference to the contained success value, if the expected - /// does not contain a success value this is undefined behavior + /// does not contain a success value the error handler is called /// @return reference to the internally contained value ValueType& value() & noexcept; /// @brief returns a const reference to the contained success value, if the expected - /// does not contain a success value this is undefined behavior + /// does not contain a success value the error handler is called /// @return const reference to the internally contained value const ValueType& value() const& noexcept; /// @brief returns a reference to the contained success value, if the expected - /// does not contain a success value this is undefined behavior + /// does not contain a success value the error handler is called /// @return rvalue reference to the internally contained value ValueType&& value() && noexcept; /// @brief dereferencing operator which returns a reference to the contained - /// success value. if the expected contains an error the behavior is - /// undefined. + /// success value. if the expected contains an error the error handler is called /// @return reference to the contained value /// @code /// cxx::expected frodo(success(45)); @@ -399,8 +399,7 @@ class IOX_NO_DISCARD expected ValueType& operator*() noexcept; /// @brief dereferencing operator which returns a reference to the contained - /// success value. if the expected contains an error the behavior is - /// undefined. + /// success value. if the expected contains an error the error handler is called /// @return const reference to the contained value /// @code /// cxx::expected frodo(success(45)); @@ -409,8 +408,8 @@ class IOX_NO_DISCARD expected /// @endcode const ValueType& operator*() const noexcept; - /// @brief arrow operator which returns the pointer to the contained success value. - /// if the expected contains an error the behavior is undefined. + /// @brief arrow operator which returns the pointer to the contained success value + /// if the expected contains an error the error handler is called /// @return pointer of type ValueType to the contained value /// @code /// cxx::expected, int> holyPiotr(success>({1,2,3})); @@ -418,8 +417,8 @@ class IOX_NO_DISCARD expected /// @endcode ValueType* operator->() noexcept; - /// @brief arrow operator which returns the pointer to the contained success value. - /// if the expected contains an error the behavior is undefined. + /// @brief arrow operator which returns the pointer to the contained success value + /// if the expected contains an error the the error handler is called /// @return pointer of type const ValueType to the contained value /// @code /// cxx::expected, int> holyPiotr(success>({1,2,3})); @@ -449,6 +448,8 @@ class IOX_NO_DISCARD expected private: explicit expected(variant&& store) noexcept; + const ErrorType& get_error_unchecked() const noexcept; + const ValueType& value_unchecked() const noexcept; variant m_store; static constexpr uint64_t VALUE_INDEX = 0U; static constexpr uint64_t ERROR_INDEX = 1U; @@ -461,7 +462,6 @@ class IOX_NO_DISCARD expected : public expected using expected::expected; }; - } // namespace cxx } // namespace iox diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/expected.inl b/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/expected.inl index 3b0e191d1e..e4d3628b34 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/expected.inl +++ b/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/expected.inl @@ -156,11 +156,26 @@ inline bool expected::has_error() const noexcept template inline ErrorType&& expected::get_error() && noexcept { - return std::move(*m_store.template get_at_index()); + return std::move(get_error()); } template inline ErrorType& expected::get_error() & noexcept +{ + // AXIVION Next Construct AutosarC++19_03-A5.2.3 : const cast to avoid code duplication + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + return const_cast(const_cast*>(this)->get_error()); +} + +template +inline const ErrorType& expected::get_error() const& noexcept +{ + ExpectsWithMsg(has_error(), "Trying to access an error but a value is stored!"); + return get_error_unchecked(); +} + +template +inline const ErrorType& expected::get_error_unchecked() const noexcept { return *m_store.template get_at_index(); } @@ -168,31 +183,28 @@ inline ErrorType& expected::get_error() & noexcept template inline ValueType&& expected::value() && noexcept { - return std::move(*m_store.template get_at_index()); + return std::move(value()); } template inline const ValueType& expected::value() const& noexcept { - return *m_store.template get_at_index(); + ExpectsWithMsg(!has_error(), "Trying to access a value but an error is stored!"); + return value_unchecked(); } template inline ValueType& expected::value() & noexcept { - return *m_store.template get_at_index(); -} - -template -inline const ErrorType& expected::get_error() const& noexcept -{ - return *m_store.template get_at_index(); + // AXIVION Next Construct AutosarC++19_03-A5.2.3 : const cast to avoid code duplication + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + return const_cast(const_cast*>(this)->value()); } template inline ValueType* expected::operator->() noexcept { - return m_store.template get_at_index(); + return &value(); } template @@ -206,7 +218,7 @@ inline const ValueType* expected::operator->() const noexc template inline ValueType& expected::operator*() noexcept { - return *m_store.template get_at_index(); + return value(); } template @@ -217,6 +229,12 @@ inline const ValueType& expected::operator*() const noexce return const_cast(const_cast(this)->operator*()); } +template +const ValueType& expected::value_unchecked() const noexcept +{ + return *m_store.template get_at_index(); +} + template template inline expected::operator expected() const noexcept @@ -359,17 +377,26 @@ inline bool expected::has_error() const noexcept template inline ErrorType&& expected::get_error() && noexcept { - return std::move(*m_store.template get_at_index()); + return std::move(get_error()); } -template -inline const ErrorType& expected::get_error() const& noexcept +template +inline ErrorType& expected::get_error() & noexcept { - return *m_store.template get_at_index(); + // AXIVION Next Construct AutosarC++19_03-A5.2.3 : const cast to avoid code duplication + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + return const_cast(const_cast*>(this)->get_error()); } template -inline ErrorType& expected::get_error() & noexcept +inline const ErrorType& expected::get_error() const& noexcept +{ + ExpectsWithMsg(has_error(), "Trying to access an error but a value is stored!"); + return get_error_unchecked(); +} + +template +inline const ErrorType& expected::get_error_unchecked() const noexcept { return *m_store.template get_at_index(); } diff --git a/iceoryx_hoofs/test/moduletests/test_cxx_expected.cpp b/iceoryx_hoofs/test/moduletests/test_cxx_expected.cpp index c80d616a3c..27d8afed5e 100644 --- a/iceoryx_hoofs/test/moduletests/test_cxx_expected.cpp +++ b/iceoryx_hoofs/test/moduletests/test_cxx_expected.cpp @@ -578,4 +578,125 @@ TEST_F(expected_test, MoveAssignmentIsNotEnforcedInMoveConstructor) ASSERT_THAT(destination.has_error(), Eq(true)); } } + +TEST_F(expected_test, AccessingErrorOfLValueErrorOnlyExpectedWhichContainsValueLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "da162edf-06b5-47d2-b35f-361d6004a6c4"); + auto sut = expected::create_value(); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ sut.get_error(); }, "Trying to access an error but a value is stored"); +} + +TEST_F(expected_test, AccessingErrorOfConstLValueErrorOnlyExpectedWhichContainsValueLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "324cab7d-ba04-4ff0-870f-79af993c272f"); + const auto sut = expected::create_value(); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ sut.get_error(); }, "Trying to access an error but a value is stored"); +} + +TEST_F(expected_test, AccessingErrorOfRValueErrorOnlyExpectedWhichContainsValueLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "0a4309e8-d9f3-41a9-9c4b-bdcfda917277"); + auto sut = expected::create_value(); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ std::move(sut).get_error(); }, "Trying to access an error but a value is stored"); +} + +TEST_F(expected_test, AccessingValueOfLValueExpectedWhichContainsErrorWithArrowOpLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "1a821c6f-83db-4fe1-8adf-873afa1251a1"); + auto sut = expected::create_error(TestError::ERROR1); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ IOX_DISCARD_RESULT(sut->m_a); }, "Trying to access a value but an error is stored"); +} + +TEST_F(expected_test, AccessingValueOfConstLValueExpectedWhichContainsErrorWithArrowOpLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "c4f04d7c-9fa3-48f6-a6fd-b8e4e47b7632"); + const auto sut = expected::create_error(TestError::ERROR1); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ IOX_DISCARD_RESULT(sut->m_a); }, "Trying to access a value but an error is stored"); +} + +TEST_F(expected_test, AccessingValueOfLValueExpectedWhichContainsErrorWithDerefOpLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "08ce6a3f-3813-46de-8e1e-3ffe8087521e"); + auto sut = expected::create_error(TestError::ERROR1); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ *sut; }, "Trying to access a value but an error is stored"); +} + +TEST_F(expected_test, AccessingValueOfConstLValueExpectedWhichContainsErrorWithDerefOpLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "838dd364-f91f-40a7-9720-2b662a045b1e"); + const auto sut = expected::create_error(TestError::ERROR1); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ *sut; }, "Trying to access a value but an error is stored"); +} + +TEST_F(expected_test, AccessingValueOfLValueExpectedWhichContainsErrorLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "92139583-b8d6-4d83-ae7e-f4109b98d214"); + auto sut = expected::create_error(TestError::ERROR1); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ sut.value(); }, "Trying to access a value but an error is stored"); +} + +TEST_F(expected_test, AccessingValueOfConstLValueExpectedWhichContainsErrorLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "1bcbb835-8b4c-4430-a534-a26573c2380d"); + const auto sut = expected::create_error(TestError::ERROR1); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ sut.value(); }, "Trying to access a value but an error is stored"); +} + + +TEST_F(expected_test, AccessingValueOfRValueExpectedWhichContainsErrorLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "32d59b52-81f5-417a-8670-dfb2c54fedfb"); + auto sut = expected::create_error(TestError::ERROR1); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ std::move(sut).value(); }, "Trying to access a value but an error is stored"); +} + +TEST_F(expected_test, AccessingErrorOfLValueExpectedWhichContainsValueLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "aee85ead-e066-49fd-99fe-6f1a6045756d"); + constexpr int VALID_VALUE{42}; + auto sut = expected::create_value(VALID_VALUE, VALID_VALUE); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ sut.get_error(); }, "Trying to access an error but a value is stored"); +} + +TEST_F(expected_test, AccessingErrorOfConstLValueExpectedWhichContainsValueLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "a49cf02e-b165-4fd6-9c24-65cedc6cddb9"); + constexpr int VALID_VALUE{42}; + const auto sut = expected::create_value(VALID_VALUE, VALID_VALUE); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ sut.get_error(); }, "Trying to access an error but a value is stored"); +} + +TEST_F(expected_test, AccessingErrorOfRValueExpectedWhichContainsValueLeadsToErrorHandlerCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "0ea90b5d-1af6-494a-b35c-da103bed2331"); + constexpr int VALID_VALUE{42}; + auto sut = expected::create_value(VALID_VALUE, VALID_VALUE); + // @todo iox-#1613 remove EXPECT_DEATH + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cppcoreguidelines-avoid-goto, hicpp-avoid-goto, hicpp-vararg) + EXPECT_DEATH({ std::move(sut).get_error(); }, "Trying to access an error but a value is stored"); +} } // namespace