diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/cxx/variant.hpp b/iceoryx_hoofs/include/iceoryx_hoofs/cxx/variant.hpp index a3b0c65022b..897d9dcb0e7 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/cxx/variant.hpp +++ b/iceoryx_hoofs/include/iceoryx_hoofs/cxx/variant.hpp @@ -1,5 +1,5 @@ // Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -158,12 +158,16 @@ class variant /// @brief if the variant contains an element the elements move constructor is called /// otherwise an empty variant is moved /// @param[in] rhs source of the move + /// @note The move c'tor does not explicitly invalidate the moved-from object but relies on the move c'tor of + /// Types to correctly invalidate the stored object constexpr variant(variant&& rhs) noexcept; /// @brief if the variant contains an element the elements move assignment operator is called /// otherwise an empty variant is moved /// @param[in] rhs source of the move assignment /// @return reference to the variant itself + /// @note The move assign operator does not explicitly invalidate the moved-from object but relies on the move + /// assign operator of Types to correctly invalidate the stored object constexpr variant& operator=(variant&& rhs) noexcept; /// @brief if the variant contains an element the elements destructor is called otherwise @@ -273,12 +277,33 @@ class variant static void error_message(const char* source, const char* msg) noexcept; void call_element_destructor() noexcept; + + template + friend constexpr bool operator==(const variant& lhs, const variant& rhs); + template + friend constexpr bool operator!=(const variant& lhs, const variant& rhs); }; /// @brief returns true if the variant holds a given type T, otherwise false template constexpr bool holds_alternative(const variant& variant) noexcept; +/// @brief equality check for two distinct variant types +/// @tparam Types variadic number of types which can be stored in the variant +/// @param[in] lhs left side of the comparision +/// @param[in] rhs right side of the comparision +/// @return true if the variants are equal, otherwise false +template +constexpr bool operator==(const variant& lhs, const variant& rhs); + +/// @brief inequality check for two distinct variant types +/// @tparam Types variadic number of types which can be stored in the variant +/// @param[in] lhs left side of the comparision +/// @param[in] rhs right side of the comparision +/// @return true if the variants are not equal, otherwise false +template +constexpr bool operator!=(const variant& lhs, const variant& rhs); + } // namespace cxx } // namespace iox diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/variant.inl b/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/variant.inl index 02d4d0c9f60..d21b9eba9df 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/variant.inl +++ b/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/variant.inl @@ -1,5 +1,5 @@ // Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // Copyright (c) 2021 by Perforce All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,6 +19,7 @@ #define IOX_HOOFS_CXX_VARIANT_INL #include "iceoryx_hoofs/cxx/variant.hpp" +#include "iceoryx_hoofs/log/logging.hpp" namespace iox { @@ -288,7 +289,7 @@ inline bool variant::has_bad_variant_element_access() const noexcept template inline void variant::error_message(const char* source, const char* msg) noexcept { - std::cerr << source << " ::: " << msg << std::endl; + IOX_LOG(ERROR) << source << " ::: " << msg; } template @@ -296,6 +297,26 @@ inline constexpr bool holds_alternative(const variant& variant) noexce { return variant.template get() != nullptr; } + +template +constexpr bool operator==(const variant& lhs, const variant& rhs) +{ + if ((lhs.index() == INVALID_VARIANT_INDEX) && (rhs.index() == INVALID_VARIANT_INDEX)) + { + return true; + } + if (lhs.index() != rhs.index()) + { + return false; + } + return internal::call_at_index<0, Types...>::equality(lhs.index(), lhs.m_storage, rhs.m_storage); +} + +template +constexpr bool operator!=(const variant& lhs, const variant& rhs) +{ + return !(lhs == rhs); +} } // namespace cxx } // namespace iox diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/variant_internal.hpp b/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/variant_internal.hpp index 2ae9fe73997..b71d0d869a7 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/variant_internal.hpp +++ b/iceoryx_hoofs/include/iceoryx_hoofs/internal/cxx/variant_internal.hpp @@ -163,6 +163,17 @@ struct call_at_index call_at_index::copyConstructor(index, source, destination); } } + + static bool equality(const uint64_t index, const byte_t* lhs, const byte_t* rhs) + { + if (N == index) + { + // AXIVION Next Construct AutosarC++19_03-A5.2.4 : Type safety ensured through template parameter and encapsulated in this class + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return *reinterpret_cast(lhs) == *reinterpret_cast(rhs); + } + return call_at_index::equality(index, lhs, rhs); + } }; template @@ -245,6 +256,20 @@ struct call_at_index assert(false && "Could not call copy constructor for variant element"); } } + + // NOLINTJUSTIFICATION 'operator new()' needs non-const 'destination' + // NOLINTNEXTLINE(readability-non-const-parameter) + static bool equality(const uint64_t index, const byte_t* lhs, const byte_t* rhs) noexcept + { + if (N == index) + { + // AXIVION Next Construct AutosarC++19_03-A5.2.4 : Type safety ensured through template parameter and encapsulated in this class + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return *reinterpret_cast(lhs) == *reinterpret_cast(rhs); + } + assert(false && "Could not call equality operator for variant element"); + return false; + } }; } // namespace internal diff --git a/iceoryx_hoofs/test/moduletests/test_cxx_variant.cpp b/iceoryx_hoofs/test/moduletests/test_cxx_variant.cpp index adedff8e626..e77c4c1f086 100644 --- a/iceoryx_hoofs/test/moduletests/test_cxx_variant.cpp +++ b/iceoryx_hoofs/test/moduletests/test_cxx_variant.cpp @@ -17,6 +17,7 @@ #include "iceoryx_hoofs/cxx/variant.hpp" #include "test.hpp" +#include #include namespace @@ -703,4 +704,41 @@ TEST_F(variant_Test, CopyVariantIntoVariantOfDifferentType) EXPECT_THAT(DoubleDelete::dtorCalls, Eq(1)); } + +TEST_F(variant_Test, TwoInvalidVariantsAreEqual) +{ + ::testing::Test::RecordProperty("TEST_ID", "4b2b7516-48ef-4aa6-a0c5-43a204e1e348"); + iox::cxx::variant sut1; + iox::cxx::variant sut2; + EXPECT_TRUE(sut1 == sut2); +} + +TEST_F(variant_Test, InvalidAndValidVariantAreUnequal) +{ + ::testing::Test::RecordProperty("TEST_ID", "0c77c24f-059b-4298-b4a2-0a7d8eb70364"); + std::string string{"Foo"}; + iox::cxx::variant sut1{string}; + iox::cxx::variant sut2; + EXPECT_FALSE(sut1 == sut2); +} + +TEST_F(variant_Test, TwoVariantsWithEqualValuesAreEqual) +{ + ::testing::Test::RecordProperty("TEST_ID", "6496566e-647d-426b-b369-7ec27c6ee673"); + std::string string{"Foo"}; + iox::cxx::variant sut1{string}; + iox::cxx::variant sut2{string}; + EXPECT_TRUE(sut1 == sut2); +} + +TEST_F(variant_Test, TwoVariantsWithUnequalValueAreUnequal) +{ + ::testing::Test::RecordProperty("TEST_ID", "b37c2f64-6ba6-42c8-9b6d-73bb344b8c8e"); + std::string string{"Foo"}; + float floatNum{42.42F}; + iox::cxx::variant sut1{string}; + iox::cxx::variant sut2{floatNum}; + EXPECT_FALSE(sut1 == sut2); +} + } // namespace