From bf6a8d91f6d6e718c5169b868445ad7a060601ed Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Mon, 3 Jun 2024 11:21:11 +0300 Subject: [PATCH 01/12] get rid of inclusion of pf17 inside of `unbounded_variant` #verification #docs #sonar --- .../unittest/test_unbounded_variant.cpp | 2 +- include/cetl/unbounded_variant.hpp | 75 ++++++++++++++++--- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/cetlvast/suites/unittest/test_unbounded_variant.cpp b/cetlvast/suites/unittest/test_unbounded_variant.cpp index f2e4665..6f58e10 100644 --- a/cetlvast/suites/unittest/test_unbounded_variant.cpp +++ b/cetlvast/suites/unittest/test_unbounded_variant.cpp @@ -26,7 +26,7 @@ using cetl::unbounded_variant; using cetl::get; using cetl::get_if; using cetl::make_unbounded_variant; -using cetl::in_place_type_t; +using cetl::ub_var::in_place_type_t; using cetl::type_id; using cetl::type_id_type; using cetl::rtti_helper; diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index 876dea4..8a1362c 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -10,10 +10,10 @@ #define CETL_UNBOUNDED_VARIANT_HPP_INCLUDED #include "rtti.hpp" -#include "pf17/cetlpf.hpp" -#include "pf17/utility.hpp" #include +#include +#include #include namespace cetl @@ -961,6 +961,57 @@ struct base_move } // namespace detail +// MARK: - "In Place" nested types. + +/// Namespace to nest `unbounded_variant` specific declarations. +/// +namespace ub_var +{ + +/// Implementation similar to \ref std::in_place_type_t or \ref cetl::pf17::in_place_type_t. +/// +/// In use by several \ref cetl::unbounded_variant constructors. +/// +/// Can't use directly either of already existing `std::in_place_type_t` or `cetl::pf17::in_place_type_t` types due to +/// C++14 limitation and polyfill optionality (by design in CETL, according to Scott), so a bit of code duplication. +/// +template +struct in_place_type_t +{ + explicit in_place_type_t() = default; +}; + +/// Implementation similar to \ref std::in_place_type or \ref cetl::pf17::in_place_type. +/// +/// In use by several \ref cetl::unbounded_variant constructors. +/// +/// Can't use directly either of already existing `std::in_place_type` or `cetl::pf17::in_place_type` types due to +/// C++14 limitation and polyfill optionality (by design in CETL, according to Scott), so a bit of code duplication. +/// +template +constexpr in_place_type_t in_place_type{}; + +/// Private implementation details of the in_place_type stuff. +namespace detail +{ +template +struct is_in_place_type_impl : std::false_type +{}; +template +struct is_in_place_type_impl> : std::true_type +{}; +template +struct is_in_place_type : is_in_place_type_impl> +{}; +static_assert(is_in_place_type)>::value, "self-test failure"); +static_assert(!is_in_place_type::value, "self-test failure"); + +} // namespace detail + +} // namespace ub_var + +// MARK: - unbounded_variant + template std::add_pointer_t get_if(UnboundedVariant* operand) noexcept; @@ -1044,7 +1095,7 @@ class unbounded_variant : detail::base_move, typename = std::enable_if_t::value && - !pf17::detail::is_in_place_type::value>> + !ub_var::detail::is_in_place_type::value>> unbounded_variant(ValueType&& value) // NOLINT(*-explicit-constructor) { create(std::forward(value)); @@ -1065,7 +1116,7 @@ class unbounded_variant : detail::base_move, typename = std::enable_if_t::value && - !pf17::detail::is_in_place_type::value>> + !ub_var::detail::is_in_place_type::value>> unbounded_variant(Pmr* const mem_res, ValueType&& value) : base{mem_res} { @@ -1089,7 +1140,7 @@ class unbounded_variant : detail::base_move, typename PmrAlias = Pmr, typename = detail::EnableIfNotPmrT> - explicit unbounded_variant(in_place_type_t, Args&&... args) + explicit unbounded_variant(ub_var::in_place_type_t, Args&&... args) { create(std::forward(args)...); } @@ -1110,7 +1161,7 @@ class unbounded_variant : detail::base_move, typename PmrAlias = Pmr, typename = detail::EnableIfPmrT> - explicit unbounded_variant(Pmr* const mem_res, in_place_type_t, Args&&... args) + explicit unbounded_variant(Pmr* const mem_res, ub_var::in_place_type_t, Args&&... args) : base{mem_res} { create(std::forward(args)...); @@ -1136,7 +1187,7 @@ class unbounded_variant : detail::base_move, typename PmrAlias = Pmr, typename = detail::EnableIfNotPmrT> - explicit unbounded_variant(in_place_type_t, std::initializer_list list, Args&&... args) + explicit unbounded_variant(ub_var::in_place_type_t, std::initializer_list list, Args&&... args) { create(list, std::forward(args)...); } @@ -1161,7 +1212,7 @@ class unbounded_variant : detail::base_move> explicit unbounded_variant(Pmr* const mem_res, - in_place_type_t, + ub_var::in_place_type_t, std::initializer_list list, Args&&... args) : base{mem_res} @@ -1445,18 +1496,18 @@ using unbounded_variant_like = unbounded_variant, std::forward(args)...)`. +/// Equivalent to `cetl::unbounded_variant(cetl::ub_var::in_place_type, std::forward(args)...)`. /// template , typename... Args> CETL_NODISCARD UnboundedVariant make_unbounded_variant(Args&&... args) { - return UnboundedVariant(in_place_type, std::forward(args)...); + return UnboundedVariant(ub_var::in_place_type, std::forward(args)...); } /// \brief Constructs an unbounded_variant object containing an object of type T, /// passing the provided arguments to T's constructor. /// -/// Equivalent to `cetl::unbounded_variant(cetl::in_place_type, list, std::forward(args)...)`. +/// Equivalent to `cetl::unbounded_variant(cetl::ub_var::in_place_type, list, std::forward(args)...)`. /// template , @@ -1464,7 +1515,7 @@ template CETL_NODISCARD UnboundedVariant make_unbounded_variant(std::initializer_list list, Args&&... args) { - return UnboundedVariant(in_place_type, list, std::forward(args)...); + return UnboundedVariant(ub_var::in_place_type, list, std::forward(args)...); } /// \brief Performs type-safe access to the contained object. From faa117baf636052b5c399682cce2d7755843b3bb Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Tue, 4 Jun 2024 12:14:32 +0300 Subject: [PATCH 02/12] Nesting `in_place_type_t` inside of `unbounded_variant`. --- .../unittest/test_unbounded_variant.cpp | 25 +++-- include/cetl/unbounded_variant.hpp | 96 +++++++------------ 2 files changed, 49 insertions(+), 72 deletions(-) diff --git a/cetlvast/suites/unittest/test_unbounded_variant.cpp b/cetlvast/suites/unittest/test_unbounded_variant.cpp index 6f58e10..640cfa9 100644 --- a/cetlvast/suites/unittest/test_unbounded_variant.cpp +++ b/cetlvast/suites/unittest/test_unbounded_variant.cpp @@ -26,7 +26,6 @@ using cetl::unbounded_variant; using cetl::get; using cetl::get_if; using cetl::make_unbounded_variant; -using cetl::ub_var::in_place_type_t; using cetl::type_id; using cetl::type_id_type; using cetl::rtti_helper; @@ -591,7 +590,7 @@ TEST_F(TestPmrUnboundedVariant, ctor_5_in_place) }; using ub_var = unbounded_variant; - const ub_var src{in_place_type_t{}, 'Y', 42}; + const ub_var src{ub_var::in_place_type_t{}, 'Y', 42}; const auto test = get(src); EXPECT_THAT(test.ch_, 'Y'); @@ -613,7 +612,7 @@ TEST_F(TestPmrUnboundedVariant, ctor_6_in_place_initializer_list) }; using ub_var = unbounded_variant; - const ub_var src{in_place_type_t{}, {'A', 'B', 'C'}, 42}; + const ub_var src{ub_var::in_place_type_t{}, {'A', 'B', 'C'}, 42}; auto& test = get(src); EXPECT_THAT(test.size_, 3); @@ -1038,8 +1037,8 @@ TEST_F(TestPmrUnboundedVariant, swap_copyable) using ub_var = unbounded_variant; ub_var empty{}; - ub_var a{in_place_type_t{}, 'A'}; - ub_var b{in_place_type_t{}, 'B'}; + ub_var a{ub_var::in_place_type_t{}, 'A'}; + ub_var b{ub_var::in_place_type_t{}, 'B'}; // Self swap a.swap(a); @@ -1070,8 +1069,8 @@ TEST_F(TestPmrUnboundedVariant, swap_movable) using ub_var = unbounded_variant; ub_var empty{}; - ub_var a{in_place_type_t{}, 'A'}; - ub_var b{in_place_type_t{}, 'B'}; + ub_var a{ub_var::in_place_type_t{}, 'A'}; + ub_var b{ub_var::in_place_type_t{}, 'B'}; // Self swap a.swap(a); @@ -1551,8 +1550,8 @@ TEST_F(TestPmrUnboundedVariant, pmr_swap_copyable) using ub_var = unbounded_variant<0, true, false, alignof(std::max_align_t), pmr>; ub_var empty{get_default_mr()}; - ub_var a{get_default_mr(), in_place_type_t{}, 'A'}; - ub_var b{get_default_mr(), in_place_type_t{}, 'B'}; + ub_var a{get_default_mr(), ub_var::in_place_type_t{}, 'A'}; + ub_var b{get_default_mr(), ub_var::in_place_type_t{}, 'B'}; // Self swap a.swap(a); @@ -1583,9 +1582,9 @@ TEST_F(TestPmrUnboundedVariant, pmr_swap_movable) using ub_var = unbounded_variant; ub_var empty{get_mr()}; - ub_var a{get_mr(), in_place_type_t{}, 'A'}; + ub_var a{get_mr(), ub_var::in_place_type_t{}, 'A'}; EXPECT_THAT(a.get_memory_resource(), get_mr()); - ub_var b{get_default_mr(), in_place_type_t{}, 'B'}; + ub_var b{get_default_mr(), ub_var::in_place_type_t{}, 'B'}; EXPECT_THAT(b.get_memory_resource(), get_default_mr()); // Self swap @@ -1628,7 +1627,7 @@ TEST_F(TestPmrUnboundedVariant, pmr_swap_movable) EXPECT_THAT(another_empty.get_memory_resource(), get_mr()); EXPECT_THAT(empty.get_memory_resource(), get_default_mr()); - const ub_var ub_vec{get_mr(), in_place_type_t>{}, {'A', 'B', 'C'}}; + const ub_var ub_vec{get_mr(), ub_var::in_place_type_t>{}, {'A', 'B', 'C'}}; EXPECT_THAT(ub_vec.get_memory_resource(), get_mr()); EXPECT_THAT(get&>(ub_vec), testing::ElementsAre('A', 'B', 'C')); } @@ -1638,7 +1637,7 @@ TEST_F(TestPmrUnboundedVariant, pmr_reset_memory_resource) using test = MyMovableOnly; using ub_var = unbounded_variant; - ub_var a{get_mr(), in_place_type_t{}, 'A'}; + ub_var a{get_mr(), ub_var::in_place_type_t{}, 'A'}; EXPECT_TRUE(a.has_value()); EXPECT_THAT(a.get_memory_resource(), get_mr()); diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index 8a1362c..b150481 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -961,57 +961,6 @@ struct base_move } // namespace detail -// MARK: - "In Place" nested types. - -/// Namespace to nest `unbounded_variant` specific declarations. -/// -namespace ub_var -{ - -/// Implementation similar to \ref std::in_place_type_t or \ref cetl::pf17::in_place_type_t. -/// -/// In use by several \ref cetl::unbounded_variant constructors. -/// -/// Can't use directly either of already existing `std::in_place_type_t` or `cetl::pf17::in_place_type_t` types due to -/// C++14 limitation and polyfill optionality (by design in CETL, according to Scott), so a bit of code duplication. -/// -template -struct in_place_type_t -{ - explicit in_place_type_t() = default; -}; - -/// Implementation similar to \ref std::in_place_type or \ref cetl::pf17::in_place_type. -/// -/// In use by several \ref cetl::unbounded_variant constructors. -/// -/// Can't use directly either of already existing `std::in_place_type` or `cetl::pf17::in_place_type` types due to -/// C++14 limitation and polyfill optionality (by design in CETL, according to Scott), so a bit of code duplication. -/// -template -constexpr in_place_type_t in_place_type{}; - -/// Private implementation details of the in_place_type stuff. -namespace detail -{ -template -struct is_in_place_type_impl : std::false_type -{}; -template -struct is_in_place_type_impl> : std::true_type -{}; -template -struct is_in_place_type : is_in_place_type_impl> -{}; -static_assert(is_in_place_type)>::value, "self-test failure"); -static_assert(!is_in_place_type::value, "self-test failure"); - -} // namespace detail - -} // namespace ub_var - -// MARK: - unbounded_variant - template std::add_pointer_t get_if(UnboundedVariant* operand) noexcept; @@ -1040,11 +989,28 @@ class unbounded_variant : detail::base_move; using base = detail::base_move; + // Forward declaration. + template + struct is_in_place_type; + public: using base::reset; using base::has_value; using base::valueless_by_exception; + /// Implementation similar to \ref std::in_place_type_t or \ref cetl::pf17::in_place_type_t. + /// + /// In use by several \ref cetl::unbounded_variant constructors. + /// + /// Can't use directly either of already existing `std::in_place_type_t` or `cetl::pf17::in_place_type_t` types due to + /// C++14 limitation and polyfill optionality (by design in CETL, according to Scott), so a bit of code duplication. + /// + template + struct in_place_type_t + { + explicit in_place_type_t() = default; + }; + /// \brief Constructs an empty `unbounded_variant` object. /// /// In case of enabled PMR support, the default memory resource is used. @@ -1095,7 +1061,7 @@ class unbounded_variant : detail::base_move, typename = std::enable_if_t::value && - !ub_var::detail::is_in_place_type::value>> + !is_in_place_type::value>> unbounded_variant(ValueType&& value) // NOLINT(*-explicit-constructor) { create(std::forward(value)); @@ -1116,7 +1082,7 @@ class unbounded_variant : detail::base_move, typename = std::enable_if_t::value && - !ub_var::detail::is_in_place_type::value>> + !is_in_place_type::value>> unbounded_variant(Pmr* const mem_res, ValueType&& value) : base{mem_res} { @@ -1140,7 +1106,7 @@ class unbounded_variant : detail::base_move, typename PmrAlias = Pmr, typename = detail::EnableIfNotPmrT> - explicit unbounded_variant(ub_var::in_place_type_t, Args&&... args) + explicit unbounded_variant(in_place_type_t, Args&&... args) { create(std::forward(args)...); } @@ -1161,7 +1127,7 @@ class unbounded_variant : detail::base_move, typename PmrAlias = Pmr, typename = detail::EnableIfPmrT> - explicit unbounded_variant(Pmr* const mem_res, ub_var::in_place_type_t, Args&&... args) + explicit unbounded_variant(Pmr* const mem_res, in_place_type_t, Args&&... args) : base{mem_res} { create(std::forward(args)...); @@ -1187,7 +1153,7 @@ class unbounded_variant : detail::base_move, typename PmrAlias = Pmr, typename = detail::EnableIfNotPmrT> - explicit unbounded_variant(ub_var::in_place_type_t, std::initializer_list list, Args&&... args) + explicit unbounded_variant(in_place_type_t, std::initializer_list list, Args&&... args) { create(list, std::forward(args)...); } @@ -1212,7 +1178,7 @@ class unbounded_variant : detail::base_move> explicit unbounded_variant(Pmr* const mem_res, - ub_var::in_place_type_t, + in_place_type_t, std::initializer_list list, Args&&... args) : base{mem_res} @@ -1358,6 +1324,18 @@ class unbounded_variant : detail::base_move friend std::add_pointer_t> get_if(const UnboundedVariant* operand) noexcept; + template + struct is_in_place_type_impl : std::false_type + {}; + template + struct is_in_place_type_impl> : std::true_type + {}; + template + struct is_in_place_type : is_in_place_type_impl> + {}; + static_assert(is_in_place_type>::value, "self-test failure"); + static_assert(!is_in_place_type::value, "self-test failure"); + template Tp* create(Args&&... args) { @@ -1501,7 +1479,7 @@ using unbounded_variant_like = unbounded_variant, typename... Args> CETL_NODISCARD UnboundedVariant make_unbounded_variant(Args&&... args) { - return UnboundedVariant(ub_var::in_place_type, std::forward(args)...); + return UnboundedVariant(UnboundedVariant::template in_place_type, std::forward(args)...); } /// \brief Constructs an unbounded_variant object containing an object of type T, @@ -1515,7 +1493,7 @@ template CETL_NODISCARD UnboundedVariant make_unbounded_variant(std::initializer_list list, Args&&... args) { - return UnboundedVariant(ub_var::in_place_type, list, std::forward(args)...); + return UnboundedVariant(UnboundedVariant::template in_place_type, list, std::forward(args)...); } /// \brief Performs type-safe access to the contained object. From 4c754a5e58dcb88e06aef89ddb08c01876e07a18 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Tue, 4 Jun 2024 12:17:13 +0300 Subject: [PATCH 03/12] remove useless doc lines --- include/cetl/unbounded_variant.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index b150481..b0d8ddf 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -1474,8 +1474,6 @@ using unbounded_variant_like = unbounded_variant, std::forward(args)...)`. -/// template , typename... Args> CETL_NODISCARD UnboundedVariant make_unbounded_variant(Args&&... args) { @@ -1485,8 +1483,6 @@ CETL_NODISCARD UnboundedVariant make_unbounded_variant(Args&&... args) /// \brief Constructs an unbounded_variant object containing an object of type T, /// passing the provided arguments to T's constructor. /// -/// Equivalent to `cetl::unbounded_variant(cetl::ub_var::in_place_type, list, std::forward(args)...)`. -/// template , typename Up, From 9ea0198336b7c39141209c776f464dee9f725e9e Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Tue, 4 Jun 2024 12:34:14 +0300 Subject: [PATCH 04/12] fix build --- include/cetl/unbounded_variant.hpp | 35 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index b0d8ddf..4c3e557 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -1002,8 +1002,9 @@ class unbounded_variant : detail::base_move struct in_place_type_t @@ -1056,12 +1057,12 @@ class unbounded_variant : detail::base_move, - typename PmrAlias = Pmr, - typename = detail::EnableIfNotPmrT, - typename = std::enable_if_t::value && - !is_in_place_type::value>> + template < + typename ValueType, + typename Tp = std::decay_t, + typename PmrAlias = Pmr, + typename = detail::EnableIfNotPmrT, + typename = std::enable_if_t::value && !is_in_place_type::value>> unbounded_variant(ValueType&& value) // NOLINT(*-explicit-constructor) { create(std::forward(value)); @@ -1077,12 +1078,12 @@ class unbounded_variant : detail::base_move, - typename PmrAlias = Pmr, - typename = detail::EnableIfPmrT, - typename = std::enable_if_t::value && - !is_in_place_type::value>> + template < + typename ValueType, + typename Tp = std::decay_t, + typename PmrAlias = Pmr, + typename = detail::EnableIfPmrT, + typename = std::enable_if_t::value && !is_in_place_type::value>> unbounded_variant(Pmr* const mem_res, ValueType&& value) : base{mem_res} { @@ -1477,7 +1478,8 @@ using unbounded_variant_like = unbounded_variant, typename... Args> CETL_NODISCARD UnboundedVariant make_unbounded_variant(Args&&... args) { - return UnboundedVariant(UnboundedVariant::template in_place_type, std::forward(args)...); + using in_place_type_t = UnboundedVariant::template in_place_type_t; + return UnboundedVariant(in_place_type_t{}, std::forward(args)...); } /// \brief Constructs an unbounded_variant object containing an object of type T, @@ -1489,7 +1491,8 @@ template CETL_NODISCARD UnboundedVariant make_unbounded_variant(std::initializer_list list, Args&&... args) { - return UnboundedVariant(UnboundedVariant::template in_place_type, list, std::forward(args)...); + using in_place_type_t = UnboundedVariant::template in_place_type_t; + return UnboundedVariant(in_place_type_t{}, list, std::forward(args)...); } /// \brief Performs type-safe access to the contained object. From 6192ecb20e3923c9291356667f3b56c8f58ecc41 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Tue, 4 Jun 2024 12:41:13 +0300 Subject: [PATCH 05/12] build fix --- include/cetl/unbounded_variant.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index 4c3e557..af94954 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -1478,7 +1478,7 @@ using unbounded_variant_like = unbounded_variant, typename... Args> CETL_NODISCARD UnboundedVariant make_unbounded_variant(Args&&... args) { - using in_place_type_t = UnboundedVariant::template in_place_type_t; + using in_place_type_t = typename UnboundedVariant::template in_place_type_t; return UnboundedVariant(in_place_type_t{}, std::forward(args)...); } @@ -1491,7 +1491,7 @@ template CETL_NODISCARD UnboundedVariant make_unbounded_variant(std::initializer_list list, Args&&... args) { - using in_place_type_t = UnboundedVariant::template in_place_type_t; + using in_place_type_t = typename UnboundedVariant::template in_place_type_t; return UnboundedVariant(in_place_type_t{}, list, std::forward(args)...); } From 634fc25d26e44a32048a3bba98306be7d77b1d5b Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Wed, 5 Jun 2024 10:18:37 +0300 Subject: [PATCH 06/12] More `make_unbounded_variant` --- .../unittest/test_unbounded_variant.cpp | 48 +++++++ include/cetl/unbounded_variant.hpp | 122 ++++++++++++++---- 2 files changed, 147 insertions(+), 23 deletions(-) diff --git a/cetlvast/suites/unittest/test_unbounded_variant.cpp b/cetlvast/suites/unittest/test_unbounded_variant.cpp index 640cfa9..608571d 100644 --- a/cetlvast/suites/unittest/test_unbounded_variant.cpp +++ b/cetlvast/suites/unittest/test_unbounded_variant.cpp @@ -1646,6 +1646,54 @@ TEST_F(TestPmrUnboundedVariant, pmr_reset_memory_resource) EXPECT_THAT(a.get_memory_resource(), get_default_mr()); } +TEST_F(TestPmrUnboundedVariant, pmr_make_unbounded_variant_cppref_example) +{ + using ub_var = unbounded_variant<0, true, true, alignof(std::max_align_t), pmr>; + + auto a0 = make_unbounded_variant(get_mr(), "Hello, cetl::unbounded_variant!\n"); + auto a1 = make_unbounded_variant, ub_var>(get_mr(), 0.1, 2.3); + + EXPECT_THAT(get(a0), "Hello, cetl::unbounded_variant!\n"); + EXPECT_THAT(get>(a1), std::complex(0.1, 2.3)); + + using lambda = std::function; + using ub_var_lambda = cetl::unbounded_variant<0, true, true, alignof(std::max_align_t), pmr>; + + auto a3 = make_unbounded_variant(get_mr(), [] { return "Lambda #3.\n"; }); + EXPECT_TRUE(a3.has_value()); + EXPECT_THAT(get(a3)(), "Lambda #3.\n"); +} + +TEST_F(TestPmrUnboundedVariant, pmr_make_unbounded_variant_1) +{ + using ub_var = unbounded_variant; + + auto src = make_unbounded_variant(get_mr(), 42); + EXPECT_THAT(get(src), 42); + static_assert(std::is_same::value, ""); +} + +TEST_F(TestPmrUnboundedVariant, pmr_make_unbounded_variant_2_list) +{ + struct MyType : rtti_helper> + { + std::size_t size_; + int number_; + + MyType(const std::initializer_list chars, const int number) + { + size_ = chars.size(); + number_ = number; + } + }; + using ub_var = unbounded_variant; + + const auto src = make_unbounded_variant(get_mr(), {'A', 'C'}, 42); + const auto& test = get(src); + EXPECT_THAT(test.size_, 2); + EXPECT_THAT(test.number_, 42); +} + } // namespace namespace cetl diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index af94954..5e66dde 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -994,13 +994,21 @@ class unbounded_variant : detail::base_move; + using base::reset; using base::has_value; using base::valueless_by_exception; /// Implementation similar to \ref std::in_place_type_t or \ref cetl::pf17::in_place_type_t. /// - /// In use by several \ref cetl::unbounded_variant constructors. + /// In use by several \ref cetl::unbounded_variant constructors, + /// but please consider using \ref cetl::make_unbounded_variant instead. /// /// Can't use directly either of already existing `std::in_place_type_t` or `cetl::pf17::in_place_type_t` types due /// to C++14 limitation and polyfill optionality (by design in CETL, according to Scott), so a bit of code @@ -1014,8 +1022,6 @@ class unbounded_variant : detail::base_move> unbounded_variant() { @@ -1047,12 +1053,9 @@ class unbounded_variant : detail::base_move(std::forward(args)...); } - /// \brief Constructs an `unbounded_variant` object with in place constructed value. + /// \brief Constructs a PMR-enabled `unbounded_variant` object with in place constructed value. + /// + /// Please consider using \ref cetl::make_unbounded_variant helper instead. /// /// Size of the value must be less than or equal to `Footprint` to benefit small object optimization, /// otherwise the value will be stored into PMR allocated storage. @@ -1136,11 +1140,10 @@ class unbounded_variant : detail::base_move(list, std::forward(args)...); } - /// \brief Constructs an `unbounded_variant` object with in place constructed value and initializer list. + /// \brief Constructs a PMR-enabled `unbounded_variant` object with in place constructed value and initializer list. /// /// Size of the value must be less than or equal to `Footprint` to benefit small object optimization, /// otherwise the value will be stored into PMR allocated storage. @@ -1472,29 +1475,102 @@ using unbounded_variant_like = unbounded_variant::value, alignof(ValueType)>; -/// \brief Constructs an unbounded_variant object containing an object of type T, -/// passing the provided arguments to T's constructor. +/// \brief Makes an `unbounded_variant` object with in place constructed value. +/// +/// Size of the value must be less than or equal to `Footprint`. +/// Any failure during the construction will result in the "valueless by exception" state. +/// +/// \tparam ValueType Type of the value to be stored. Its size must be less than or equal to `Footprint`. +/// \tparam UnboundedVariant Template type of the result unbounded variant. +/// \tparam Args Types of arguments to be passed to the constructor of `ValueType`. +/// \param args Arguments to be forwarded to the constructor of `ValueType`. /// -template , typename... Args> +template , + typename... Args, + typename = std::enable_if_t> CETL_NODISCARD UnboundedVariant make_unbounded_variant(Args&&... args) { using in_place_type_t = typename UnboundedVariant::template in_place_type_t; return UnboundedVariant(in_place_type_t{}, std::forward(args)...); } -/// \brief Constructs an unbounded_variant object containing an object of type T, -/// passing the provided arguments to T's constructor. +/// \brief Makes an `unbounded_variant` object with in place constructed value and initializer list. +/// +/// Size of the value must be less than or equal to `Footprint`. +/// Any failure during the construction will result in the "valueless by exception" state. +/// +/// \tparam ValueType Type of the value to be stored. Its size must be less than or equal to `Footprint`. +/// \tparam UnboundedVariant Template type of the result unbounded variant. +/// \tparam Up Type of the elements of the initializer list. +/// \tparam Args Types of arguments to be passed to the constructor of `ValueType`. +/// \param mem_res Pointer to a memory resource to be used by the variant. +/// \param list Initializer list to be forwarded to the constructor of `ValueType`. +/// \param args Arguments to be forwarded to the constructor of `ValueType`. /// template , typename Up, - typename... Args> + typename... Args, + typename = std::enable_if_t> CETL_NODISCARD UnboundedVariant make_unbounded_variant(std::initializer_list list, Args&&... args) { using in_place_type_t = typename UnboundedVariant::template in_place_type_t; return UnboundedVariant(in_place_type_t{}, list, std::forward(args)...); } +/// \brief Makes a PMR-enabled `unbounded_variant` object with in place constructed value. +/// +/// Size of the value must be less than or equal to `Footprint` to benefit small object optimization, +/// otherwise the value will be stored into PMR allocated storage. +/// Any failure during the construction will result in the "valueless by exception" state. +/// +/// \tparam ValueType Type of the value to be stored. Its size must be less than or equal to `Footprint`. +/// \tparam UnboundedVariant Template type of the result unbounded variant. +/// \tparam Args Types of arguments to be passed to the constructor of `ValueType`. +/// \param mem_res Pointer to a memory resource to be used by the variant. +/// \param list Initializer list to be forwarded to the constructor of `ValueType`. +/// \param args Arguments to be forwarded to the constructor of `ValueType`. +/// +template , + typename... Args, + typename Pmr = typename UnboundedVariant::PmrType, + typename = std::enable_if_t> +CETL_NODISCARD UnboundedVariant make_unbounded_variant(Pmr* const mem_res, Args&&... args) +{ + using in_place_type_t = typename UnboundedVariant::template in_place_type_t; + return UnboundedVariant(mem_res, in_place_type_t{}, std::forward(args)...); +} + +/// \brief Makes a PMR-enabled `unbounded_variant` object with in place constructed value and initializer list. +/// +/// Size of the value must be less than or equal to `Footprint` to benefit small object optimization, +/// otherwise the value will be stored into PMR allocated storage. +/// Any failure during the construction will result in the "valueless by exception" state. +/// +/// \tparam ValueType Type of the value to be stored. Its size must be less than or equal to `Footprint`. +/// \tparam UnboundedVariant Template type of the result unbounded variant. +/// \tparam Up Type of the elements of the initializer list. +/// \tparam Args Types of arguments to be passed to the constructor of `ValueType`. +/// \param mem_res Pointer to a memory resource to be used by the variant. +/// \param list Initializer list to be forwarded to the constructor of `ValueType`. +/// \param args Arguments to be forwarded to the constructor of `ValueType`. +/// +template , + typename Up, + typename... Args, + typename Pmr = typename UnboundedVariant::PmrType, + typename = std::enable_if_t> +CETL_NODISCARD UnboundedVariant make_unbounded_variant(Pmr* const mem_res, + std::initializer_list list, + Args&&... args) +{ + using in_place_type_t = typename UnboundedVariant::template in_place_type_t; + return UnboundedVariant(mem_res, in_place_type_t{}, list, std::forward(args)...); +} + /// \brief Performs type-safe access to the contained object. /// /// \param operand Target unbounded_variant object. From 8066f01d8234fd41e55b7b6c36a714cc396ca90f Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Wed, 5 Jun 2024 10:54:54 +0300 Subject: [PATCH 07/12] =?UTF-8?q?Public=20`PmrType`=20=E2=86=92=20`pmr=5Ft?= =?UTF-8?q?ype`;=20more=20unit=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unittest/test_unbounded_variant.cpp | 26 +++++++++++++++++++ include/cetl/unbounded_variant.hpp | 20 +++++++------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/cetlvast/suites/unittest/test_unbounded_variant.cpp b/cetlvast/suites/unittest/test_unbounded_variant.cpp index 608571d..0f672b1 100644 --- a/cetlvast/suites/unittest/test_unbounded_variant.cpp +++ b/cetlvast/suites/unittest/test_unbounded_variant.cpp @@ -349,6 +349,7 @@ TEST_F(TestPmrUnboundedVariant, bad_unbounded_variant_access_assignment) TEST_F(TestPmrUnboundedVariant, cppref_example) { using ub_var = unbounded_variant; + static_assert(std::is_void::value, "pmr_type should be `void`"); ub_var a = 1; EXPECT_THAT(get(a), 1); @@ -1208,6 +1209,7 @@ TEST_F(TestPmrUnboundedVariant, emplace_2_initializer_list) TEST_F(TestPmrUnboundedVariant, pmr_only_ctor) { using ub_var = unbounded_variant<0 /*Footprint*/, true /*Copyable*/, true /*Movable*/, 1 /*Alignment*/, pmr>; + static_assert(std::is_same::value, "pmr_type should be `pmr`"); ub_var dst{get_default_mr()}; EXPECT_THAT(dst.has_value(), false); @@ -1694,6 +1696,30 @@ TEST_F(TestPmrUnboundedVariant, pmr_make_unbounded_variant_2_list) EXPECT_THAT(test.number_, 42); } +TEST_F(TestPmrUnboundedVariant, pmr_use_mock_as_custom_mr_type) +{ + using custom_mr_mock = StrictMock; + custom_mr_mock mr_mock{}; + + const auto Alignment = alignof(std::max_align_t); + using ub_var = unbounded_variant; + static_assert(std::is_same::value, "should be custom memory resource mock"); + + auto src = make_unbounded_variant(&mr_mock, 42); + EXPECT_THAT(get(src), 42); + + EXPECT_CALL(mr_mock, do_allocate(sizeof(double), Alignment)) + .WillOnce( + [this](std::size_t size_bytes, std::size_t alignment) { return mr_.allocate(size_bytes, alignment); }); + EXPECT_CALL(mr_mock, do_deallocate(_, sizeof(double), Alignment)) + .WillOnce([this](void* p, std::size_t size_bytes, std::size_t alignment) { + mr_.deallocate(p, size_bytes, alignment); + }); + + src = 3.1415926; + EXPECT_THAT(get(src), 3.1415926); +} + } // namespace namespace cetl diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index 5e66dde..cdc47f7 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -998,8 +998,7 @@ class unbounded_variant : detail::base_move; + using pmr_type = Pmr; using base::reset; using base::has_value; @@ -1488,7 +1487,7 @@ using unbounded_variant_like = unbounded_variant, typename... Args, - typename = std::enable_if_t> + typename = detail::EnableIfNotPmrT> CETL_NODISCARD UnboundedVariant make_unbounded_variant(Args&&... args) { using in_place_type_t = typename UnboundedVariant::template in_place_type_t; @@ -1512,7 +1511,7 @@ template , typename Up, typename... Args, - typename = std::enable_if_t> + typename = detail::EnableIfNotPmrT> CETL_NODISCARD UnboundedVariant make_unbounded_variant(std::initializer_list list, Args&&... args) { using in_place_type_t = typename UnboundedVariant::template in_place_type_t; @@ -1535,9 +1534,9 @@ CETL_NODISCARD UnboundedVariant make_unbounded_variant(std::initializer_list template , typename... Args, - typename Pmr = typename UnboundedVariant::PmrType, - typename = std::enable_if_t> -CETL_NODISCARD UnboundedVariant make_unbounded_variant(Pmr* const mem_res, Args&&... args) + typename = detail::EnableIfPmrT> +CETL_NODISCARD UnboundedVariant make_unbounded_variant(typename UnboundedVariant::pmr_type* const mem_res, + Args&&... args) { using in_place_type_t = typename UnboundedVariant::template in_place_type_t; return UnboundedVariant(mem_res, in_place_type_t{}, std::forward(args)...); @@ -1561,10 +1560,9 @@ template , typename Up, typename... Args, - typename Pmr = typename UnboundedVariant::PmrType, - typename = std::enable_if_t> -CETL_NODISCARD UnboundedVariant make_unbounded_variant(Pmr* const mem_res, - std::initializer_list list, + typename = detail::EnableIfPmrT> +CETL_NODISCARD UnboundedVariant make_unbounded_variant(typename UnboundedVariant::pmr_type* const mem_res, + std::initializer_list list, Args&&... args) { using in_place_type_t = typename UnboundedVariant::template in_place_type_t; From a71a5da0651d856113bd884e25a9d4dea2d319d1 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Wed, 5 Jun 2024 16:35:29 +0300 Subject: [PATCH 08/12] Added `type_id()` & `type_size()` methods to the `cetl::unbounded_variant`. #verification #docs #sonar --- .../unittest/test_unbounded_variant.cpp | 109 +++++++++++------- include/cetl/rtti.hpp | 10 ++ include/cetl/unbounded_variant.hpp | 80 ++++++++++--- 3 files changed, 141 insertions(+), 58 deletions(-) diff --git a/cetlvast/suites/unittest/test_unbounded_variant.cpp b/cetlvast/suites/unittest/test_unbounded_variant.cpp index 0f672b1..36843e3 100644 --- a/cetlvast/suites/unittest/test_unbounded_variant.cpp +++ b/cetlvast/suites/unittest/test_unbounded_variant.cpp @@ -19,6 +19,44 @@ // NOLINTBEGIN(*-use-after-move) +namespace cetl +{ + +template <> +constexpr type_id type_id_value = {1}; + +template <> +constexpr type_id type_id_value = {2}; + +template <> +constexpr type_id type_id_value = {3}; + +template <> +constexpr type_id type_id_value = {4}; + +template <> +constexpr type_id type_id_value = {5}; + +template <> +constexpr type_id type_id_value = {6}; + +template <> +constexpr type_id type_id_value = {7}; + +template <> +constexpr type_id type_id_value> = {8}; + +template <> +constexpr type_id type_id_value> = {9}; + +template <> +constexpr type_id type_id_value = {10}; + +template <> +constexpr type_id type_id_value> = {11}; + +} // namespace cetl + namespace { @@ -28,6 +66,7 @@ using cetl::get_if; using cetl::make_unbounded_variant; using cetl::type_id; using cetl::type_id_type; +using cetl::type_id_invalid; using cetl::rtti_helper; using testing::_; @@ -94,7 +133,7 @@ struct side_effect_stats } }; -struct MyBase : rtti_helper> +struct MyBase : rtti_helper> { char payload_; int value_ = 0; @@ -186,7 +225,7 @@ struct MyCopyableOnly final : MyBase static constexpr type_id _get_type_id_() noexcept { - return {0x0, 0b01}; + return {0x1, 0b01}; } CETL_NODISCARD void* _cast_(const type_id& id) & noexcept override @@ -225,7 +264,7 @@ struct MyMovableOnly final : MyBase static constexpr type_id _get_type_id_() noexcept { - return {0x0, 0b10}; + return {0x1, 0b10}; } CETL_NODISCARD void* _cast_(const type_id& id) & noexcept override @@ -263,7 +302,7 @@ struct MyCopyableAndMovable final : MyBase static constexpr type_id _get_type_id_() noexcept { - return {0x0, 0b11}; + return {0x1, 0b11}; } CETL_NODISCARD void* _cast_(const type_id& id) & noexcept override @@ -382,6 +421,9 @@ TEST_F(TestPmrUnboundedVariant, cppref_example) TEST_F(TestPmrUnboundedVariant, ctor_1_default) { + EXPECT_THAT(unbounded_variant<1>{}.type_size(), 0UL); + EXPECT_THAT(unbounded_variant<1>{}.type_id(), type_id_invalid); + EXPECT_FALSE((unbounded_variant<1>{}.has_value())); EXPECT_FALSE((unbounded_variant<1, false>{}.has_value())); EXPECT_FALSE((unbounded_variant<1, false, true>{}.has_value())); @@ -397,6 +439,9 @@ TEST_F(TestPmrUnboundedVariant, ctor_1_default) TEST_F(TestPmrUnboundedVariant, ctor_1_default_pmr) { + EXPECT_THAT((unbounded_variant<0, true, true, 8, pmr>{get_mr()}.type_size()), 0UL); + EXPECT_THAT((unbounded_variant<0, true, true, 8, pmr>{get_mr()}.type_id()), type_id_invalid); + EXPECT_FALSE((unbounded_variant<0, false, false, 8, pmr>{get_mr()}.has_value())); EXPECT_FALSE((unbounded_variant<0, false, true, 8, pmr>{get_mr()}.has_value())); EXPECT_FALSE((unbounded_variant<0, true, false, 8, pmr>{get_mr()}.has_value())); @@ -420,12 +465,22 @@ TEST_F(TestPmrUnboundedVariant, ctor_2_copy) using ub_var = unbounded_variant; const ub_var src{42}; - ub_var dst{src}; + EXPECT_THAT(src.type_size(), sizeof(int)); + EXPECT_THAT(src.type_id(), cetl::type_id_value); + + ub_var dst{src}; + EXPECT_THAT(src.type_size(), sizeof(int)); + EXPECT_THAT(src.type_id(), cetl::type_id_value); + EXPECT_THAT(dst.type_size(), sizeof(int)); + EXPECT_THAT(dst.type_id(), cetl::type_id_value); EXPECT_THAT(get(src), 42); EXPECT_THAT(get(dst), 42); const ub_var empty{}; + EXPECT_THAT(empty.type_size(), 0); + EXPECT_THAT(empty.type_id(), type_id_invalid); + ub_var dst2{empty}; EXPECT_THAT(dst2.has_value(), false); dst2 = {}; @@ -1011,6 +1066,8 @@ TEST_F(TestPmrUnboundedVariant, get_if_polymorphic) auto side_effects = stats.make_side_effect_fn(); ub_var test_ubv = MyCopyableAndMovable{'Y', side_effects}; + EXPECT_THAT(test_ubv.type_size(), sizeof(MyCopyableAndMovable)); + EXPECT_THAT(test_ubv.type_id(), cetl::type_id_value); auto& test_base1 = get(test_ubv); EXPECT_THAT(test_base1.payload_, 'Y'); @@ -1020,6 +1077,8 @@ TEST_F(TestPmrUnboundedVariant, get_if_polymorphic) EXPECT_THAT(get_if(&test_ubv), IsNull()); test_ubv = MyBase{'X', side_effects}; + EXPECT_THAT(test_ubv.type_size(), sizeof(MyBase)); + EXPECT_THAT(test_ubv.type_id(), cetl::type_id_value); auto& test_base2 = get(test_ubv); EXPECT_THAT(test_base2.payload_, 'X'); @@ -1166,6 +1225,8 @@ TEST_F(TestPmrUnboundedVariant, emplace_1_ctor_exception) EXPECT_THAT(t.has_value(), false); EXPECT_THAT(t.valueless_by_exception(), true); + EXPECT_THAT(t.type_size(), 0); + EXPECT_THAT(t.type_id(), type_id_invalid); EXPECT_THAT(stats.constructs, 1); EXPECT_THAT(stats.destructs, 0); t.reset(); @@ -1402,6 +1463,8 @@ TEST_F(TestPmrUnboundedVariant, pmr_with_footprint_move_value_when_out_of_memory #endif EXPECT_THAT(dst.has_value(), false); EXPECT_THAT(dst.valueless_by_exception(), true); + EXPECT_THAT(dst.type_size(), 0); + EXPECT_THAT(dst.type_id(), type_id_invalid); EXPECT_THAT(stats.ops, "@"); } EXPECT_THAT(stats.constructs, stats.destructs); @@ -1725,45 +1788,13 @@ TEST_F(TestPmrUnboundedVariant, pmr_use_mock_as_custom_mr_type) namespace cetl { -template <> -constexpr type_id type_id_value = {1}; - -template <> -constexpr type_id type_id_value = {2}; - -template <> -constexpr type_id type_id_value = {3}; - -template <> -constexpr type_id type_id_value = {4}; - -template <> -constexpr type_id type_id_value = {5}; - -template <> -constexpr type_id type_id_value = {6}; - -template <> -constexpr type_id type_id_value = {7}; - template <> constexpr type_id type_id_value> = {0xB3, 0xB8, 0x4E, 0xC1, 0x1F, 0xE4, 0x49, 0x35, 0x9E, 0xC9, 0x1A, 0x77, 0x7B, 0x82, 0x53, 0x25}; template <> -constexpr type_id type_id_value> = {8}; - -template <> -constexpr type_id type_id_value> = {9}; - -template <> -constexpr type_id type_id_value = {10}; - -template <> -constexpr type_id type_id_value = {11}; - -template <> -constexpr type_id type_id_value> = {12}; +constexpr type_id type_id_value = + {0xD5, 0x62, 0x39, 0x66, 0x90, 0x8B, 0x4F, 0x56, 0x8F, 0x2A, 0x2F, 0x4F, 0xDF, 0x3F, 0x31, 0x5B}; } // namespace cetl diff --git a/include/cetl/rtti.hpp b/include/cetl/rtti.hpp index 9a35578..00598fa 100644 --- a/include/cetl/rtti.hpp +++ b/include/cetl/rtti.hpp @@ -35,6 +35,7 @@ using type_id = std::array; /// The bytes of the UUID are given as a list of template parameters; there shall be at most 16 of them; /// if any are missing, they are assumed to be 0. /// For conversion to \ref type_id use \ref cetl::type_id_type_value. +/// Please don't use empty or all zeros bytes, as it will be treated as the invalid type ID (see \ref type_id_invalid). template using type_id_type = std::integer_sequence; @@ -81,6 +82,15 @@ constexpr type_id type_id_type_value() noexcept template constexpr type_id type_id_value = T::_get_type_id_(); +/// The type ID value specializations for `void` type. +/// +/// In use for the `type_id_invalid` constant, which f.e. is in use at +/// `unbounded_variant::type_id()` method to indicate that the variant is valueless. +/// +template <> +constexpr type_id type_id_value{}; +constexpr type_id type_id_invalid{type_id_value}; + /// An alternative implementation of simple runtime type information (RTTI) capability designed for high-integrity /// real-time systems, where the use of the standard C++ RTTI is discouraged. /// diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index cdc47f7..66ab5ac 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace cetl { @@ -542,7 +543,7 @@ struct base_access : base_storage base::template check_footprint(); CETL_DEBUG_ASSERT(nullptr == value_destroyer_, "Expected to be empty before making handlers."); - CETL_DEBUG_ASSERT(nullptr == value_converter_, ""); + CETL_DEBUG_ASSERT(nullptr == value_mut_converter_, ""); CETL_DEBUG_ASSERT(nullptr == value_const_converter_, ""); value_destroyer_ = [](void* const storage) { @@ -556,27 +557,55 @@ struct base_access : base_storage template , int> = 0> void make_converters() noexcept { - value_const_converter_ = [](const void* const storage, const type_id& id) { - const auto ptr = static_cast(storage); - return ptr->_cast_(id); + value_const_converter_ = [](const void* const storage, const cetl::type_id& dst_type_id) { + CETL_DEBUG_ASSERT(nullptr != storage, ""); + const auto ptr = static_cast(storage); + const void* const dst_ptr = ptr->_cast_(dst_type_id); + return std::make_pair(dst_ptr, cetl::type_id_value); }; - value_converter_ = [](void* const storage, const type_id& id) { - auto ptr = static_cast(storage); - return ptr->_cast_(id); + value_mut_converter_ = [](void* const storage, const cetl::type_id& dst_type_id) { + CETL_DEBUG_ASSERT(nullptr != storage, ""); + const auto ptr = static_cast(storage); + return ptr->_cast_(dst_type_id); }; } template , int> = 0> void make_converters() noexcept { - value_const_converter_ = [](const void* const storage, const type_id& id) { - return (id == type_id_value) ? storage : nullptr; + value_const_converter_ = [](const void* const storage, const cetl::type_id& dst_type_id) { + CETL_DEBUG_ASSERT(nullptr != storage, ""); + const void* const dst_ptr = (dst_type_id == cetl::type_id_value) ? storage : nullptr; + return std::make_pair(dst_ptr, cetl::type_id_value); }; - value_converter_ = [](void* const storage, const type_id& id) { - return (id == type_id_value) ? storage : nullptr; + value_mut_converter_ = [](void* const storage, const cetl::type_id& dst_type_id) { + CETL_DEBUG_ASSERT(nullptr != storage, ""); + return (dst_type_id == cetl::type_id_value) ? storage : nullptr; }; } + /// \brief Returns the unique identifier of the actual type of the stored value. + /// `cetl::type_id_invalid` if storage is empty. + /// + CETL_NODISCARD cetl::type_id type_id() const noexcept + { + if (!has_value()) + { + return cetl::type_id_invalid; + } + CETL_DEBUG_ASSERT(nullptr != value_const_converter_, "Non-empty storage is expected to have value converter."); + + return value_const_converter_(base::get_raw_storage(), {}).second; + } + + /// \brief Returns the size of the stored value in bytes. + /// Zero if storage is empty. + /// + CETL_NODISCARD std::size_t type_size() const noexcept + { + return has_value() ? base::get_value_size() : 0UL; + } + template CETL_NODISCARD void* get_ptr() noexcept { @@ -588,7 +617,7 @@ struct base_access : base_storage } CETL_DEBUG_ASSERT(nullptr != value_const_converter_, "Non-empty storage is expected to have value converter."); - return value_converter_(base::get_raw_storage(), type_id_value); + return value_mut_converter_(base::get_raw_storage(), cetl::type_id_value); } template @@ -602,20 +631,20 @@ struct base_access : base_storage } CETL_DEBUG_ASSERT(nullptr != value_const_converter_, "Non-empty storage is expected to have value converter."); - return value_const_converter_(base::get_raw_storage(), type_id_value); + return value_const_converter_(base::get_raw_storage(), cetl::type_id_value).first; } void copy_handlers_from(const base_access& src) noexcept { value_destroyer_ = src.value_destroyer_; - value_converter_ = src.value_converter_; + value_mut_converter_ = src.value_mut_converter_; value_const_converter_ = src.value_const_converter_; } void move_handlers_from(base_access& src) noexcept { value_destroyer_ = src.value_destroyer_; - value_converter_ = src.value_converter_; + value_mut_converter_ = src.value_mut_converter_; value_const_converter_ = src.value_const_converter_; src.reset(); @@ -632,7 +661,7 @@ struct base_access : base_storage } value_destroyer_ = nullptr; - value_converter_ = nullptr; + value_mut_converter_ = nullptr; value_const_converter_ = nullptr; base::reset(); @@ -644,10 +673,21 @@ struct base_access : base_storage // Holds type-erased value destroyer. `nullptr` if storage has no value stored. void (*value_destroyer_)(void* self) = nullptr; - // Holds type-erased value converters (const and non-const). `nullptr` if storage has no value stored. + // Holds type-erased value converter. `nullptr` if storage has no value stored. + // This function does polymorphic casting, and returns converted raw pointer to the destination mutable value + // type (according to `dst_type_id`), otherwise `nullptr` if conversion is impossible (or `self` is `nullptr`). + // + void* (*value_mut_converter_)(void* self, const cetl::type_id& dst_type_id) = nullptr; + + // Holds type-erased const value converter. `nullptr` if storage has no value stored. + // This function does polymorphic casting, and returns: + // - Converted raw pointer to the destination const value type (according to `dst_type_id`), + // otherwise `nullptr` if conversion is impossible (or `self` is `nullptr`); + // - Unique identifier of the actual type of the stored value, + // otherwise `type_id_invalid` (all zeros) if storage is empty. // - void* (*value_converter_)(void* self, const type_id& id) = nullptr; - const void* (*value_const_converter_)(const void* self, const type_id& id) = nullptr; + using ValueConstPtrAndTypeId = std::pair; + ValueConstPtrAndTypeId (*value_const_converter_)(const void* self, const cetl::type_id& dst_type_id) = nullptr; }; // base_access @@ -1001,6 +1041,8 @@ class unbounded_variant : detail::base_move Date: Wed, 5 Jun 2024 19:03:15 +0300 Subject: [PATCH 09/12] Eliminated `type_id_invalid` definition. #verification #docs #sonar --- cetlvast/include/cetlvast/helpers_rtti.hpp | 82 +++++++++++++++++++ cetlvast/suites/compile/CMakeLists.txt | 2 +- ..._unbounded_variant_footprint_get_const.cpp | 11 +-- ...ounded_variant_footprint_get_non_const.cpp | 11 +-- .../test_unbounded_variant_footprint_set.cpp | 11 +-- .../unittest/test_unbounded_variant.cpp | 61 +++----------- include/cetl/rtti.hpp | 8 +- include/cetl/unbounded_variant.hpp | 6 +- 8 files changed, 103 insertions(+), 89 deletions(-) create mode 100644 cetlvast/include/cetlvast/helpers_rtti.hpp diff --git a/cetlvast/include/cetlvast/helpers_rtti.hpp b/cetlvast/include/cetlvast/helpers_rtti.hpp new file mode 100644 index 0000000..5f1ed56 --- /dev/null +++ b/cetlvast/include/cetlvast/helpers_rtti.hpp @@ -0,0 +1,82 @@ +/// @copyright +/// Copyright (C) OpenCyphal Development Team +/// Copyright Amazon.com Inc. or its affiliates. +/// SPDX-License-Identifier: MIT + +#ifndef CETLVAST_HELPERS_RTTI_HPP +#define CETLVAST_HELPERS_RTTI_HPP + +#include "cetl/rtti.hpp" + +#include +#include +#include +#include +#include + +namespace cetl +{ + +// 6B02B2B9-610B-414E-9304-E7FC5BC0D061 +template <> +constexpr type_id type_id_value = + {0x6B, 0x02, 0xB2, 0xB9, 0x61, 0x0B, 0x41, 0x4E, 0x93, 0x04, 0xE7, 0xFC, 0x5B, 0xC0, 0xD0, 0x61}; + +// AA3F7C4D-0E44-43CB-AB4C-2AE19E646F91 +template <> +constexpr type_id type_id_value = + {0xAA, 0x3F, 0x7C, 0x4D, 0x0E, 0x44, 0x43, 0xCB, 0xAB, 0x4C, 0x2A, 0xE1, 0x9E, 0x64, 0x6F, 0x91}; + +// 42844900-45ED-41A0-AA63-D6A42B60B343 +template <> +constexpr type_id type_id_value = + {0x42, 0x84, 0x49, 0x00, 0x45, 0xED, 0x41, 0xA0, 0xAA, 0x63, 0xD6, 0xA4, 0x2B, 0x60, 0xB3, 0x43}; + +// 6B5BE490-194C-4E2E-B8DE-3BB15CC52777 +template <> +constexpr type_id type_id_value = + {0x6B, 0x5B, 0xE4, 0x90, 0x19, 0x4C, 0x4E, 0x2E, 0xB8, 0xDE, 0x3B, 0xB1, 0x5C, 0xC5, 0x27, 0x77}; + +// 05855903-D323-41C3-8C58-691E035507D8 +template <> +constexpr type_id type_id_value = + {0x05, 0x85, 0x59, 0x03, 0xD3, 0x23, 0x41, 0xC3, 0x8C, 0x58, 0x69, 0x1E, 0x03, 0x55, 0x07, 0xD8}; + +// 6BC0579E-B665-480A-AFB0-45DB755A143E +template <> +constexpr type_id type_id_value = + {0x6B, 0xC0, 0x57, 0x9E, 0xB6, 0x65, 0x48, 0x0A, 0xAF, 0xB0, 0x45, 0xDB, 0x75, 0x5A, 0x14, 0x3E}; + +// 3C22EF31-63C0-4710-9AAE-966E89134C19 +template <> +constexpr type_id type_id_value = + {0x3C, 0x22, 0xEF, 0x31, 0x63, 0xC0, 0x47, 0x10, 0x9A, 0xAE, 0x96, 0x6E, 0x89, 0x13, 0x4C, 0x19}; + +// 89A2F7BC-5BEA-47BF-96C4-CFFA3A2DBBB2 +template <> +constexpr type_id type_id_value = + {0x89, 0xA2, 0xF7, 0xBC, 0x5B, 0xEA, 0x47, 0xBF, 0x96, 0xC4, 0xCF, 0xFA, 0x3A, 0x2D, 0xBB, 0xB2}; + +// A0672C3A-C6D2-4BF5-990A-1A4601264D60 +template <> +constexpr type_id type_id_value = + {0xA0, 0x67, 0x2C, 0x3A, 0xC6, 0xD2, 0x4B, 0xF5, 0x99, 0x0A, 0x1A, 0x46, 0x01, 0x26, 0x4D, 0x60}; + +// 473A0E53-86AB-4426-9F32-732D519F940D +template <> +constexpr type_id type_id_value> = + {0x47, 0x3A, 0x0E, 0x53, 0x86, 0xAB, 0x44, 0x26, 0x9F, 0x32, 0x73, 0x2D, 0x51, 0x9F, 0x94, 0x0D}; + +// D30E9194-8ECB-4831-9B31-F73C031DBFFB +template <> +constexpr type_id type_id_value> = + {0xD3, 0x0E, 0x91, 0x94, 0x8E, 0xCB, 0x48, 0x31, 0x9B, 0x31, 0xF7, 0x3C, 0x03, 0x1D, 0xBF, 0xFB}; + +// 63E796F8-AAFC-4E61-B545-99CE28B796FD +template <> +constexpr type_id type_id_value> = + {0x63, 0xE7, 0x96, 0xF8, 0xAA, 0xFC, 0x4E, 0x61, 0xB5, 0x45, 0x99, 0xCE, 0x28, 0xB7, 0x96, 0xFD}; + +} // namespace cetl + +#endif // CETLVAST_HELPERS_RTTI_HPP diff --git a/cetlvast/suites/compile/CMakeLists.txt b/cetlvast/suites/compile/CMakeLists.txt index 115b4c3..acdbc7b 100644 --- a/cetlvast/suites/compile/CMakeLists.txt +++ b/cetlvast/suites/compile/CMakeLists.txt @@ -21,7 +21,7 @@ set(ALL_TESTS_RUN "") foreach(COMPILE_TEST ${COMPILE_TESTS}) define_compile_failure_test(TEST_SOURCE ${COMPILE_TEST} - EXTRA_TEST_LIBS cetl + EXTRA_TEST_LIBS cetl cetlvast OUT_TEST_BUILD_TARGET COMPILE_TEST_BUILD_TARGET OUT_TEST_PRECHECK_TARGET COMPILE_TEST_PRECHECK_TARGET) list(APPEND ALL_TESTS_BUILD "${COMPILE_TEST_BUILD_TARGET}") diff --git a/cetlvast/suites/compile/test_unbounded_variant_footprint_get_const.cpp b/cetlvast/suites/compile/test_unbounded_variant_footprint_get_const.cpp index 1e82ed0..fea4f07 100644 --- a/cetlvast/suites/compile/test_unbounded_variant_footprint_get_const.cpp +++ b/cetlvast/suites/compile/test_unbounded_variant_footprint_get_const.cpp @@ -8,19 +8,10 @@ /// #include "cetl/unbounded_variant.hpp" +#include "cetlvast/helpers_rtti.hpp" #include -namespace cetl -{ -template <> -constexpr type_id type_id_value{}; - -template <> -constexpr type_id type_id_value{}; - -} // namespace cetl - int main() { using ub_var = cetl::unbounded_variant; diff --git a/cetlvast/suites/compile/test_unbounded_variant_footprint_get_non_const.cpp b/cetlvast/suites/compile/test_unbounded_variant_footprint_get_non_const.cpp index c118861..339f44e 100644 --- a/cetlvast/suites/compile/test_unbounded_variant_footprint_get_non_const.cpp +++ b/cetlvast/suites/compile/test_unbounded_variant_footprint_get_non_const.cpp @@ -8,19 +8,10 @@ /// #include "cetl/unbounded_variant.hpp" +#include "cetlvast/helpers_rtti.hpp" #include -namespace cetl -{ -template <> -constexpr type_id type_id_value{}; - -template <> -constexpr type_id type_id_value{}; - -} // namespace cetl - int main() { using ub_var = cetl::unbounded_variant; diff --git a/cetlvast/suites/compile/test_unbounded_variant_footprint_set.cpp b/cetlvast/suites/compile/test_unbounded_variant_footprint_set.cpp index c966332..8acb5c6 100644 --- a/cetlvast/suites/compile/test_unbounded_variant_footprint_set.cpp +++ b/cetlvast/suites/compile/test_unbounded_variant_footprint_set.cpp @@ -8,19 +8,10 @@ /// #include "cetl/unbounded_variant.hpp" +#include "cetlvast/helpers_rtti.hpp" #include -namespace cetl -{ -template <> -constexpr type_id type_id_value{}; - -template <> -constexpr type_id type_id_value{}; - -} // namespace cetl - int main() { using ub_var = cetl::unbounded_variant; diff --git a/cetlvast/suites/unittest/test_unbounded_variant.cpp b/cetlvast/suites/unittest/test_unbounded_variant.cpp index 36843e3..bbe7f00 100644 --- a/cetlvast/suites/unittest/test_unbounded_variant.cpp +++ b/cetlvast/suites/unittest/test_unbounded_variant.cpp @@ -7,6 +7,7 @@ /// SPDX-License-Identifier: MIT #include +#include #include #include @@ -19,44 +20,6 @@ // NOLINTBEGIN(*-use-after-move) -namespace cetl -{ - -template <> -constexpr type_id type_id_value = {1}; - -template <> -constexpr type_id type_id_value = {2}; - -template <> -constexpr type_id type_id_value = {3}; - -template <> -constexpr type_id type_id_value = {4}; - -template <> -constexpr type_id type_id_value = {5}; - -template <> -constexpr type_id type_id_value = {6}; - -template <> -constexpr type_id type_id_value = {7}; - -template <> -constexpr type_id type_id_value> = {8}; - -template <> -constexpr type_id type_id_value> = {9}; - -template <> -constexpr type_id type_id_value = {10}; - -template <> -constexpr type_id type_id_value> = {11}; - -} // namespace cetl - namespace { @@ -66,7 +29,7 @@ using cetl::get_if; using cetl::make_unbounded_variant; using cetl::type_id; using cetl::type_id_type; -using cetl::type_id_invalid; +using cetl::type_id_value; using cetl::rtti_helper; using testing::_; @@ -422,7 +385,7 @@ TEST_F(TestPmrUnboundedVariant, cppref_example) TEST_F(TestPmrUnboundedVariant, ctor_1_default) { EXPECT_THAT(unbounded_variant<1>{}.type_size(), 0UL); - EXPECT_THAT(unbounded_variant<1>{}.type_id(), type_id_invalid); + EXPECT_THAT(unbounded_variant<1>{}.type_id(), type_id_value); EXPECT_FALSE((unbounded_variant<1>{}.has_value())); EXPECT_FALSE((unbounded_variant<1, false>{}.has_value())); @@ -440,7 +403,7 @@ TEST_F(TestPmrUnboundedVariant, ctor_1_default) TEST_F(TestPmrUnboundedVariant, ctor_1_default_pmr) { EXPECT_THAT((unbounded_variant<0, true, true, 8, pmr>{get_mr()}.type_size()), 0UL); - EXPECT_THAT((unbounded_variant<0, true, true, 8, pmr>{get_mr()}.type_id()), type_id_invalid); + EXPECT_THAT((unbounded_variant<0, true, true, 8, pmr>{get_mr()}.type_id()), type_id_value); EXPECT_FALSE((unbounded_variant<0, false, false, 8, pmr>{get_mr()}.has_value())); EXPECT_FALSE((unbounded_variant<0, false, true, 8, pmr>{get_mr()}.has_value())); @@ -466,20 +429,20 @@ TEST_F(TestPmrUnboundedVariant, ctor_2_copy) const ub_var src{42}; EXPECT_THAT(src.type_size(), sizeof(int)); - EXPECT_THAT(src.type_id(), cetl::type_id_value); + EXPECT_THAT(src.type_id(), type_id_value); ub_var dst{src}; EXPECT_THAT(src.type_size(), sizeof(int)); - EXPECT_THAT(src.type_id(), cetl::type_id_value); + EXPECT_THAT(src.type_id(), type_id_value); EXPECT_THAT(dst.type_size(), sizeof(int)); - EXPECT_THAT(dst.type_id(), cetl::type_id_value); + EXPECT_THAT(dst.type_id(), type_id_value); EXPECT_THAT(get(src), 42); EXPECT_THAT(get(dst), 42); const ub_var empty{}; EXPECT_THAT(empty.type_size(), 0); - EXPECT_THAT(empty.type_id(), type_id_invalid); + EXPECT_THAT(empty.type_id(), type_id_value); ub_var dst2{empty}; EXPECT_THAT(dst2.has_value(), false); @@ -1067,7 +1030,7 @@ TEST_F(TestPmrUnboundedVariant, get_if_polymorphic) ub_var test_ubv = MyCopyableAndMovable{'Y', side_effects}; EXPECT_THAT(test_ubv.type_size(), sizeof(MyCopyableAndMovable)); - EXPECT_THAT(test_ubv.type_id(), cetl::type_id_value); + EXPECT_THAT(test_ubv.type_id(), type_id_value); auto& test_base1 = get(test_ubv); EXPECT_THAT(test_base1.payload_, 'Y'); @@ -1078,7 +1041,7 @@ TEST_F(TestPmrUnboundedVariant, get_if_polymorphic) test_ubv = MyBase{'X', side_effects}; EXPECT_THAT(test_ubv.type_size(), sizeof(MyBase)); - EXPECT_THAT(test_ubv.type_id(), cetl::type_id_value); + EXPECT_THAT(test_ubv.type_id(), type_id_value); auto& test_base2 = get(test_ubv); EXPECT_THAT(test_base2.payload_, 'X'); @@ -1226,7 +1189,7 @@ TEST_F(TestPmrUnboundedVariant, emplace_1_ctor_exception) EXPECT_THAT(t.has_value(), false); EXPECT_THAT(t.valueless_by_exception(), true); EXPECT_THAT(t.type_size(), 0); - EXPECT_THAT(t.type_id(), type_id_invalid); + EXPECT_THAT(t.type_id(), type_id_value); EXPECT_THAT(stats.constructs, 1); EXPECT_THAT(stats.destructs, 0); t.reset(); @@ -1464,7 +1427,7 @@ TEST_F(TestPmrUnboundedVariant, pmr_with_footprint_move_value_when_out_of_memory EXPECT_THAT(dst.has_value(), false); EXPECT_THAT(dst.valueless_by_exception(), true); EXPECT_THAT(dst.type_size(), 0); - EXPECT_THAT(dst.type_id(), type_id_invalid); + EXPECT_THAT(dst.type_id(), type_id_value); EXPECT_THAT(stats.ops, "@"); } EXPECT_THAT(stats.constructs, stats.destructs); diff --git a/include/cetl/rtti.hpp b/include/cetl/rtti.hpp index 00598fa..b6930b3 100644 --- a/include/cetl/rtti.hpp +++ b/include/cetl/rtti.hpp @@ -35,7 +35,7 @@ using type_id = std::array; /// The bytes of the UUID are given as a list of template parameters; there shall be at most 16 of them; /// if any are missing, they are assumed to be 0. /// For conversion to \ref type_id use \ref cetl::type_id_type_value. -/// Please don't use empty or all zeros bytes, as it will be treated as the invalid type ID (see \ref type_id_invalid). +/// Please don't use empty or all zeros bytes (reserved for `type_id_value` specialization). template using type_id_type = std::integer_sequence; @@ -82,14 +82,10 @@ constexpr type_id type_id_type_value() noexcept template constexpr type_id type_id_value = T::_get_type_id_(); -/// The type ID value specializations for `void` type. -/// -/// In use for the `type_id_invalid` constant, which f.e. is in use at -/// `unbounded_variant::type_id()` method to indicate that the variant is valueless. +/// The type ID value specialization reserved for `void` type - all zeros. /// template <> constexpr type_id type_id_value{}; -constexpr type_id type_id_invalid{type_id_value}; /// An alternative implementation of simple runtime type information (RTTI) capability designed for high-integrity /// real-time systems, where the use of the standard C++ RTTI is discouraged. diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index 66ab5ac..fbe5989 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -585,13 +585,13 @@ struct base_access : base_storage } /// \brief Returns the unique identifier of the actual type of the stored value. - /// `cetl::type_id_invalid` if storage is empty. + /// `cetl::type_id_value` if storage is empty. /// CETL_NODISCARD cetl::type_id type_id() const noexcept { if (!has_value()) { - return cetl::type_id_invalid; + return cetl::type_id_value; } CETL_DEBUG_ASSERT(nullptr != value_const_converter_, "Non-empty storage is expected to have value converter."); @@ -684,7 +684,7 @@ struct base_access : base_storage // - Converted raw pointer to the destination const value type (according to `dst_type_id`), // otherwise `nullptr` if conversion is impossible (or `self` is `nullptr`); // - Unique identifier of the actual type of the stored value, - // otherwise `type_id_invalid` (all zeros) if storage is empty. + // otherwise `type_id_value` (all zeros) if storage is empty. // using ValueConstPtrAndTypeId = std::pair; ValueConstPtrAndTypeId (*value_const_converter_)(const void* self, const cetl::type_id& dst_type_id) = nullptr; From e0adc23126e5c014cdd295c5295d3bf5887fa4ce Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Thu, 6 Jun 2024 11:36:57 +0300 Subject: [PATCH 10/12] Fix A5-1-6: return type of a non-void return type lambda expression should be explicitly specified. --- include/cetl/unbounded_variant.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/cetl/unbounded_variant.hpp b/include/cetl/unbounded_variant.hpp index fbe5989..77734c9 100644 --- a/include/cetl/unbounded_variant.hpp +++ b/include/cetl/unbounded_variant.hpp @@ -557,13 +557,14 @@ struct base_access : base_storage template , int> = 0> void make_converters() noexcept { - value_const_converter_ = [](const void* const storage, const cetl::type_id& dst_type_id) { + value_const_converter_ = [](const void* const storage, + const cetl::type_id& dst_type_id) -> ValueConstPtrAndTypeId { CETL_DEBUG_ASSERT(nullptr != storage, ""); const auto ptr = static_cast(storage); const void* const dst_ptr = ptr->_cast_(dst_type_id); return std::make_pair(dst_ptr, cetl::type_id_value); }; - value_mut_converter_ = [](void* const storage, const cetl::type_id& dst_type_id) { + value_mut_converter_ = [](void* const storage, const cetl::type_id& dst_type_id) -> void* { CETL_DEBUG_ASSERT(nullptr != storage, ""); const auto ptr = static_cast(storage); return ptr->_cast_(dst_type_id); @@ -573,12 +574,13 @@ struct base_access : base_storage template , int> = 0> void make_converters() noexcept { - value_const_converter_ = [](const void* const storage, const cetl::type_id& dst_type_id) { + value_const_converter_ = [](const void* const storage, + const cetl::type_id& dst_type_id) -> ValueConstPtrAndTypeId { CETL_DEBUG_ASSERT(nullptr != storage, ""); const void* const dst_ptr = (dst_type_id == cetl::type_id_value) ? storage : nullptr; return std::make_pair(dst_ptr, cetl::type_id_value); }; - value_mut_converter_ = [](void* const storage, const cetl::type_id& dst_type_id) { + value_mut_converter_ = [](void* const storage, const cetl::type_id& dst_type_id) -> void* { CETL_DEBUG_ASSERT(nullptr != storage, ""); return (dst_type_id == cetl::type_id_value) ? storage : nullptr; }; From c813c5033de7d5809e7b291b39a6938d983cfd52 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Thu, 6 Jun 2024 14:38:34 +0300 Subject: [PATCH 11/12] Fix ODR violation by having helper `type_id_getter()` template function. --- include/cetl/rtti.hpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/include/cetl/rtti.hpp b/include/cetl/rtti.hpp index b6930b3..f0654e3 100644 --- a/include/cetl/rtti.hpp +++ b/include/cetl/rtti.hpp @@ -76,16 +76,28 @@ constexpr type_id type_id_type_value() noexcept return detail::type_id_type_value_impl(TypeIDType{}); } -/// The type ID value of the given type. -/// This helper is provided for regularity; it has the same value as \c T::_get_type_id_(). +/// The type ID getter for the given type. +/// This helper is provided for regularity; it returns the same value as \c T::_get_type_id_(). /// The type shall satisfy \ref cetl::has_type_id. template -constexpr type_id type_id_value = T::_get_type_id_(); +constexpr type_id type_id_getter() noexcept +{ + return T::_get_type_id_(); +} -/// The type ID value specialization reserved for `void` type - all zeros. +/// The type ID getter specialization reserved for `void` type - returns all zeros. /// template <> -constexpr type_id type_id_value{}; +constexpr type_id type_id_getter() noexcept +{ + return {}; +} + +/// The type ID value of the given type. +/// This helper is provided for regularity; it has the same value as \c T::_get_type_id_(). +/// The type shall satisfy \ref cetl::has_type_id. +template +constexpr type_id type_id_value = type_id_getter(); /// An alternative implementation of simple runtime type information (RTTI) capability designed for high-integrity /// real-time systems, where the use of the standard C++ RTTI is discouraged. From 50e70deba932c6071f36cc4e0a5edb1fd3affefc Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Thu, 6 Jun 2024 15:20:15 +0300 Subject: [PATCH 12/12] =?UTF-8?q?`type=5Fid=5Fvalue`=20=E2=86=92=20`type?= =?UTF-8?q?=5Fid=5Fgetter`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cetlvast/include/cetlvast/helpers_rtti.hpp | 86 +++++++++++-------- .../examples/example_10_unbounded_variant.cpp | 21 ++++- .../unittest/test_unbounded_variant.cpp | 15 ++-- include/cetl/rtti.hpp | 2 + 4 files changed, 80 insertions(+), 44 deletions(-) diff --git a/cetlvast/include/cetlvast/helpers_rtti.hpp b/cetlvast/include/cetlvast/helpers_rtti.hpp index 5f1ed56..f34614a 100644 --- a/cetlvast/include/cetlvast/helpers_rtti.hpp +++ b/cetlvast/include/cetlvast/helpers_rtti.hpp @@ -17,65 +17,83 @@ namespace cetl { +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers) + // 6B02B2B9-610B-414E-9304-E7FC5BC0D061 template <> -constexpr type_id type_id_value = - {0x6B, 0x02, 0xB2, 0xB9, 0x61, 0x0B, 0x41, 0x4E, 0x93, 0x04, 0xE7, 0xFC, 0x5B, 0xC0, 0xD0, 0x61}; - +constexpr type_id type_id_getter() noexcept +{ + return {0x6B, 0x02, 0xB2, 0xB9, 0x61, 0x0B, 0x41, 0x4E, 0x93, 0x04, 0xE7, 0xFC, 0x5B, 0xC0, 0xD0, 0x61}; +} // AA3F7C4D-0E44-43CB-AB4C-2AE19E646F91 template <> -constexpr type_id type_id_value = - {0xAA, 0x3F, 0x7C, 0x4D, 0x0E, 0x44, 0x43, 0xCB, 0xAB, 0x4C, 0x2A, 0xE1, 0x9E, 0x64, 0x6F, 0x91}; - +constexpr type_id type_id_getter() noexcept +{ + return {0xAA, 0x3F, 0x7C, 0x4D, 0x0E, 0x44, 0x43, 0xCB, 0xAB, 0x4C, 0x2A, 0xE1, 0x9E, 0x64, 0x6F, 0x91}; +} // 42844900-45ED-41A0-AA63-D6A42B60B343 template <> -constexpr type_id type_id_value = - {0x42, 0x84, 0x49, 0x00, 0x45, 0xED, 0x41, 0xA0, 0xAA, 0x63, 0xD6, 0xA4, 0x2B, 0x60, 0xB3, 0x43}; - +constexpr type_id type_id_getter() noexcept +{ + return {0x42, 0x84, 0x49, 0x00, 0x45, 0xED, 0x41, 0xA0, 0xAA, 0x63, 0xD6, 0xA4, 0x2B, 0x60, 0xB3, 0x43}; +} // 6B5BE490-194C-4E2E-B8DE-3BB15CC52777 template <> -constexpr type_id type_id_value = - {0x6B, 0x5B, 0xE4, 0x90, 0x19, 0x4C, 0x4E, 0x2E, 0xB8, 0xDE, 0x3B, 0xB1, 0x5C, 0xC5, 0x27, 0x77}; +constexpr type_id type_id_getter() noexcept +{ + return {0x6B, 0x5B, 0xE4, 0x90, 0x19, 0x4C, 0x4E, 0x2E, 0xB8, 0xDE, 0x3B, 0xB1, 0x5C, 0xC5, 0x27, 0x77}; +} // 05855903-D323-41C3-8C58-691E035507D8 template <> -constexpr type_id type_id_value = - {0x05, 0x85, 0x59, 0x03, 0xD3, 0x23, 0x41, 0xC3, 0x8C, 0x58, 0x69, 0x1E, 0x03, 0x55, 0x07, 0xD8}; - +constexpr type_id type_id_getter() noexcept +{ + return {0x05, 0x85, 0x59, 0x03, 0xD3, 0x23, 0x41, 0xC3, 0x8C, 0x58, 0x69, 0x1E, 0x03, 0x55, 0x07, 0xD8}; +} // 6BC0579E-B665-480A-AFB0-45DB755A143E template <> -constexpr type_id type_id_value = - {0x6B, 0xC0, 0x57, 0x9E, 0xB6, 0x65, 0x48, 0x0A, 0xAF, 0xB0, 0x45, 0xDB, 0x75, 0x5A, 0x14, 0x3E}; - +constexpr type_id type_id_getter() noexcept +{ + return {0x6B, 0xC0, 0x57, 0x9E, 0xB6, 0x65, 0x48, 0x0A, 0xAF, 0xB0, 0x45, 0xDB, 0x75, 0x5A, 0x14, 0x3E}; +} // 3C22EF31-63C0-4710-9AAE-966E89134C19 template <> -constexpr type_id type_id_value = - {0x3C, 0x22, 0xEF, 0x31, 0x63, 0xC0, 0x47, 0x10, 0x9A, 0xAE, 0x96, 0x6E, 0x89, 0x13, 0x4C, 0x19}; - +constexpr type_id type_id_getter() noexcept +{ + return {0x3C, 0x22, 0xEF, 0x31, 0x63, 0xC0, 0x47, 0x10, 0x9A, 0xAE, 0x96, 0x6E, 0x89, 0x13, 0x4C, 0x19}; +} // 89A2F7BC-5BEA-47BF-96C4-CFFA3A2DBBB2 template <> -constexpr type_id type_id_value = - {0x89, 0xA2, 0xF7, 0xBC, 0x5B, 0xEA, 0x47, 0xBF, 0x96, 0xC4, 0xCF, 0xFA, 0x3A, 0x2D, 0xBB, 0xB2}; - +constexpr type_id type_id_getter() noexcept +{ + return {0x89, 0xA2, 0xF7, 0xBC, 0x5B, 0xEA, 0x47, 0xBF, 0x96, 0xC4, 0xCF, 0xFA, 0x3A, 0x2D, 0xBB, 0xB2}; +} // A0672C3A-C6D2-4BF5-990A-1A4601264D60 template <> -constexpr type_id type_id_value = - {0xA0, 0x67, 0x2C, 0x3A, 0xC6, 0xD2, 0x4B, 0xF5, 0x99, 0x0A, 0x1A, 0x46, 0x01, 0x26, 0x4D, 0x60}; - +constexpr type_id type_id_getter() noexcept +{ + return {0xA0, 0x67, 0x2C, 0x3A, 0xC6, 0xD2, 0x4B, 0xF5, 0x99, 0x0A, 0x1A, 0x46, 0x01, 0x26, 0x4D, 0x60}; +} // 473A0E53-86AB-4426-9F32-732D519F940D template <> -constexpr type_id type_id_value> = - {0x47, 0x3A, 0x0E, 0x53, 0x86, 0xAB, 0x44, 0x26, 0x9F, 0x32, 0x73, 0x2D, 0x51, 0x9F, 0x94, 0x0D}; - +constexpr type_id type_id_getter>() noexcept +{ + return {0x47, 0x3A, 0x0E, 0x53, 0x86, 0xAB, 0x44, 0x26, 0x9F, 0x32, 0x73, 0x2D, 0x51, 0x9F, 0x94, 0x0D}; +} // D30E9194-8ECB-4831-9B31-F73C031DBFFB template <> -constexpr type_id type_id_value> = - {0xD3, 0x0E, 0x91, 0x94, 0x8E, 0xCB, 0x48, 0x31, 0x9B, 0x31, 0xF7, 0x3C, 0x03, 0x1D, 0xBF, 0xFB}; - +constexpr type_id type_id_getter>() noexcept +{ + return {0xD3, 0x0E, 0x91, 0x94, 0x8E, 0xCB, 0x48, 0x31, 0x9B, 0x31, 0xF7, 0x3C, 0x03, 0x1D, 0xBF, 0xFB}; +} // 63E796F8-AAFC-4E61-B545-99CE28B796FD template <> -constexpr type_id type_id_value> = - {0x63, 0xE7, 0x96, 0xF8, 0xAA, 0xFC, 0x4E, 0x61, 0xB5, 0x45, 0x99, 0xCE, 0x28, 0xB7, 0x96, 0xFD}; +constexpr type_id type_id_getter>() noexcept +{ + return {0x63, 0xE7, 0x96, 0xF8, 0xAA, 0xFC, 0x4E, 0x61, 0xB5, 0x45, 0x99, 0xCE, 0x28, 0xB7, 0x96, 0xFD}; +} + +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers) } // namespace cetl diff --git a/cetlvast/suites/docs/examples/example_10_unbounded_variant.cpp b/cetlvast/suites/docs/examples/example_10_unbounded_variant.cpp index 00ca124..8f5cdd4 100644 --- a/cetlvast/suites/docs/examples/example_10_unbounded_variant.cpp +++ b/cetlvast/suites/docs/examples/example_10_unbounded_variant.cpp @@ -13,14 +13,27 @@ //! [example_10_unbounded_variant_type_id] namespace cetl { + template <> -constexpr type_id type_id_value = {1}; +constexpr type_id type_id_getter() noexcept +{ + return {1}; +} template <> -constexpr type_id type_id_value = {2}; +constexpr type_id type_id_getter() noexcept +{ + return {2}; +} template <> -constexpr type_id type_id_value = {3}; +constexpr type_id type_id_getter() noexcept +{ + return {3}; +} template <> -constexpr type_id type_id_value = {4}; +constexpr type_id type_id_getter() noexcept +{ + return {4}; +} } // namespace cetl //! [example_10_unbounded_variant_type_id] diff --git a/cetlvast/suites/unittest/test_unbounded_variant.cpp b/cetlvast/suites/unittest/test_unbounded_variant.cpp index bbe7f00..40b07f6 100644 --- a/cetlvast/suites/unittest/test_unbounded_variant.cpp +++ b/cetlvast/suites/unittest/test_unbounded_variant.cpp @@ -444,7 +444,7 @@ TEST_F(TestPmrUnboundedVariant, ctor_2_copy) EXPECT_THAT(empty.type_size(), 0); EXPECT_THAT(empty.type_id(), type_id_value); - ub_var dst2{empty}; + ub_var dst2{empty}; EXPECT_THAT(dst2.has_value(), false); dst2 = {}; EXPECT_THAT(dst2.has_value(), false); @@ -1752,12 +1752,15 @@ namespace cetl { template <> -constexpr type_id type_id_value> = - {0xB3, 0xB8, 0x4E, 0xC1, 0x1F, 0xE4, 0x49, 0x35, 0x9E, 0xC9, 0x1A, 0x77, 0x7B, 0x82, 0x53, 0x25}; - +constexpr type_id type_id_getter>() noexcept +{ + return {0xB3, 0xB8, 0x4E, 0xC1, 0x1F, 0xE4, 0x49, 0x35, 0x9E, 0xC9, 0x1A, 0x77, 0x7B, 0x82, 0x53, 0x25}; +} template <> -constexpr type_id type_id_value = - {0xD5, 0x62, 0x39, 0x66, 0x90, 0x8B, 0x4F, 0x56, 0x8F, 0x2A, 0x2F, 0x4F, 0xDF, 0x3F, 0x31, 0x5B}; +constexpr type_id type_id_getter() noexcept +{ + return {0xD5, 0x62, 0x39, 0x66, 0x90, 0x8B, 0x4F, 0x56, 0x8F, 0x2A, 0x2F, 0x4F, 0xDF, 0x3F, 0x31, 0x5B}; +} } // namespace cetl diff --git a/include/cetl/rtti.hpp b/include/cetl/rtti.hpp index f0654e3..0373b8f 100644 --- a/include/cetl/rtti.hpp +++ b/include/cetl/rtti.hpp @@ -79,6 +79,8 @@ constexpr type_id type_id_type_value() noexcept /// The type ID getter for the given type. /// This helper is provided for regularity; it returns the same value as \c T::_get_type_id_(). /// The type shall satisfy \ref cetl::has_type_id. +/// Specialize this getter to add RTTI support to types where it is not possible to define a static method +/// (e.g., builtins, pointers, third-party classes, etc). template constexpr type_id type_id_getter() noexcept {