From 72311fdbf70ad58a9939b230145efaf8eeab039d Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 5 Aug 2025 11:05:58 -0700 Subject: [PATCH 01/30] Construct Variant with move-only type --- tests/VariantTest.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 684d0c50e..3a54b4d00 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -8,6 +8,17 @@ const char *s_variant_test_str = "This is a string, that should be long enough to avoid small string optimizations"; +struct MoveOnlyStruct +{ + MoveOnlyStruct() = default; + + MoveOnlyStruct(MoveOnlyStruct &&) = default; + MoveOnlyStruct &operator=(MoveOnlyStruct &&) = default; + + MoveOnlyStruct(const MoveOnlyStruct &) = delete; + MoveOnlyStruct &operator=(const MoveOnlyStruct &) = delete; +}; + static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -42,6 +53,13 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * var2CpyAssigned = var2a; MyTestVariant2 var2aCpyConstructedVariant(var2aCpyAssigned); } + + { + // test with a move-only type + using MyTestVariant3 = Aws::Crt::Variant; + MyTestVariant3 var3(MoveOnlyStruct{}); + MyTestVariant3 var3a = std::move(var3); + } } return AWS_OP_SUCCESS; From 68c31e00b86793976b5367045f282d3316985f33 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 5 Aug 2025 11:22:00 -0700 Subject: [PATCH 02/30] Make more similar to cpp-v2 failure test --- tests/VariantTest.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 3a54b4d00..cfef8f09b 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -8,17 +8,6 @@ const char *s_variant_test_str = "This is a string, that should be long enough to avoid small string optimizations"; -struct MoveOnlyStruct -{ - MoveOnlyStruct() = default; - - MoveOnlyStruct(MoveOnlyStruct &&) = default; - MoveOnlyStruct &operator=(MoveOnlyStruct &&) = default; - - MoveOnlyStruct(const MoveOnlyStruct &) = delete; - MoveOnlyStruct &operator=(const MoveOnlyStruct &) = delete; -}; - static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -56,9 +45,20 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * { // test with a move-only type - using MyTestVariant3 = Aws::Crt::Variant; - MyTestVariant3 var3(MoveOnlyStruct{}); + + struct TestError + { + explicit operator bool() const noexcept { return baseStatus; } + + bool baseStatus; + int crtError; + }; + + using MyTestVariant3 = Aws::Crt::Variant, TestError>; + Aws::Crt::ScopedResource ptr(new Aws::Crt::String("12345")); + MyTestVariant3 var3(std::move(ptr)); MyTestVariant3 var3a = std::move(var3); + (void)var3a; } } @@ -382,4 +382,4 @@ static int s_VariantVisitor(struct aws_allocator *allocator, void *ctx) return AWS_OP_SUCCESS; } -AWS_TEST_CASE(VariantVisitor, s_VariantVisitor) \ No newline at end of file +AWS_TEST_CASE(VariantVisitor, s_VariantVisitor) From 317a09b12c83347ad5514d7fabf848ead6b93f51 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 5 Aug 2025 12:42:20 -0700 Subject: [PATCH 03/30] Even more similar --- tests/VariantTest.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index cfef8f09b..a67e3002f 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -8,6 +8,12 @@ const char *s_variant_test_str = "This is a string, that should be long enough to avoid small string optimizations"; +#if defined(WIN32) +# define AWS_VARIANTTEST_API __declspec(dllexport) +#else +# define AWS_VARIANTTEST_API +#endif + static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -46,7 +52,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * { // test with a move-only type - struct TestError + struct AWS_VARIANTTEST_API TestError { explicit operator bool() const noexcept { return baseStatus; } @@ -55,7 +61,8 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * }; using MyTestVariant3 = Aws::Crt::Variant, TestError>; - Aws::Crt::ScopedResource ptr(new Aws::Crt::String("12345")); + Aws::Crt::ScopedResource ptr( + new Aws::Crt::String("12345"), [](Aws::Crt::String *p) { delete p; }); MyTestVariant3 var3(std::move(ptr)); MyTestVariant3 var3a = std::move(var3); (void)var3a; From 85d7beaef25ef27620ee4e3c28a4dc64ea9fbe85 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 5 Aug 2025 13:08:41 -0700 Subject: [PATCH 04/30] Wrap into class --- tests/VariantTest.cpp | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index a67e3002f..0f909b134 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -61,11 +61,38 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * }; using MyTestVariant3 = Aws::Crt::Variant, TestError>; - Aws::Crt::ScopedResource ptr( - new Aws::Crt::String("12345"), [](Aws::Crt::String *p) { delete p; }); - MyTestVariant3 var3(std::move(ptr)); - MyTestVariant3 var3a = std::move(var3); - (void)var3a; + + class FailVariantTestResult + { + public: + FailVariantTestResult() = default; + FailVariantTestResult(MyTestVariant3 &&result) : m_result(std::move(result)) {} + + Aws::Crt::String *GetFirst() const noexcept + { + if (m_result.holds_alternative>()) + { + return m_result.get>().get(); + } + + return nullptr; + } + + const TestError *GetSecond() const noexcept + { + if (m_result.holds_alternative()) + { + return &m_result.get(); + } + + return nullptr; + } + + private: + MyTestVariant3 m_result; + }; + + FailVariantTestResult result1; } } From 5951115fd215c70bbe30f98bad92fcbbfb1d8f39 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Wed, 6 Aug 2025 15:47:31 -0700 Subject: [PATCH 05/30] Add VariantWrapper --- include/aws/crt/Variant.h | 161 ++++++++++++++++++++++++++++++++++++++ tests/VariantTest.cpp | 6 +- 2 files changed, 164 insertions(+), 3 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 1ac603fb8..981e3abdc 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -15,8 +15,23 @@ namespace Aws { namespace Crt { + namespace detail + { + template struct Conjunction : std::true_type + { + }; + + template + struct Conjunction + : std::conditional, std::false_type>::type + { + }; + + } // namespace detail + namespace VariantDetail { + // TODO Pass values instead of refs. template constexpr const T &ConstExprMax(const T &a, const T &b) { return (a < b) ? b : a; @@ -116,6 +131,33 @@ namespace Aws }; } // namespace VariantDebug #endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ + + template class CopyableVariantImpl + { + public: + CopyableVariantImpl(const CopyableVariantImpl &) = default; + CopyableVariantImpl &operator=(const CopyableVariantImpl &) = default; + }; + template <> class CopyableVariantImpl + { + public: + CopyableVariantImpl(const CopyableVariantImpl &) = delete; + CopyableVariantImpl &operator=(const CopyableVariantImpl &) = delete; + }; + + template class MovableVariantImpl + { + public: + MovableVariantImpl(MovableVariantImpl &&) = default; + MovableVariantImpl &operator=(MovableVariantImpl &&) = default; + }; + template <> class MovableVariantImpl + { + public: + MovableVariantImpl(MovableVariantImpl &&) = delete; + MovableVariantImpl &operator=(MovableVariantImpl &&) = delete; + }; + } // namespace VariantDetail template class VariantAlternative; @@ -137,6 +179,11 @@ namespace Aws using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); + static constexpr bool is_copy_constructible = detail::Conjunction...>::value; + static constexpr bool is_copy_assignable = detail::Conjunction...>::value; + static constexpr bool is_move_constructible = detail::Conjunction...>::value; + static constexpr bool is_move_assignable = detail::Conjunction...>::value; + Variant() { using FirstAlternative = typename ThisVariantAlternative<0>::type; @@ -588,5 +635,119 @@ namespace Aws { constexpr static const std::size_t Value = T::AlternativeCount; }; + + template class VariantWrapper + { + private: + template using ThisVariantAlternative = VariantAlternative; + + template + using EnableIfOtherIsThisVariantAlternative = typename std:: + enable_if::type, Ts...>::value, int>::type; + + public: + using IndexT = VariantDetail::Index::VariantIndex; + static constexpr std::size_t AlternativeCount = sizeof...(Ts); + + VariantWrapper() = default; + + VariantWrapper(const VariantWrapper &) = default; + VariantWrapper(VariantWrapper &&) = default; + + VariantWrapper &operator=(const VariantWrapper &) = default; + VariantWrapper &operator=(VariantWrapper &&) = default; + + template = 1> + VariantWrapper(const T &val) : m_variant(val) + { + } + + template = 1> + VariantWrapper(T &&val) : m_variant(std::forward(val)) + { + } + + template + explicit VariantWrapper(Aws::Crt::InPlaceTypeT ipt, Args &&...args) + : m_variant(ipt, std::forward(args)...) + { + } + + template = 1> + T &emplace(Args &&...args) + { + return m_variant.emplace(std::forward(args)...); + } + + template + auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & + { + return m_variant.emplace(std::forward(args)...); + } + + template = 1> bool holds_alternative() const + { + return m_variant.template holds_alternative(); + } + + template = 1> T &get() + { + return m_variant.template get(); + } + + template = 1> T *get_if() + { + return m_variant.template get_if(); + } + + template auto get() -> typename ThisVariantAlternative::type & + { + return m_variant.template get(); + } + + template = 1> const T &get() const + { + return m_variant.template get(); + } + + template = 1> const T *get_if() const + { + return m_variant.template get_if(); + } + + template auto get() const -> const typename ThisVariantAlternative::type & + { + return m_variant.template get(); + } + + template + using RawAlternativePointerT = + typename std::add_pointer::type>::type; + + template auto get_if() -> RawAlternativePointerT + { + return m_variant.template get_if(); + } + + template + using ConstRawAlternativePointerT = typename std::add_pointer< + typename std::add_const::type>::type>::type; + + template auto get_if() const -> ConstRawAlternativePointerT + { + return m_variant.template get_if(); + } + + std::size_t index() const { return m_variant.index(); } + + template void Visit(VisitorT &&visitor) + { + m_variant.Visit(std::forward(visitor)); + } + + private: + Variant m_variant; + }; + } // namespace Crt } // namespace Aws diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 0f909b134..35c752a84 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -21,7 +21,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * Aws::Crt::ApiHandle apiHandle(allocator); { - using MyTestVariant1 = Aws::Crt::Variant; + using MyTestVariant1 = Aws::Crt::VariantWrapper; MyTestVariant1 var1; MyTestVariant1 var1CpyAssigned; var1CpyAssigned = var1; @@ -36,7 +36,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * { // just a different order or types - using MyTestVariant2 = Aws::Crt::Variant; + using MyTestVariant2 = Aws::Crt::VariantWrapper; MyTestVariant2 var2; MyTestVariant2 var2CpyAssigned; var2CpyAssigned = var2; @@ -60,7 +60,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * int crtError; }; - using MyTestVariant3 = Aws::Crt::Variant, TestError>; + using MyTestVariant3 = Aws::Crt::VariantWrapper, TestError>; class FailVariantTestResult { From c8cb6cee6108a60cfb21ffcae7644283af70a259 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Fri, 8 Aug 2025 15:15:13 -0700 Subject: [PATCH 06/30] Add failing test --- tests/VariantTest.cpp | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 35c752a84..d111c89eb 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -68,31 +68,14 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * FailVariantTestResult() = default; FailVariantTestResult(MyTestVariant3 &&result) : m_result(std::move(result)) {} - Aws::Crt::String *GetFirst() const noexcept - { - if (m_result.holds_alternative>()) - { - return m_result.get>().get(); - } - - return nullptr; - } - - const TestError *GetSecond() const noexcept - { - if (m_result.holds_alternative()) - { - return &m_result.get(); - } - - return nullptr; - } - private: MyTestVariant3 m_result; }; FailVariantTestResult result1; + FailVariantTestResult result2; + + result1 = result2; } } From b542771faa7b7d7c21cb67a5c1cb6f48ee906714 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 16:36:03 -0700 Subject: [PATCH 07/30] Try to reproduce with AWS_CRT_CPP_API --- tests/VariantTest.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index d111c89eb..1a2a139c5 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -52,30 +52,32 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * { // test with a move-only type - struct AWS_VARIANTTEST_API TestError + struct MoveOnlyTest { - explicit operator bool() const noexcept { return baseStatus; } + MoveOnlyTest() = default; - bool baseStatus; - int crtError; + MoveOnlyTest(MoveOnlyTest &&) = default; + MoveOnlyTest &operator=(MoveOnlyTest &&) = default; + + MoveOnlyTest(const MoveOnlyTest &) = delete; + MoveOnlyTest &operator=(const MoveOnlyTest &) = delete; }; - using MyTestVariant3 = Aws::Crt::VariantWrapper, TestError>; + using MyMoveOnlyVariant = Aws::Crt::VariantWrapper; - class FailVariantTestResult + /* Regression test. + * AWS_CRT_CPP_API expands into __declspec(dllexport) on Windows platform when dll semantics is enabled. + * __declspec(dllexport) causes compiler to generate all special members of the marked class. + * These generated special members should be valid. */ + class AWS_CRT_CPP_API FailVariantTestResult { public: FailVariantTestResult() = default; - FailVariantTestResult(MyTestVariant3 &&result) : m_result(std::move(result)) {} + FailVariantTestResult(MyMoveOnlyVariant &&result) : m_result(std::move(result)) {} private: - MyTestVariant3 m_result; + MyMoveOnlyVariant m_result; }; - - FailVariantTestResult result1; - FailVariantTestResult result2; - - result1 = result2; } } From 8289f3fc4a189dcc4e3316a7f33b5cbcc338b576 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 16:46:38 -0700 Subject: [PATCH 08/30] Try to instantiate --- tests/VariantTest.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 1a2a139c5..7e2f8fddc 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -69,7 +69,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * * AWS_CRT_CPP_API expands into __declspec(dllexport) on Windows platform when dll semantics is enabled. * __declspec(dllexport) causes compiler to generate all special members of the marked class. * These generated special members should be valid. */ - class AWS_CRT_CPP_API FailVariantTestResult + class AWS_VARIANTTEST_API FailVariantTestResult { public: FailVariantTestResult() = default; @@ -78,6 +78,10 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * private: MyMoveOnlyVariant m_result; }; + + FailVariantTestResult result1; + FailVariantTestResult result2; + result2 = std::move(result1); } } From d6fcd7814b5d04b1cdf20875fded291831114480 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 16:56:01 -0700 Subject: [PATCH 09/30] Simplify test --- tests/VariantTest.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 7e2f8fddc..d8a357ebd 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -72,16 +72,8 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * class AWS_VARIANTTEST_API FailVariantTestResult { public: - FailVariantTestResult() = default; - FailVariantTestResult(MyMoveOnlyVariant &&result) : m_result(std::move(result)) {} - - private: MyMoveOnlyVariant m_result; }; - - FailVariantTestResult result1; - FailVariantTestResult result2; - result2 = std::move(result1); } } From d13dbed6230c269f839d7f7ea652f8ce81021697 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 17:11:01 -0700 Subject: [PATCH 10/30] Use only Variant in tests for now --- tests/VariantTest.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index d8a357ebd..6ca304ca7 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -8,6 +8,7 @@ const char *s_variant_test_str = "This is a string, that should be long enough to avoid small string optimizations"; +// TODO ? Check for AWS_CRT_USE_WINDOWS_DLL_SEMANTICS #if defined(WIN32) # define AWS_VARIANTTEST_API __declspec(dllexport) #else @@ -21,7 +22,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * Aws::Crt::ApiHandle apiHandle(allocator); { - using MyTestVariant1 = Aws::Crt::VariantWrapper; + using MyTestVariant1 = Aws::Crt::Variant; MyTestVariant1 var1; MyTestVariant1 var1CpyAssigned; var1CpyAssigned = var1; @@ -36,7 +37,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * { // just a different order or types - using MyTestVariant2 = Aws::Crt::VariantWrapper; + using MyTestVariant2 = Aws::Crt::Variant; MyTestVariant2 var2; MyTestVariant2 var2CpyAssigned; var2CpyAssigned = var2; @@ -63,7 +64,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * MoveOnlyTest &operator=(const MoveOnlyTest &) = delete; }; - using MyMoveOnlyVariant = Aws::Crt::VariantWrapper; + using MyMoveOnlyVariant = Aws::Crt::Variant; /* Regression test. * AWS_CRT_CPP_API expands into __declspec(dllexport) on Windows platform when dll semantics is enabled. From 2ae5d814c8d5cdeee27a37d95c14270257ae459d Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 17:41:52 -0700 Subject: [PATCH 11/30] Use VariantWrapper with a fix --- include/aws/crt/Variant.h | 56 ++++++++++++++++++++++++++------------- tests/VariantTest.cpp | 2 +- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 981e3abdc..b96dfa012 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -132,30 +132,51 @@ namespace Aws } // namespace VariantDebug #endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ - template class CopyableVariantImpl + /* Depending on the Variant types, this struct either deletes special move members or defaults them. */ + template class MovableVariant; + + template <> class MovableVariant { public: - CopyableVariantImpl(const CopyableVariantImpl &) = default; - CopyableVariantImpl &operator=(const CopyableVariantImpl &) = default; + MovableVariant() = default; + MovableVariant(const MovableVariant &) = default; + MovableVariant &operator=(const MovableVariant &) = default; + MovableVariant(MovableVariant &&other) = default; + MovableVariant &operator=(MovableVariant &&other) = default; }; - template <> class CopyableVariantImpl + template <> class MovableVariant { public: - CopyableVariantImpl(const CopyableVariantImpl &) = delete; - CopyableVariantImpl &operator=(const CopyableVariantImpl &) = delete; + MovableVariant() = default; + MovableVariant(const MovableVariant &) = default; + MovableVariant &operator=(const MovableVariant &) = default; + MovableVariant(MovableVariant &&) = delete; + MovableVariant &operator=(MovableVariant &&) = delete; }; - template class MovableVariantImpl + /* Depending on the Variant types, this struct either deletes special copy members or defaults them. */ + template class CopyableVariant; + + template <> class CopyableVariant { public: - MovableVariantImpl(MovableVariantImpl &&) = default; - MovableVariantImpl &operator=(MovableVariantImpl &&) = default; + CopyableVariant() = default; + CopyableVariant(const CopyableVariant &other) = default; + CopyableVariant &operator=(const CopyableVariant &other) = default; + CopyableVariant(CopyableVariant &&) = default; + CopyableVariant &operator=(CopyableVariant &&) = default; }; - template <> class MovableVariantImpl + + template <> class CopyableVariant { public: - MovableVariantImpl(MovableVariantImpl &&) = delete; - MovableVariantImpl &operator=(MovableVariantImpl &&) = delete; + CopyableVariant() = default; + + CopyableVariant(const CopyableVariant &) = delete; + CopyableVariant &operator=(const CopyableVariant &) = delete; + + CopyableVariant(CopyableVariant &&) = default; + CopyableVariant &operator=(CopyableVariant &&) = default; }; } // namespace VariantDetail @@ -636,7 +657,10 @@ namespace Aws constexpr static const std::size_t Value = T::AlternativeCount; }; - template class VariantWrapper + template + class VariantWrapper + : public VariantDetail::MovableVariant...>::value>, + public VariantDetail::CopyableVariant...>::value> { private: template using ThisVariantAlternative = VariantAlternative; @@ -651,12 +675,6 @@ namespace Aws VariantWrapper() = default; - VariantWrapper(const VariantWrapper &) = default; - VariantWrapper(VariantWrapper &&) = default; - - VariantWrapper &operator=(const VariantWrapper &) = default; - VariantWrapper &operator=(VariantWrapper &&) = default; - template = 1> VariantWrapper(const T &val) : m_variant(val) { diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 6ca304ca7..07a36a415 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -64,7 +64,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * MoveOnlyTest &operator=(const MoveOnlyTest &) = delete; }; - using MyMoveOnlyVariant = Aws::Crt::Variant; + using MyMoveOnlyVariant = Aws::Crt::VariantWrapper; /* Regression test. * AWS_CRT_CPP_API expands into __declspec(dllexport) on Windows platform when dll semantics is enabled. From 3a5bb616fac4fcb345dc30e1e88e2849c088b9c8 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 18:17:50 -0700 Subject: [PATCH 12/30] Replace Variant with VariantWrapper --- include/aws/crt/Variant.h | 4 ++-- tests/VariantTest.cpp | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index b96dfa012..095e29836 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -694,13 +694,13 @@ namespace Aws template = 1> T &emplace(Args &&...args) { - return m_variant.emplace(std::forward(args)...); + return m_variant.template emplace(std::forward(args)...); } template auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & { - return m_variant.emplace(std::forward(args)...); + return m_variant.template emplace(std::forward(args)...); } template = 1> bool holds_alternative() const diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 07a36a415..96f40fd9d 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -22,7 +22,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * Aws::Crt::ApiHandle apiHandle(allocator); { - using MyTestVariant1 = Aws::Crt::Variant; + using MyTestVariant1 = Aws::Crt::VariantWrapper; MyTestVariant1 var1; MyTestVariant1 var1CpyAssigned; var1CpyAssigned = var1; @@ -37,7 +37,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * { // just a different order or types - using MyTestVariant2 = Aws::Crt::Variant; + using MyTestVariant2 = Aws::Crt::VariantWrapper; MyTestVariant2 var2; MyTestVariant2 var2CpyAssigned; var2CpyAssigned = var2; @@ -90,7 +90,7 @@ static int s_VariantConstructor(struct aws_allocator *allocator, void *ctx) Aws::Crt::ApiHandle apiHandle(allocator); { - using VariantIntCharString = Aws::Crt::Variant; + using VariantIntCharString = Aws::Crt::VariantWrapper; VariantIntCharString var1 = Aws::Crt::String(s_variant_test_str); ASSERT_STR_EQUALS(s_variant_test_str, var1.get<2>().c_str()); ASSERT_STR_EQUALS(s_variant_test_str, var1.get().c_str()); @@ -104,7 +104,7 @@ static int s_VariantConstructor(struct aws_allocator *allocator, void *ctx) ASSERT_STR_EQUALS(s_variant_test_str, var1move.get().c_str()); } { - using VariantStringCharInt = Aws::Crt::Variant; + using VariantStringCharInt = Aws::Crt::VariantWrapper; VariantStringCharInt var1{Aws::Crt::InPlaceTypeT(), s_variant_test_str}; ASSERT_STR_EQUALS(s_variant_test_str, var1.get<0>().c_str()); ASSERT_STR_EQUALS(s_variant_test_str, var1.get().c_str()); @@ -146,7 +146,8 @@ static int s_VariantConstructor(struct aws_allocator *allocator, void *ctx) virtual ~MyTestVirtualClassChild() { *m_pStateChild -= 20; } }; - using MyTestVariant = Aws::Crt::Variant; + using MyTestVariant = + Aws::Crt::VariantWrapper; // Test for constructing from one of alternative types with a virtual destructor { int parentState = 0; @@ -211,7 +212,7 @@ static int s_VariantOperatorEquals(struct aws_allocator *allocator, void *ctx) Aws::Crt::ApiHandle apiHandle(allocator); { - using VariantIntCharString = Aws::Crt::Variant; + using VariantIntCharString = Aws::Crt::VariantWrapper; VariantIntCharString var1(int(5)); ASSERT_INT_EQUALS(5, var1.get()); @@ -283,7 +284,7 @@ static int s_VariantEmplace(struct aws_allocator *allocator, void *ctx) Aws::Crt::ApiHandle apiHandle(allocator); { - using VariantIntCharString = Aws::Crt::Variant; + using VariantIntCharString = Aws::Crt::VariantWrapper; VariantIntCharString var1(char('a')); ASSERT_INT_EQUALS('a', var1.get()); @@ -370,7 +371,7 @@ static int s_VariantVisitor(struct aws_allocator *allocator, void *ctx) Aws::Crt::ApiHandle apiHandle(allocator); { - using VariantStringIntChar = Aws::Crt::Variant; + using VariantStringIntChar = Aws::Crt::VariantWrapper; TestVisitor visitor; TestVisitorCustomizedPerType specializedVisitor; From c2c9b8154850d300267ba76caab9d036180bf4a9 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 19:33:02 -0700 Subject: [PATCH 13/30] Use proper naming --- include/aws/crt/Variant.h | 793 +++++++++++++++++++------------------- tests/VariantTest.cpp | 19 +- 2 files changed, 416 insertions(+), 396 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 095e29836..bf0e444ab 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -94,7 +94,7 @@ namespace Aws return std::is_same::value || ContainsType(); } - // a case when the template parameter pack is empty (i.e. Variant<>) + // a case when the template parameter pack is empty (i.e. VariantImpl<>) template constexpr bool ContainsType() { return false; @@ -132,7 +132,7 @@ namespace Aws } // namespace VariantDebug #endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ - /* Depending on the Variant types, this struct either deletes special move members or defaults them. */ + /* Depending on the VariantImpl types, this struct either deletes special move members or defaults them. */ template class MovableVariant; template <> class MovableVariant @@ -154,7 +154,7 @@ namespace Aws MovableVariant &operator=(MovableVariant &&) = delete; }; - /* Depending on the Variant types, this struct either deletes special copy members or defaults them. */ + /* Depending on the VariantImpl types, this struct either deletes special copy members or defaults them. */ template class CopyableVariant; template <> class CopyableVariant @@ -179,491 +179,512 @@ namespace Aws CopyableVariant &operator=(CopyableVariant &&) = default; }; - } // namespace VariantDetail - - template class VariantAlternative; - - /** - * Custom implementation of a Variant type. std::variant requires C++17 - * @tparam Ts types of the variant value - */ - template class Variant - { - private: - template using ThisVariantAlternative = VariantAlternative; + template class VariantAlternative; - template - using EnableIfOtherIsThisVariantAlternative = typename std:: - enable_if::type, Ts...>::value, int>::type; - - public: - using IndexT = VariantDetail::Index::VariantIndex; - static constexpr std::size_t AlternativeCount = sizeof...(Ts); - - static constexpr bool is_copy_constructible = detail::Conjunction...>::value; - static constexpr bool is_copy_assignable = detail::Conjunction...>::value; - static constexpr bool is_move_constructible = detail::Conjunction...>::value; - static constexpr bool is_move_assignable = detail::Conjunction...>::value; - - Variant() + template class VariantImpl { - using FirstAlternative = typename ThisVariantAlternative<0>::type; - new (m_storage) FirstAlternative(); - m_index = 0; - } + private: + template using ThisVariantAlternative = VariantAlternative; - Variant(const Variant &other) - { - AWS_FATAL_ASSERT(other.m_index != -1); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); - } + template + using EnableIfOtherIsThisVariantAlternative = typename std::enable_if< + VariantDetail::Checker::HasType::type, Ts...>::value, + int>::type; - Variant(Variant &&other) - { - AWS_FATAL_ASSERT(other.m_index != -1); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); - } + public: + using IndexT = VariantDetail::Index::VariantIndex; + static constexpr std::size_t AlternativeCount = sizeof...(Ts); - template = 1> Variant(const T &val) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); - - using PlainT = typename std::decay::type; - new (m_storage) PlainT(val); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - } + static constexpr bool is_copy_constructible = + detail::Conjunction...>::value; + static constexpr bool is_copy_assignable = detail::Conjunction...>::value; + static constexpr bool is_move_constructible = + detail::Conjunction...>::value; + static constexpr bool is_move_assignable = detail::Conjunction...>::value; - template = 1> Variant(T &&val) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); - - using PlainT = typename std::decay::type; - new (m_storage) PlainT(std::forward(val)); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - } - - // An overload to initialize with an Alternative T in-place - template explicit Variant(Aws::Crt::InPlaceTypeT, Args &&...args) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); - - using PlainT = typename std::decay::type; - new (m_storage) PlainT(std::forward(args)...); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - } + VariantImpl() + { + using FirstAlternative = typename ThisVariantAlternative<0>::type; + new (m_storage) FirstAlternative(); + m_index = 0; + } - Variant &operator=(const Variant &other) - { - if (this != &other) + VariantImpl(const VariantImpl &other) { AWS_FATAL_ASSERT(other.m_index != -1); - if (m_index != other.m_index) - { - Destroy(); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); - } - else - { - VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner()); - } + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); } - return *this; - } - Variant &operator=(Variant &&other) - { - if (this != &other) + VariantImpl(VariantImpl &&other) { AWS_FATAL_ASSERT(other.m_index != -1); - if (m_index != other.m_index) - { - Destroy(); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); - } - else - { - VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveAssigner()); - }; + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); } - return *this; - } - /* emplace */ - template = 1> - T &emplace(Args &&...args) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); - - Destroy(); - - using PlainT = typename std::decay::type; - new (m_storage) PlainT(std::forward(args)...); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - - T *value = reinterpret_cast(m_storage); - return *value; - } - - template - auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & - { - static_assert(Index < AlternativeCount, "Unknown alternative index to emplace"); - using AlternativeT = typename ThisVariantAlternative::type; + template = 1> VariantImpl(const T &val) + { + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a VariantImpl with a type bigger than all alternatives."); - return emplace(std::forward(args)...); - } + using PlainT = typename std::decay::type; + new (m_storage) PlainT(val); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); + } - template = 1> bool holds_alternative() const - { - AWS_ASSERT(m_index != -1); - return m_index == VariantDetail::Index::GetIndexOf(); - } + template = 1> VariantImpl(T &&val) + { + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a VariantImpl with a type bigger than all alternatives."); - /* non-const get */ - template = 1> T &get() - { - AWS_FATAL_ASSERT(holds_alternative()); - T *value = reinterpret_cast(m_storage); - return *value; - } + using PlainT = typename std::decay::type; + new (m_storage) PlainT(std::forward(val)); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); + } - template = 1> T *get_if() - { - if (holds_alternative()) + // An overload to initialize with an Alternative T in-place + template explicit VariantImpl(Aws::Crt::InPlaceTypeT, Args &&...args) { - T *value = reinterpret_cast(m_storage); - return value; + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a VariantImpl with a type bigger than all alternatives."); + + using PlainT = typename std::decay::type; + new (m_storage) PlainT(std::forward(args)...); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); } - else + + VariantImpl &operator=(const VariantImpl &other) { - return nullptr; + if (this != &other) + { + AWS_FATAL_ASSERT(other.m_index != -1); + if (m_index != other.m_index) + { + Destroy(); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); + } + else + { + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner()); + } + } + return *this; } - } - template auto get() -> typename ThisVariantAlternative::type & - { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - AWS_FATAL_ASSERT(holds_alternative()); - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *ret = reinterpret_cast(m_storage); - return *ret; - } - - /* const get */ - template = 1> const T &get() const - { - AWS_FATAL_ASSERT(holds_alternative()); - const T *value = reinterpret_cast(m_storage); - return *value; - } - - template = 1> const T *get_if() const - { - if (holds_alternative()) + VariantImpl &operator=(VariantImpl &&other) { - T *value = reinterpret_cast(m_storage); - return value; + if (this != &other) + { + AWS_FATAL_ASSERT(other.m_index != -1); + if (m_index != other.m_index) + { + Destroy(); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); + } + else + { + VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveAssigner()); + }; + } + return *this; } - else + + /* emplace */ + template = 1> + T &emplace(Args &&...args) { - return nullptr; - } - } + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a VariantImpl with a type bigger than all alternatives."); - template auto get() const -> const typename ThisVariantAlternative::type & - { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - AWS_ASSERT(Index == m_index); - using AlternativeT = typename ThisVariantAlternative::type; - const AlternativeT *ret = reinterpret_cast(m_storage); - return *ret; - } + Destroy(); - /* This is just a templated way to say - * "int*" for - * a VariantAlternative<0, Variant()>*/ - template - using RawAlternativePointerT = - typename std::add_pointer::type>::type; + using PlainT = typename std::decay::type; + new (m_storage) PlainT(std::forward(args)...); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); - template auto get_if() -> RawAlternativePointerT - { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - if (holds_alternative()) - { - using AlternativePtrT = RawAlternativePointerT; - AlternativePtrT value = reinterpret_cast(m_storage); - return value; + T *value = reinterpret_cast(m_storage); + return *value; } - else + + template + auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & { - return nullptr; - } - } + static_assert(Index < AlternativeCount, "Unknown alternative index to emplace"); + using AlternativeT = typename ThisVariantAlternative::type; - template - using ConstRawAlternativePointerT = typename std::add_pointer< - typename std::add_const::type>::type>::type; + return emplace(std::forward(args)...); + } - template auto get_if() const -> ConstRawAlternativePointerT - { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - if (holds_alternative()) + template = 1> bool holds_alternative() const { - using AlternativePtrT = ConstRawAlternativePointerT; - AlternativePtrT value = reinterpret_cast(m_storage); - return value; + AWS_ASSERT(m_index != -1); + return m_index == VariantDetail::Index::GetIndexOf(); } - else + + /* non-const get */ + template = 1> T &get() { - return nullptr; + AWS_FATAL_ASSERT(holds_alternative()); + T *value = reinterpret_cast(m_storage); + return *value; } - } - - std::size_t index() const { return m_index; } - - ~Variant() { Destroy(); } - template void Visit(VisitorT &&visitor) - { - return VisitorUtil<0, Ts...>::Visit(this, std::forward(visitor)); - } - - private: - static constexpr std::size_t STORAGE_SIZE = VariantDetail::ParameterPackSize::GetMaxSizeOf(); - - alignas(VariantDetail::ParameterPackSize::AlignAsPack()) char m_storage[STORAGE_SIZE]; - IndexT m_index = -1; -#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG) - VariantDetail::VariantDebug::VariantDebugBrowser browser = m_storage; -#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ - - template constexpr bool holds_alternative() const { return Index == m_index; } - - struct Destroyer - { - template void operator()(AlternativeT &&value) const + template = 1> T *get_if() { - (void)value; - using PlaintT = typename std::remove_reference::type; - value.~PlaintT(); + if (holds_alternative()) + { + T *value = reinterpret_cast(m_storage); + return value; + } + else + { + return nullptr; + } } - }; - void Destroy() - { - AWS_FATAL_ASSERT(m_index != -1); - Visit(Destroyer()); - - m_index = -1; - } - - struct CopyMoveConstructor - { - template void operator()(AlternativeT &&value, AlternativeT &&other) const + template auto get() -> typename ThisVariantAlternative::type & { - using PlaintT = typename std::remove_reference::type; - new (&value) PlaintT(std::move(other)); + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + AWS_FATAL_ASSERT(holds_alternative()); + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *ret = reinterpret_cast(m_storage); + return *ret; } - template - void operator()(AlternativeT &&value, ConstAlternativeT &other) const + /* const get */ + template = 1> const T &get() const { - using PlaintT = typename std::remove_reference::type; - using PlaintOtherT = - typename std::remove_const::type>::type; - static_assert(std::is_same::value, "Incompatible types"); - - new (&value) PlaintT(other); + AWS_FATAL_ASSERT(holds_alternative()); + const T *value = reinterpret_cast(m_storage); + return *value; } - }; - struct CopyMoveAssigner - { - template void operator()(AlternativeT &&value, AlternativeT &&other) const + template = 1> const T *get_if() const { - value = std::move(other); + if (holds_alternative()) + { + T *value = reinterpret_cast(m_storage); + return value; + } + else + { + return nullptr; + } } - template - void operator()(AlternativeT &&value, ConstAlternativeT &other) const + template auto get() const -> const typename ThisVariantAlternative::type & { - using PlaintT = typename std::remove_reference::type; - using PlaintOtherT = - typename std::remove_const::type>::type; - static_assert(std::is_same::value, "Incompatible types"); - - value = other; + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + AWS_ASSERT(Index == m_index); + using AlternativeT = typename ThisVariantAlternative::type; + const AlternativeT *ret = reinterpret_cast(m_storage); + return *ret; } - }; - template struct VisitorUtil; + /* This is just a templated way to say + * "int*" for + * a VariantAlternative<0, VariantImpl()>*/ + template + using RawAlternativePointerT = + typename std::add_pointer::type>::type; - template - struct VisitorUtil - { - template static void Visit(Variant *pThis, VisitorStruct &&visitor) + template auto get_if() -> RawAlternativePointerT { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - - if (Index == pThis->m_index) + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + if (holds_alternative()) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value); + using AlternativePtrT = RawAlternativePointerT; + AlternativePtrT value = reinterpret_cast(m_storage); + return value; } else { - VisitorUtil(Index + 1), Second, Rest...>::Visit( - pThis, std::forward(visitor)); + return nullptr; } } - template - static void VisitBinary(Variant *pThis, Variant &&other, VisitorStruct &&visitor) - { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + template + using ConstRawAlternativePointerT = typename std::add_pointer< + typename std::add_const::type>::type>::type; - if (Index == pThis->m_index) + template auto get_if() const -> ConstRawAlternativePointerT + { + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + if (holds_alternative()) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value, other.get()); + using AlternativePtrT = ConstRawAlternativePointerT; + AlternativePtrT value = reinterpret_cast(m_storage); + return value; } else { - VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( - pThis, std::forward>(other), std::forward(visitor)); + return nullptr; } } - template - static void VisitBinary(Variant *pThis, const Variant &other, VisitorStruct &&visitor) + std::size_t index() const { return m_index; } + + ~VariantImpl() { Destroy(); } + + template void Visit(VisitorT &&visitor) { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + return VisitorUtil<0, Ts...>::Visit(this, std::forward(visitor)); + } - if (Index == pThis->m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - const AlternativeT &otherValue = other.get(); - visitor(*value, otherValue); - } - else + private: + static constexpr std::size_t STORAGE_SIZE = VariantDetail::ParameterPackSize::GetMaxSizeOf(); + + alignas(VariantDetail::ParameterPackSize::AlignAsPack()) char m_storage[STORAGE_SIZE]; + IndexT m_index = -1; +#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG) + VariantDetail::VariantDebug::VariantDebugBrowser browser = m_storage; +#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ + + template constexpr bool holds_alternative() const { return Index == m_index; } + + struct Destroyer + { + template void operator()(AlternativeT &&value) const { - VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( - pThis, other, std::forward(visitor)); + (void)value; + using PlaintT = typename std::remove_reference::type; + value.~PlaintT(); } + }; + + void Destroy() + { + AWS_FATAL_ASSERT(m_index != -1); + Visit(Destroyer()); + + m_index = -1; } - }; - template struct VisitorUtil - { - template static void Visit(Variant *pThis, VisitorStruct &&visitor) + struct CopyMoveConstructor { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + template void operator()(AlternativeT &&value, AlternativeT &&other) const + { + using PlaintT = typename std::remove_reference::type; + new (&value) PlaintT(std::move(other)); + } - if (Index == pThis->m_index) + template + void operator()(AlternativeT &&value, ConstAlternativeT &other) const { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value); + using PlaintT = typename std::remove_reference::type; + using PlaintOtherT = + typename std::remove_const::type>::type; + static_assert(std::is_same::value, "Incompatible types"); + + new (&value) PlaintT(other); } - else + }; + + struct CopyMoveAssigner + { + template void operator()(AlternativeT &&value, AlternativeT &&other) const { - AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + value = std::move(other); } - } - template - static void VisitBinary(Variant *pThis, Variant &&other, VisitorStruct &&visitor) + template + void operator()(AlternativeT &&value, ConstAlternativeT &other) const + { + using PlaintT = typename std::remove_reference::type; + using PlaintOtherT = + typename std::remove_const::type>::type; + static_assert(std::is_same::value, "Incompatible types"); + + value = other; + } + }; + + template struct VisitorUtil; + + template + struct VisitorUtil { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + template static void Visit(VariantImpl *pThis, VisitorStruct &&visitor) + { + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value); + } + else + { + VisitorUtil(Index + 1), Second, Rest...>::Visit( + pThis, std::forward(visitor)); + } + } - if (Index == pThis->m_index) + template + static void VisitBinary( + VariantImpl *pThis, + VariantImpl &&other, + VisitorStruct &&visitor) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value, other.get()); + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value, other.get()); + } + else + { + VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( + pThis, std::forward>(other), std::forward(visitor)); + } } - else + + template + static void VisitBinary( + VariantImpl *pThis, + const VariantImpl &other, + VisitorStruct &&visitor) { - AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + const AlternativeT &otherValue = other.get(); + visitor(*value, otherValue); + } + else + { + VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( + pThis, other, std::forward(visitor)); + } } - } + }; - template - static void VisitBinary(Variant *pThis, const Variant &other, VisitorStruct &&visitor) + template struct VisitorUtil { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + template static void Visit(VariantImpl *pThis, VisitorStruct &&visitor) + { + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value); + } + else + { + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + } + } - if (Index == pThis->m_index) + template + static void VisitBinary( + VariantImpl *pThis, + VariantImpl &&other, + VisitorStruct &&visitor) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - const AlternativeT &otherValue = other.get(); - visitor(*value, otherValue); + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value, other.get()); + } + else + { + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + } } - else + + template + static void VisitBinary( + VariantImpl *pThis, + const VariantImpl &other, + VisitorStruct &&visitor) { - AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + const AlternativeT &otherValue = other.get(); + visitor(*value, otherValue); + } + else + { + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + } } - } + }; }; - }; - /* Helper template to get an actual type from an Index */ - template class VariantAlternative - { - public: - // uses std::tuple as a helper struct to provide index-based access of a parameter pack - using type = typename std::tuple_element>::type; + /* Helper template to get an actual type from an Index */ + template class VariantAlternative + { + public: + // uses std::tuple as a helper struct to provide index-based access of a parameter pack + using type = typename std::tuple_element>::type; - VariantAlternative(const Variant &) {} + VariantAlternative(const VariantImpl &) {} - VariantAlternative(const Variant *) {} - }; + VariantAlternative(const VariantImpl *) {} + }; - template class VariantSize - { - constexpr static const std::size_t Value = T::AlternativeCount; - }; + template class VariantSize + { + constexpr static const std::size_t Value = T::AlternativeCount; + }; + } // namespace VariantDetail + + /** + * Custom implementation of a Variant type. std::variant requires C++17 + * @tparam Ts types of the variant value + * + * @details Copyability and Movability depend only on constructors (copy and move correspondingly) of the + * underlying types. This means that a class with move constructor but with no move assignment operator might + * cause compilation issues. If such a type ever needs to be supported, two additional base classes need to be + * introduced: CopyAssignable and MoveAssignable. For now, it'll be overengineering. The same applies to copy + * constructor and copy assignment operator. + */ template - class VariantWrapper + class Variant : public VariantDetail::MovableVariant...>::value>, public VariantDetail::CopyableVariant...>::value> { private: - template using ThisVariantAlternative = VariantAlternative; + template using ThisVariantAlternative = VariantDetail::VariantAlternative; template using EnableIfOtherIsThisVariantAlternative = typename std:: @@ -673,20 +694,20 @@ namespace Aws using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); - VariantWrapper() = default; + // TODO Check NoDefaultConstructible. + Variant() = default; - template = 1> - VariantWrapper(const T &val) : m_variant(val) + template = 1> Variant(const T &val) : m_variant(val) { } template = 1> - VariantWrapper(T &&val) : m_variant(std::forward(val)) + Variant(T &&val) : m_variant(std::forward(val)) { } template - explicit VariantWrapper(Aws::Crt::InPlaceTypeT ipt, Args &&...args) + explicit Variant(Aws::Crt::InPlaceTypeT ipt, Args &&...args) : m_variant(ipt, std::forward(args)...) { } @@ -764,7 +785,7 @@ namespace Aws } private: - Variant m_variant; + VariantDetail::VariantImpl m_variant; }; } // namespace Crt diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 96f40fd9d..6ca304ca7 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -22,7 +22,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * Aws::Crt::ApiHandle apiHandle(allocator); { - using MyTestVariant1 = Aws::Crt::VariantWrapper; + using MyTestVariant1 = Aws::Crt::Variant; MyTestVariant1 var1; MyTestVariant1 var1CpyAssigned; var1CpyAssigned = var1; @@ -37,7 +37,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * { // just a different order or types - using MyTestVariant2 = Aws::Crt::VariantWrapper; + using MyTestVariant2 = Aws::Crt::Variant; MyTestVariant2 var2; MyTestVariant2 var2CpyAssigned; var2CpyAssigned = var2; @@ -64,7 +64,7 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * MoveOnlyTest &operator=(const MoveOnlyTest &) = delete; }; - using MyMoveOnlyVariant = Aws::Crt::VariantWrapper; + using MyMoveOnlyVariant = Aws::Crt::Variant; /* Regression test. * AWS_CRT_CPP_API expands into __declspec(dllexport) on Windows platform when dll semantics is enabled. @@ -90,7 +90,7 @@ static int s_VariantConstructor(struct aws_allocator *allocator, void *ctx) Aws::Crt::ApiHandle apiHandle(allocator); { - using VariantIntCharString = Aws::Crt::VariantWrapper; + using VariantIntCharString = Aws::Crt::Variant; VariantIntCharString var1 = Aws::Crt::String(s_variant_test_str); ASSERT_STR_EQUALS(s_variant_test_str, var1.get<2>().c_str()); ASSERT_STR_EQUALS(s_variant_test_str, var1.get().c_str()); @@ -104,7 +104,7 @@ static int s_VariantConstructor(struct aws_allocator *allocator, void *ctx) ASSERT_STR_EQUALS(s_variant_test_str, var1move.get().c_str()); } { - using VariantStringCharInt = Aws::Crt::VariantWrapper; + using VariantStringCharInt = Aws::Crt::Variant; VariantStringCharInt var1{Aws::Crt::InPlaceTypeT(), s_variant_test_str}; ASSERT_STR_EQUALS(s_variant_test_str, var1.get<0>().c_str()); ASSERT_STR_EQUALS(s_variant_test_str, var1.get().c_str()); @@ -146,8 +146,7 @@ static int s_VariantConstructor(struct aws_allocator *allocator, void *ctx) virtual ~MyTestVirtualClassChild() { *m_pStateChild -= 20; } }; - using MyTestVariant = - Aws::Crt::VariantWrapper; + using MyTestVariant = Aws::Crt::Variant; // Test for constructing from one of alternative types with a virtual destructor { int parentState = 0; @@ -212,7 +211,7 @@ static int s_VariantOperatorEquals(struct aws_allocator *allocator, void *ctx) Aws::Crt::ApiHandle apiHandle(allocator); { - using VariantIntCharString = Aws::Crt::VariantWrapper; + using VariantIntCharString = Aws::Crt::Variant; VariantIntCharString var1(int(5)); ASSERT_INT_EQUALS(5, var1.get()); @@ -284,7 +283,7 @@ static int s_VariantEmplace(struct aws_allocator *allocator, void *ctx) Aws::Crt::ApiHandle apiHandle(allocator); { - using VariantIntCharString = Aws::Crt::VariantWrapper; + using VariantIntCharString = Aws::Crt::Variant; VariantIntCharString var1(char('a')); ASSERT_INT_EQUALS('a', var1.get()); @@ -371,7 +370,7 @@ static int s_VariantVisitor(struct aws_allocator *allocator, void *ctx) Aws::Crt::ApiHandle apiHandle(allocator); { - using VariantStringIntChar = Aws::Crt::VariantWrapper; + using VariantStringIntChar = Aws::Crt::Variant; TestVisitor visitor; TestVisitorCustomizedPerType specializedVisitor; From 8995ecb27623368123bf2beff6eb5abca1143a34 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 21:11:28 -0700 Subject: [PATCH 14/30] Cleaning up --- include/aws/crt/Variant.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index bf0e444ab..c6c905ad9 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -132,7 +132,7 @@ namespace Aws } // namespace VariantDebug #endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ - /* Depending on the VariantImpl types, this struct either deletes special move members or defaults them. */ + /* Depending on the Variant types, this struct either deletes special move members or defaults them. */ template class MovableVariant; template <> class MovableVariant @@ -154,7 +154,7 @@ namespace Aws MovableVariant &operator=(MovableVariant &&) = delete; }; - /* Depending on the VariantImpl types, this struct either deletes special copy members or defaults them. */ + /* Depending on the Variant types, this struct either deletes special copy members or defaults them. */ template class CopyableVariant; template <> class CopyableVariant @@ -230,7 +230,7 @@ namespace Aws "This variant does not have such alternative T."); static_assert( sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a VariantImpl with a type bigger than all alternatives."); + "Attempting to instantiate a Variant with a type bigger than all alternatives."); using PlainT = typename std::decay::type; new (m_storage) PlainT(val); @@ -245,7 +245,7 @@ namespace Aws "This variant does not have such alternative T."); static_assert( sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a VariantImpl with a type bigger than all alternatives."); + "Attempting to instantiate a Variant with a type bigger than all alternatives."); using PlainT = typename std::decay::type; new (m_storage) PlainT(std::forward(val)); @@ -261,7 +261,7 @@ namespace Aws "This variant does not have such alternative T."); static_assert( sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a VariantImpl with a type bigger than all alternatives."); + "Attempting to instantiate a Variant with a type bigger than all alternatives."); using PlainT = typename std::decay::type; new (m_storage) PlainT(std::forward(args)...); @@ -316,7 +316,7 @@ namespace Aws "This variant does not have such alternative T."); static_assert( sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a VariantImpl with a type bigger than all alternatives."); + "Attempting to instantiate a Variant with a type bigger than all alternatives."); Destroy(); @@ -669,8 +669,8 @@ namespace Aws } // namespace VariantDetail /** - * Custom implementation of a Variant type. std::variant requires C++17 - * @tparam Ts types of the variant value + * Custom implementation of a Variant type. std::variant requires C++17. + * @tparam Ts Types of the variant value. * * @details Copyability and Movability depend only on constructors (copy and move correspondingly) of the * underlying types. This means that a class with move constructor but with no move assignment operator might From ee70c0eac850f8c0138aa49ce90926de0136921a Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 21:33:33 -0700 Subject: [PATCH 15/30] Rearrange tests --- include/aws/crt/Variant.h | 12 +++--- tests/CMakeLists.txt | 3 +- tests/VariantTest.cpp | 80 +++++++++++++++++++++------------------ 3 files changed, 52 insertions(+), 43 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index c6c905ad9..0ba82289b 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -671,18 +671,18 @@ namespace Aws /** * Custom implementation of a Variant type. std::variant requires C++17. * @tparam Ts Types of the variant value. - * - * @details Copyability and Movability depend only on constructors (copy and move correspondingly) of the - * underlying types. This means that a class with move constructor but with no move assignment operator might - * cause compilation issues. If such a type ever needs to be supported, two additional base classes need to be - * introduced: CopyAssignable and MoveAssignable. For now, it'll be overengineering. The same applies to copy - * constructor and copy assignment operator. */ template class Variant : public VariantDetail::MovableVariant...>::value>, public VariantDetail::CopyableVariant...>::value> { + /* Copyability and Movability depend only on constructors (copy and move correspondingly) of the + * underlying types. This means that a class with move constructor but with no move assignment operator + * might cause compilation issues. If such a type ever needs to be supported, two additional base classes + * need to be introduced: CopyAssignable and MoveAssignable. For now, it'll be overengineering. The same + * applies to copy constructor and copy assignment operator. */ + private: template using ThisVariantAlternative = VariantDetail::VariantAlternative; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index df86e33de..62011a0f7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -105,7 +105,8 @@ add_test_case(OptionalEmplace) add_test_case(OptionalCopyAndMoveSemantics) add_test_case(VariantCompiles) add_test_case(VariantConstructor) -add_test_case(VariantOperatorEquals) +add_test_case(VariantAssignmentOperator) +add_test_case(VariantWithMoveOnlyUnderlyingType) add_test_case(VariantEmplace) add_test_case(VariantVisitor) add_test_case(StreamTestCreateDestroyWrapper) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 6ca304ca7..0644b645c 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -8,13 +8,6 @@ const char *s_variant_test_str = "This is a string, that should be long enough to avoid small string optimizations"; -// TODO ? Check for AWS_CRT_USE_WINDOWS_DLL_SEMANTICS -#if defined(WIN32) -# define AWS_VARIANTTEST_API __declspec(dllexport) -#else -# define AWS_VARIANTTEST_API -#endif - static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -49,33 +42,6 @@ static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void * var2CpyAssigned = var2a; MyTestVariant2 var2aCpyConstructedVariant(var2aCpyAssigned); } - - { - // test with a move-only type - - struct MoveOnlyTest - { - MoveOnlyTest() = default; - - MoveOnlyTest(MoveOnlyTest &&) = default; - MoveOnlyTest &operator=(MoveOnlyTest &&) = default; - - MoveOnlyTest(const MoveOnlyTest &) = delete; - MoveOnlyTest &operator=(const MoveOnlyTest &) = delete; - }; - - using MyMoveOnlyVariant = Aws::Crt::Variant; - - /* Regression test. - * AWS_CRT_CPP_API expands into __declspec(dllexport) on Windows platform when dll semantics is enabled. - * __declspec(dllexport) causes compiler to generate all special members of the marked class. - * These generated special members should be valid. */ - class AWS_VARIANTTEST_API FailVariantTestResult - { - public: - MyMoveOnlyVariant m_result; - }; - } } return AWS_OP_SUCCESS; @@ -204,7 +170,7 @@ static int s_VariantConstructor(struct aws_allocator *allocator, void *ctx) AWS_TEST_CASE(VariantConstructor, s_VariantConstructor) -static int s_VariantOperatorEquals(struct aws_allocator *allocator, void *ctx) +static int s_VariantAssignmentOperator(struct aws_allocator *allocator, void *ctx) { (void)ctx; { @@ -248,7 +214,49 @@ static int s_VariantOperatorEquals(struct aws_allocator *allocator, void *ctx) return AWS_OP_SUCCESS; } -AWS_TEST_CASE(VariantOperatorEquals, s_VariantOperatorEquals) +AWS_TEST_CASE(VariantAssignmentOperator, s_VariantAssignmentOperator) + +static int s_VariantWithMoveOnlyUnderlyingType(struct aws_allocator *allocator, void *ctx) +{ + (void)ctx; + + Aws::Crt::ApiHandle apiHandle(allocator); + + // test with a move-only type + + struct MoveOnlyTestType + { + MoveOnlyTestType() = default; + + MoveOnlyTestType(MoveOnlyTestType &&) = default; + MoveOnlyTestType &operator=(MoveOnlyTestType &&) = default; + + MoveOnlyTestType(const MoveOnlyTestType &) = delete; + MoveOnlyTestType &operator=(const MoveOnlyTestType &) = delete; + }; + + using MoveOnlyVariant = Aws::Crt::Variant; + + /* Regression test. + * The __declspec(dllexport) directive exports class member function on Windows platform. We enable it when + * building shared libraries. In the past, this directive caused msvc to generate special copy members for classes + * containing Crt::Variant with move-only underlying types, which led to compile-time errors. */ + +#if defined(_WIN32) +# define AWS_VARIANTTEST_WINDOWS_API __declspec(dllexport) +#else +# define AWS_VARIANTTEST_WINDOWS_API +#endif + + struct AWS_VARIANTTEST_WINDOWS_API FailVariantTestResult + { + MoveOnlyVariant m_result; + }; + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(VariantWithMoveOnlyUnderlyingType, s_VariantWithMoveOnlyUnderlyingType) struct TestStringOnlyVisitor { From 67bc43e81894f9a4b23c6cbcd8ff63e247fe5722 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 21:34:23 -0700 Subject: [PATCH 16/30] Revert Variant.h to ensure new test fails --- include/aws/crt/Variant.h | 924 +++++++++++++++----------------------- 1 file changed, 362 insertions(+), 562 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 0ba82289b..1ac603fb8 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -15,23 +15,8 @@ namespace Aws { namespace Crt { - namespace detail - { - template struct Conjunction : std::true_type - { - }; - - template - struct Conjunction - : std::conditional, std::false_type>::type - { - }; - - } // namespace detail - namespace VariantDetail { - // TODO Pass values instead of refs. template constexpr const T &ConstExprMax(const T &a, const T &b) { return (a < b) ? b : a; @@ -94,7 +79,7 @@ namespace Aws return std::is_same::value || ContainsType(); } - // a case when the template parameter pack is empty (i.e. VariantImpl<>) + // a case when the template parameter pack is empty (i.e. Variant<>) template constexpr bool ContainsType() { return false; @@ -131,662 +116,477 @@ namespace Aws }; } // namespace VariantDebug #endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ + } // namespace VariantDetail - /* Depending on the Variant types, this struct either deletes special move members or defaults them. */ - template class MovableVariant; + template class VariantAlternative; - template <> class MovableVariant - { - public: - MovableVariant() = default; - MovableVariant(const MovableVariant &) = default; - MovableVariant &operator=(const MovableVariant &) = default; - MovableVariant(MovableVariant &&other) = default; - MovableVariant &operator=(MovableVariant &&other) = default; - }; - template <> class MovableVariant - { - public: - MovableVariant() = default; - MovableVariant(const MovableVariant &) = default; - MovableVariant &operator=(const MovableVariant &) = default; - MovableVariant(MovableVariant &&) = delete; - MovableVariant &operator=(MovableVariant &&) = delete; - }; + /** + * Custom implementation of a Variant type. std::variant requires C++17 + * @tparam Ts types of the variant value + */ + template class Variant + { + private: + template using ThisVariantAlternative = VariantAlternative; - /* Depending on the Variant types, this struct either deletes special copy members or defaults them. */ - template class CopyableVariant; + template + using EnableIfOtherIsThisVariantAlternative = typename std:: + enable_if::type, Ts...>::value, int>::type; + + public: + using IndexT = VariantDetail::Index::VariantIndex; + static constexpr std::size_t AlternativeCount = sizeof...(Ts); - template <> class CopyableVariant + Variant() { - public: - CopyableVariant() = default; - CopyableVariant(const CopyableVariant &other) = default; - CopyableVariant &operator=(const CopyableVariant &other) = default; - CopyableVariant(CopyableVariant &&) = default; - CopyableVariant &operator=(CopyableVariant &&) = default; - }; + using FirstAlternative = typename ThisVariantAlternative<0>::type; + new (m_storage) FirstAlternative(); + m_index = 0; + } - template <> class CopyableVariant + Variant(const Variant &other) { - public: - CopyableVariant() = default; + AWS_FATAL_ASSERT(other.m_index != -1); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); + } - CopyableVariant(const CopyableVariant &) = delete; - CopyableVariant &operator=(const CopyableVariant &) = delete; + Variant(Variant &&other) + { + AWS_FATAL_ASSERT(other.m_index != -1); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); + } - CopyableVariant(CopyableVariant &&) = default; - CopyableVariant &operator=(CopyableVariant &&) = default; - }; + template = 1> Variant(const T &val) + { + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a Variant with a type bigger than all alternatives."); + + using PlainT = typename std::decay::type; + new (m_storage) PlainT(val); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); + } - template class VariantAlternative; + template = 1> Variant(T &&val) + { + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a Variant with a type bigger than all alternatives."); + + using PlainT = typename std::decay::type; + new (m_storage) PlainT(std::forward(val)); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); + } - template class VariantImpl + // An overload to initialize with an Alternative T in-place + template explicit Variant(Aws::Crt::InPlaceTypeT, Args &&...args) { - private: - template using ThisVariantAlternative = VariantAlternative; - - template - using EnableIfOtherIsThisVariantAlternative = typename std::enable_if< - VariantDetail::Checker::HasType::type, Ts...>::value, - int>::type; - - public: - using IndexT = VariantDetail::Index::VariantIndex; - static constexpr std::size_t AlternativeCount = sizeof...(Ts); - - static constexpr bool is_copy_constructible = - detail::Conjunction...>::value; - static constexpr bool is_copy_assignable = detail::Conjunction...>::value; - static constexpr bool is_move_constructible = - detail::Conjunction...>::value; - static constexpr bool is_move_assignable = detail::Conjunction...>::value; - - VariantImpl() - { - using FirstAlternative = typename ThisVariantAlternative<0>::type; - new (m_storage) FirstAlternative(); - m_index = 0; - } + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a Variant with a type bigger than all alternatives."); + + using PlainT = typename std::decay::type; + new (m_storage) PlainT(std::forward(args)...); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); + } - VariantImpl(const VariantImpl &other) + Variant &operator=(const Variant &other) + { + if (this != &other) { AWS_FATAL_ASSERT(other.m_index != -1); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); + if (m_index != other.m_index) + { + Destroy(); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); + } + else + { + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner()); + } } + return *this; + } - VariantImpl(VariantImpl &&other) + Variant &operator=(Variant &&other) + { + if (this != &other) { AWS_FATAL_ASSERT(other.m_index != -1); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); + if (m_index != other.m_index) + { + Destroy(); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); + } + else + { + VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveAssigner()); + }; } + return *this; + } - template = 1> VariantImpl(const T &val) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); + /* emplace */ + template = 1> + T &emplace(Args &&...args) + { + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a Variant with a type bigger than all alternatives."); + + Destroy(); + + using PlainT = typename std::decay::type; + new (m_storage) PlainT(std::forward(args)...); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); + + T *value = reinterpret_cast(m_storage); + return *value; + } - using PlainT = typename std::decay::type; - new (m_storage) PlainT(val); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - } + template + auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & + { + static_assert(Index < AlternativeCount, "Unknown alternative index to emplace"); + using AlternativeT = typename ThisVariantAlternative::type; - template = 1> VariantImpl(T &&val) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); + return emplace(std::forward(args)...); + } - using PlainT = typename std::decay::type; - new (m_storage) PlainT(std::forward(val)); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - } + template = 1> bool holds_alternative() const + { + AWS_ASSERT(m_index != -1); + return m_index == VariantDetail::Index::GetIndexOf(); + } - // An overload to initialize with an Alternative T in-place - template explicit VariantImpl(Aws::Crt::InPlaceTypeT, Args &&...args) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); + /* non-const get */ + template = 1> T &get() + { + AWS_FATAL_ASSERT(holds_alternative()); + T *value = reinterpret_cast(m_storage); + return *value; + } - using PlainT = typename std::decay::type; - new (m_storage) PlainT(std::forward(args)...); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); + template = 1> T *get_if() + { + if (holds_alternative()) + { + T *value = reinterpret_cast(m_storage); + return value; } - - VariantImpl &operator=(const VariantImpl &other) + else { - if (this != &other) - { - AWS_FATAL_ASSERT(other.m_index != -1); - if (m_index != other.m_index) - { - Destroy(); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); - } - else - { - VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner()); - } - } - return *this; + return nullptr; } + } + + template auto get() -> typename ThisVariantAlternative::type & + { + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + AWS_FATAL_ASSERT(holds_alternative()); + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *ret = reinterpret_cast(m_storage); + return *ret; + } + + /* const get */ + template = 1> const T &get() const + { + AWS_FATAL_ASSERT(holds_alternative()); + const T *value = reinterpret_cast(m_storage); + return *value; + } - VariantImpl &operator=(VariantImpl &&other) + template = 1> const T *get_if() const + { + if (holds_alternative()) { - if (this != &other) - { - AWS_FATAL_ASSERT(other.m_index != -1); - if (m_index != other.m_index) - { - Destroy(); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); - } - else - { - VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveAssigner()); - }; - } - return *this; + T *value = reinterpret_cast(m_storage); + return value; } - - /* emplace */ - template = 1> - T &emplace(Args &&...args) + else { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); + return nullptr; + } + } - Destroy(); + template auto get() const -> const typename ThisVariantAlternative::type & + { + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + AWS_ASSERT(Index == m_index); + using AlternativeT = typename ThisVariantAlternative::type; + const AlternativeT *ret = reinterpret_cast(m_storage); + return *ret; + } - using PlainT = typename std::decay::type; - new (m_storage) PlainT(std::forward(args)...); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); + /* This is just a templated way to say + * "int*" for + * a VariantAlternative<0, Variant()>*/ + template + using RawAlternativePointerT = + typename std::add_pointer::type>::type; - T *value = reinterpret_cast(m_storage); - return *value; + template auto get_if() -> RawAlternativePointerT + { + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + if (holds_alternative()) + { + using AlternativePtrT = RawAlternativePointerT; + AlternativePtrT value = reinterpret_cast(m_storage); + return value; } - - template - auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & + else { - static_assert(Index < AlternativeCount, "Unknown alternative index to emplace"); - using AlternativeT = typename ThisVariantAlternative::type; - - return emplace(std::forward(args)...); + return nullptr; } + } + + template + using ConstRawAlternativePointerT = typename std::add_pointer< + typename std::add_const::type>::type>::type; - template = 1> bool holds_alternative() const + template auto get_if() const -> ConstRawAlternativePointerT + { + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + if (holds_alternative()) { - AWS_ASSERT(m_index != -1); - return m_index == VariantDetail::Index::GetIndexOf(); + using AlternativePtrT = ConstRawAlternativePointerT; + AlternativePtrT value = reinterpret_cast(m_storage); + return value; } - - /* non-const get */ - template = 1> T &get() + else { - AWS_FATAL_ASSERT(holds_alternative()); - T *value = reinterpret_cast(m_storage); - return *value; + return nullptr; } + } + + std::size_t index() const { return m_index; } + + ~Variant() { Destroy(); } + + template void Visit(VisitorT &&visitor) + { + return VisitorUtil<0, Ts...>::Visit(this, std::forward(visitor)); + } - template = 1> T *get_if() + private: + static constexpr std::size_t STORAGE_SIZE = VariantDetail::ParameterPackSize::GetMaxSizeOf(); + + alignas(VariantDetail::ParameterPackSize::AlignAsPack()) char m_storage[STORAGE_SIZE]; + IndexT m_index = -1; +#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG) + VariantDetail::VariantDebug::VariantDebugBrowser browser = m_storage; +#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ + + template constexpr bool holds_alternative() const { return Index == m_index; } + + struct Destroyer + { + template void operator()(AlternativeT &&value) const { - if (holds_alternative()) - { - T *value = reinterpret_cast(m_storage); - return value; - } - else - { - return nullptr; - } + (void)value; + using PlaintT = typename std::remove_reference::type; + value.~PlaintT(); } + }; + + void Destroy() + { + AWS_FATAL_ASSERT(m_index != -1); + Visit(Destroyer()); + + m_index = -1; + } - template auto get() -> typename ThisVariantAlternative::type & + struct CopyMoveConstructor + { + template void operator()(AlternativeT &&value, AlternativeT &&other) const { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - AWS_FATAL_ASSERT(holds_alternative()); - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *ret = reinterpret_cast(m_storage); - return *ret; + using PlaintT = typename std::remove_reference::type; + new (&value) PlaintT(std::move(other)); } - /* const get */ - template = 1> const T &get() const + template + void operator()(AlternativeT &&value, ConstAlternativeT &other) const { - AWS_FATAL_ASSERT(holds_alternative()); - const T *value = reinterpret_cast(m_storage); - return *value; + using PlaintT = typename std::remove_reference::type; + using PlaintOtherT = + typename std::remove_const::type>::type; + static_assert(std::is_same::value, "Incompatible types"); + + new (&value) PlaintT(other); } + }; - template = 1> const T *get_if() const + struct CopyMoveAssigner + { + template void operator()(AlternativeT &&value, AlternativeT &&other) const { - if (holds_alternative()) - { - T *value = reinterpret_cast(m_storage); - return value; - } - else - { - return nullptr; - } + value = std::move(other); } - template auto get() const -> const typename ThisVariantAlternative::type & + template + void operator()(AlternativeT &&value, ConstAlternativeT &other) const { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - AWS_ASSERT(Index == m_index); - using AlternativeT = typename ThisVariantAlternative::type; - const AlternativeT *ret = reinterpret_cast(m_storage); - return *ret; + using PlaintT = typename std::remove_reference::type; + using PlaintOtherT = + typename std::remove_const::type>::type; + static_assert(std::is_same::value, "Incompatible types"); + + value = other; } + }; - /* This is just a templated way to say - * "int*" for - * a VariantAlternative<0, VariantImpl()>*/ - template - using RawAlternativePointerT = - typename std::add_pointer::type>::type; + template struct VisitorUtil; - template auto get_if() -> RawAlternativePointerT + template + struct VisitorUtil + { + template static void Visit(Variant *pThis, VisitorStruct &&visitor) { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - if (holds_alternative()) + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) { - using AlternativePtrT = RawAlternativePointerT; - AlternativePtrT value = reinterpret_cast(m_storage); - return value; + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value); } else { - return nullptr; + VisitorUtil(Index + 1), Second, Rest...>::Visit( + pThis, std::forward(visitor)); } } - template - using ConstRawAlternativePointerT = typename std::add_pointer< - typename std::add_const::type>::type>::type; - - template auto get_if() const -> ConstRawAlternativePointerT + template + static void VisitBinary(Variant *pThis, Variant &&other, VisitorStruct &&visitor) { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - if (holds_alternative()) + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) { - using AlternativePtrT = ConstRawAlternativePointerT; - AlternativePtrT value = reinterpret_cast(m_storage); - return value; + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value, other.get()); } else { - return nullptr; + VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( + pThis, std::forward>(other), std::forward(visitor)); } } - std::size_t index() const { return m_index; } - - ~VariantImpl() { Destroy(); } - - template void Visit(VisitorT &&visitor) - { - return VisitorUtil<0, Ts...>::Visit(this, std::forward(visitor)); - } - - private: - static constexpr std::size_t STORAGE_SIZE = VariantDetail::ParameterPackSize::GetMaxSizeOf(); - - alignas(VariantDetail::ParameterPackSize::AlignAsPack()) char m_storage[STORAGE_SIZE]; - IndexT m_index = -1; -#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG) - VariantDetail::VariantDebug::VariantDebugBrowser browser = m_storage; -#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ - - template constexpr bool holds_alternative() const { return Index == m_index; } - - struct Destroyer - { - template void operator()(AlternativeT &&value) const - { - (void)value; - using PlaintT = typename std::remove_reference::type; - value.~PlaintT(); - } - }; - - void Destroy() + template + static void VisitBinary(Variant *pThis, const Variant &other, VisitorStruct &&visitor) { - AWS_FATAL_ASSERT(m_index != -1); - Visit(Destroyer()); + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - m_index = -1; - } - - struct CopyMoveConstructor - { - template void operator()(AlternativeT &&value, AlternativeT &&other) const + if (Index == pThis->m_index) { - using PlaintT = typename std::remove_reference::type; - new (&value) PlaintT(std::move(other)); + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + const AlternativeT &otherValue = other.get(); + visitor(*value, otherValue); } - - template - void operator()(AlternativeT &&value, ConstAlternativeT &other) const + else { - using PlaintT = typename std::remove_reference::type; - using PlaintOtherT = - typename std::remove_const::type>::type; - static_assert(std::is_same::value, "Incompatible types"); - - new (&value) PlaintT(other); + VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( + pThis, other, std::forward(visitor)); } - }; + } + }; - struct CopyMoveAssigner + template struct VisitorUtil + { + template static void Visit(Variant *pThis, VisitorStruct &&visitor) { - template void operator()(AlternativeT &&value, AlternativeT &&other) const + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) { - value = std::move(other); + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value); } - - template - void operator()(AlternativeT &&value, ConstAlternativeT &other) const + else { - using PlaintT = typename std::remove_reference::type; - using PlaintOtherT = - typename std::remove_const::type>::type; - static_assert(std::is_same::value, "Incompatible types"); - - value = other; + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); } - }; - - template struct VisitorUtil; + } - template - struct VisitorUtil + template + static void VisitBinary(Variant *pThis, Variant &&other, VisitorStruct &&visitor) { - template static void Visit(VariantImpl *pThis, VisitorStruct &&visitor) - { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - - if (Index == pThis->m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value); - } - else - { - VisitorUtil(Index + 1), Second, Rest...>::Visit( - pThis, std::forward(visitor)); - } - } + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - template - static void VisitBinary( - VariantImpl *pThis, - VariantImpl &&other, - VisitorStruct &&visitor) + if (Index == pThis->m_index) { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - - if (Index == pThis->m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value, other.get()); - } - else - { - VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( - pThis, std::forward>(other), std::forward(visitor)); - } + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value, other.get()); } - - template - static void VisitBinary( - VariantImpl *pThis, - const VariantImpl &other, - VisitorStruct &&visitor) + else { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - - if (Index == pThis->m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - const AlternativeT &otherValue = other.get(); - visitor(*value, otherValue); - } - else - { - VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( - pThis, other, std::forward(visitor)); - } + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); } - }; + } - template struct VisitorUtil + template + static void VisitBinary(Variant *pThis, const Variant &other, VisitorStruct &&visitor) { - template static void Visit(VariantImpl *pThis, VisitorStruct &&visitor) - { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - - if (Index == pThis->m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value); - } - else - { - AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); - } - } + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - template - static void VisitBinary( - VariantImpl *pThis, - VariantImpl &&other, - VisitorStruct &&visitor) + if (Index == pThis->m_index) { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - - if (Index == pThis->m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value, other.get()); - } - else - { - AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); - } + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + const AlternativeT &otherValue = other.get(); + visitor(*value, otherValue); } - - template - static void VisitBinary( - VariantImpl *pThis, - const VariantImpl &other, - VisitorStruct &&visitor) + else { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - - if (Index == pThis->m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - const AlternativeT &otherValue = other.get(); - visitor(*value, otherValue); - } - else - { - AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); - } + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); } - }; - }; - - /* Helper template to get an actual type from an Index */ - template class VariantAlternative - { - public: - // uses std::tuple as a helper struct to provide index-based access of a parameter pack - using type = typename std::tuple_element>::type; - - VariantAlternative(const VariantImpl &) {} - - VariantAlternative(const VariantImpl *) {} - }; - - template class VariantSize - { - constexpr static const std::size_t Value = T::AlternativeCount; + } }; + }; - } // namespace VariantDetail - - /** - * Custom implementation of a Variant type. std::variant requires C++17. - * @tparam Ts Types of the variant value. - */ - template - class Variant - : public VariantDetail::MovableVariant...>::value>, - public VariantDetail::CopyableVariant...>::value> + /* Helper template to get an actual type from an Index */ + template class VariantAlternative { - /* Copyability and Movability depend only on constructors (copy and move correspondingly) of the - * underlying types. This means that a class with move constructor but with no move assignment operator - * might cause compilation issues. If such a type ever needs to be supported, two additional base classes - * need to be introduced: CopyAssignable and MoveAssignable. For now, it'll be overengineering. The same - * applies to copy constructor and copy assignment operator. */ - - private: - template using ThisVariantAlternative = VariantDetail::VariantAlternative; - - template - using EnableIfOtherIsThisVariantAlternative = typename std:: - enable_if::type, Ts...>::value, int>::type; - public: - using IndexT = VariantDetail::Index::VariantIndex; - static constexpr std::size_t AlternativeCount = sizeof...(Ts); - - // TODO Check NoDefaultConstructible. - Variant() = default; - - template = 1> Variant(const T &val) : m_variant(val) - { - } - - template = 1> - Variant(T &&val) : m_variant(std::forward(val)) - { - } - - template - explicit Variant(Aws::Crt::InPlaceTypeT ipt, Args &&...args) - : m_variant(ipt, std::forward(args)...) - { - } - - template = 1> - T &emplace(Args &&...args) - { - return m_variant.template emplace(std::forward(args)...); - } - - template - auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & - { - return m_variant.template emplace(std::forward(args)...); - } - - template = 1> bool holds_alternative() const - { - return m_variant.template holds_alternative(); - } - - template = 1> T &get() - { - return m_variant.template get(); - } - - template = 1> T *get_if() - { - return m_variant.template get_if(); - } - - template auto get() -> typename ThisVariantAlternative::type & - { - return m_variant.template get(); - } - - template = 1> const T &get() const - { - return m_variant.template get(); - } - - template = 1> const T *get_if() const - { - return m_variant.template get_if(); - } - - template auto get() const -> const typename ThisVariantAlternative::type & - { - return m_variant.template get(); - } - - template - using RawAlternativePointerT = - typename std::add_pointer::type>::type; - - template auto get_if() -> RawAlternativePointerT - { - return m_variant.template get_if(); - } - - template - using ConstRawAlternativePointerT = typename std::add_pointer< - typename std::add_const::type>::type>::type; - - template auto get_if() const -> ConstRawAlternativePointerT - { - return m_variant.template get_if(); - } + // uses std::tuple as a helper struct to provide index-based access of a parameter pack + using type = typename std::tuple_element>::type; - std::size_t index() const { return m_variant.index(); } + VariantAlternative(const Variant &) {} - template void Visit(VisitorT &&visitor) - { - m_variant.Visit(std::forward(visitor)); - } - - private: - VariantDetail::VariantImpl m_variant; + VariantAlternative(const Variant *) {} }; + template class VariantSize + { + constexpr static const std::size_t Value = T::AlternativeCount; + }; } // namespace Crt } // namespace Aws From 91f180ff1b2585dca100a86c9ca40b035560fd31 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 21:44:16 -0700 Subject: [PATCH 17/30] Restore Variant changes --- include/aws/crt/Variant.h | 924 +++++++++++++++++++++++--------------- 1 file changed, 562 insertions(+), 362 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 1ac603fb8..0ba82289b 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -15,8 +15,23 @@ namespace Aws { namespace Crt { + namespace detail + { + template struct Conjunction : std::true_type + { + }; + + template + struct Conjunction + : std::conditional, std::false_type>::type + { + }; + + } // namespace detail + namespace VariantDetail { + // TODO Pass values instead of refs. template constexpr const T &ConstExprMax(const T &a, const T &b) { return (a < b) ? b : a; @@ -79,7 +94,7 @@ namespace Aws return std::is_same::value || ContainsType(); } - // a case when the template parameter pack is empty (i.e. Variant<>) + // a case when the template parameter pack is empty (i.e. VariantImpl<>) template constexpr bool ContainsType() { return false; @@ -116,477 +131,662 @@ namespace Aws }; } // namespace VariantDebug #endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ - } // namespace VariantDetail - template class VariantAlternative; + /* Depending on the Variant types, this struct either deletes special move members or defaults them. */ + template class MovableVariant; - /** - * Custom implementation of a Variant type. std::variant requires C++17 - * @tparam Ts types of the variant value - */ - template class Variant - { - private: - template using ThisVariantAlternative = VariantAlternative; - - template - using EnableIfOtherIsThisVariantAlternative = typename std:: - enable_if::type, Ts...>::value, int>::type; + template <> class MovableVariant + { + public: + MovableVariant() = default; + MovableVariant(const MovableVariant &) = default; + MovableVariant &operator=(const MovableVariant &) = default; + MovableVariant(MovableVariant &&other) = default; + MovableVariant &operator=(MovableVariant &&other) = default; + }; + template <> class MovableVariant + { + public: + MovableVariant() = default; + MovableVariant(const MovableVariant &) = default; + MovableVariant &operator=(const MovableVariant &) = default; + MovableVariant(MovableVariant &&) = delete; + MovableVariant &operator=(MovableVariant &&) = delete; + }; - public: - using IndexT = VariantDetail::Index::VariantIndex; - static constexpr std::size_t AlternativeCount = sizeof...(Ts); + /* Depending on the Variant types, this struct either deletes special copy members or defaults them. */ + template class CopyableVariant; - Variant() + template <> class CopyableVariant { - using FirstAlternative = typename ThisVariantAlternative<0>::type; - new (m_storage) FirstAlternative(); - m_index = 0; - } + public: + CopyableVariant() = default; + CopyableVariant(const CopyableVariant &other) = default; + CopyableVariant &operator=(const CopyableVariant &other) = default; + CopyableVariant(CopyableVariant &&) = default; + CopyableVariant &operator=(CopyableVariant &&) = default; + }; - Variant(const Variant &other) + template <> class CopyableVariant { - AWS_FATAL_ASSERT(other.m_index != -1); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); - } + public: + CopyableVariant() = default; - Variant(Variant &&other) - { - AWS_FATAL_ASSERT(other.m_index != -1); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); - } + CopyableVariant(const CopyableVariant &) = delete; + CopyableVariant &operator=(const CopyableVariant &) = delete; - template = 1> Variant(const T &val) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); - - using PlainT = typename std::decay::type; - new (m_storage) PlainT(val); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - } + CopyableVariant(CopyableVariant &&) = default; + CopyableVariant &operator=(CopyableVariant &&) = default; + }; - template = 1> Variant(T &&val) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); - - using PlainT = typename std::decay::type; - new (m_storage) PlainT(std::forward(val)); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - } + template class VariantAlternative; - // An overload to initialize with an Alternative T in-place - template explicit Variant(Aws::Crt::InPlaceTypeT, Args &&...args) + template class VariantImpl { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); - - using PlainT = typename std::decay::type; - new (m_storage) PlainT(std::forward(args)...); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - } + private: + template using ThisVariantAlternative = VariantAlternative; + + template + using EnableIfOtherIsThisVariantAlternative = typename std::enable_if< + VariantDetail::Checker::HasType::type, Ts...>::value, + int>::type; + + public: + using IndexT = VariantDetail::Index::VariantIndex; + static constexpr std::size_t AlternativeCount = sizeof...(Ts); + + static constexpr bool is_copy_constructible = + detail::Conjunction...>::value; + static constexpr bool is_copy_assignable = detail::Conjunction...>::value; + static constexpr bool is_move_constructible = + detail::Conjunction...>::value; + static constexpr bool is_move_assignable = detail::Conjunction...>::value; + + VariantImpl() + { + using FirstAlternative = typename ThisVariantAlternative<0>::type; + new (m_storage) FirstAlternative(); + m_index = 0; + } - Variant &operator=(const Variant &other) - { - if (this != &other) + VariantImpl(const VariantImpl &other) { AWS_FATAL_ASSERT(other.m_index != -1); - if (m_index != other.m_index) - { - Destroy(); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); - } - else - { - VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner()); - } + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); } - return *this; - } - Variant &operator=(Variant &&other) - { - if (this != &other) + VariantImpl(VariantImpl &&other) { AWS_FATAL_ASSERT(other.m_index != -1); - if (m_index != other.m_index) - { - Destroy(); - m_index = other.m_index; - VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); - } - else - { - VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveAssigner()); - }; + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); } - return *this; - } - /* emplace */ - template = 1> - T &emplace(Args &&...args) - { - static_assert( - VariantDetail::Checker::HasType::type, Ts...>::value, - "This variant does not have such alternative T."); - static_assert( - sizeof(T) <= STORAGE_SIZE, - "Attempting to instantiate a Variant with a type bigger than all alternatives."); - - Destroy(); - - using PlainT = typename std::decay::type; - new (m_storage) PlainT(std::forward(args)...); - m_index = VariantDetail::Index::GetIndexOf(); - AWS_ASSERT(m_index != -1); - - T *value = reinterpret_cast(m_storage); - return *value; - } - - template - auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & - { - static_assert(Index < AlternativeCount, "Unknown alternative index to emplace"); - using AlternativeT = typename ThisVariantAlternative::type; + template = 1> VariantImpl(const T &val) + { + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a Variant with a type bigger than all alternatives."); - return emplace(std::forward(args)...); - } + using PlainT = typename std::decay::type; + new (m_storage) PlainT(val); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); + } - template = 1> bool holds_alternative() const - { - AWS_ASSERT(m_index != -1); - return m_index == VariantDetail::Index::GetIndexOf(); - } + template = 1> VariantImpl(T &&val) + { + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a Variant with a type bigger than all alternatives."); - /* non-const get */ - template = 1> T &get() - { - AWS_FATAL_ASSERT(holds_alternative()); - T *value = reinterpret_cast(m_storage); - return *value; - } + using PlainT = typename std::decay::type; + new (m_storage) PlainT(std::forward(val)); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); + } - template = 1> T *get_if() - { - if (holds_alternative()) + // An overload to initialize with an Alternative T in-place + template explicit VariantImpl(Aws::Crt::InPlaceTypeT, Args &&...args) { - T *value = reinterpret_cast(m_storage); - return value; + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a Variant with a type bigger than all alternatives."); + + using PlainT = typename std::decay::type; + new (m_storage) PlainT(std::forward(args)...); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); } - else + + VariantImpl &operator=(const VariantImpl &other) { - return nullptr; + if (this != &other) + { + AWS_FATAL_ASSERT(other.m_index != -1); + if (m_index != other.m_index) + { + Destroy(); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); + } + else + { + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner()); + } + } + return *this; } - } - - template auto get() -> typename ThisVariantAlternative::type & - { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - AWS_FATAL_ASSERT(holds_alternative()); - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *ret = reinterpret_cast(m_storage); - return *ret; - } - - /* const get */ - template = 1> const T &get() const - { - AWS_FATAL_ASSERT(holds_alternative()); - const T *value = reinterpret_cast(m_storage); - return *value; - } - template = 1> const T *get_if() const - { - if (holds_alternative()) + VariantImpl &operator=(VariantImpl &&other) { - T *value = reinterpret_cast(m_storage); - return value; + if (this != &other) + { + AWS_FATAL_ASSERT(other.m_index != -1); + if (m_index != other.m_index) + { + Destroy(); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); + } + else + { + VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveAssigner()); + }; + } + return *this; } - else + + /* emplace */ + template = 1> + T &emplace(Args &&...args) { - return nullptr; - } - } + static_assert( + VariantDetail::Checker::HasType::type, Ts...>::value, + "This variant does not have such alternative T."); + static_assert( + sizeof(T) <= STORAGE_SIZE, + "Attempting to instantiate a Variant with a type bigger than all alternatives."); - template auto get() const -> const typename ThisVariantAlternative::type & - { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - AWS_ASSERT(Index == m_index); - using AlternativeT = typename ThisVariantAlternative::type; - const AlternativeT *ret = reinterpret_cast(m_storage); - return *ret; - } + Destroy(); - /* This is just a templated way to say - * "int*" for - * a VariantAlternative<0, Variant()>*/ - template - using RawAlternativePointerT = - typename std::add_pointer::type>::type; + using PlainT = typename std::decay::type; + new (m_storage) PlainT(std::forward(args)...); + m_index = VariantDetail::Index::GetIndexOf(); + AWS_ASSERT(m_index != -1); - template auto get_if() -> RawAlternativePointerT - { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - if (holds_alternative()) - { - using AlternativePtrT = RawAlternativePointerT; - AlternativePtrT value = reinterpret_cast(m_storage); - return value; + T *value = reinterpret_cast(m_storage); + return *value; } - else + + template + auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & { - return nullptr; - } - } + static_assert(Index < AlternativeCount, "Unknown alternative index to emplace"); + using AlternativeT = typename ThisVariantAlternative::type; - template - using ConstRawAlternativePointerT = typename std::add_pointer< - typename std::add_const::type>::type>::type; + return emplace(std::forward(args)...); + } - template auto get_if() const -> ConstRawAlternativePointerT - { - static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - if (holds_alternative()) + template = 1> bool holds_alternative() const { - using AlternativePtrT = ConstRawAlternativePointerT; - AlternativePtrT value = reinterpret_cast(m_storage); - return value; + AWS_ASSERT(m_index != -1); + return m_index == VariantDetail::Index::GetIndexOf(); } - else + + /* non-const get */ + template = 1> T &get() { - return nullptr; + AWS_FATAL_ASSERT(holds_alternative()); + T *value = reinterpret_cast(m_storage); + return *value; } - } - - std::size_t index() const { return m_index; } - - ~Variant() { Destroy(); } - - template void Visit(VisitorT &&visitor) - { - return VisitorUtil<0, Ts...>::Visit(this, std::forward(visitor)); - } - private: - static constexpr std::size_t STORAGE_SIZE = VariantDetail::ParameterPackSize::GetMaxSizeOf(); - - alignas(VariantDetail::ParameterPackSize::AlignAsPack()) char m_storage[STORAGE_SIZE]; - IndexT m_index = -1; -#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG) - VariantDetail::VariantDebug::VariantDebugBrowser browser = m_storage; -#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ - - template constexpr bool holds_alternative() const { return Index == m_index; } - - struct Destroyer - { - template void operator()(AlternativeT &&value) const + template = 1> T *get_if() { - (void)value; - using PlaintT = typename std::remove_reference::type; - value.~PlaintT(); + if (holds_alternative()) + { + T *value = reinterpret_cast(m_storage); + return value; + } + else + { + return nullptr; + } } - }; - - void Destroy() - { - AWS_FATAL_ASSERT(m_index != -1); - Visit(Destroyer()); - - m_index = -1; - } - struct CopyMoveConstructor - { - template void operator()(AlternativeT &&value, AlternativeT &&other) const + template auto get() -> typename ThisVariantAlternative::type & { - using PlaintT = typename std::remove_reference::type; - new (&value) PlaintT(std::move(other)); + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + AWS_FATAL_ASSERT(holds_alternative()); + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *ret = reinterpret_cast(m_storage); + return *ret; } - template - void operator()(AlternativeT &&value, ConstAlternativeT &other) const + /* const get */ + template = 1> const T &get() const { - using PlaintT = typename std::remove_reference::type; - using PlaintOtherT = - typename std::remove_const::type>::type; - static_assert(std::is_same::value, "Incompatible types"); - - new (&value) PlaintT(other); + AWS_FATAL_ASSERT(holds_alternative()); + const T *value = reinterpret_cast(m_storage); + return *value; } - }; - struct CopyMoveAssigner - { - template void operator()(AlternativeT &&value, AlternativeT &&other) const + template = 1> const T *get_if() const { - value = std::move(other); + if (holds_alternative()) + { + T *value = reinterpret_cast(m_storage); + return value; + } + else + { + return nullptr; + } } - template - void operator()(AlternativeT &&value, ConstAlternativeT &other) const + template auto get() const -> const typename ThisVariantAlternative::type & { - using PlaintT = typename std::remove_reference::type; - using PlaintOtherT = - typename std::remove_const::type>::type; - static_assert(std::is_same::value, "Incompatible types"); - - value = other; + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + AWS_ASSERT(Index == m_index); + using AlternativeT = typename ThisVariantAlternative::type; + const AlternativeT *ret = reinterpret_cast(m_storage); + return *ret; } - }; - template struct VisitorUtil; + /* This is just a templated way to say + * "int*" for + * a VariantAlternative<0, VariantImpl()>*/ + template + using RawAlternativePointerT = + typename std::add_pointer::type>::type; - template - struct VisitorUtil - { - template static void Visit(Variant *pThis, VisitorStruct &&visitor) + template auto get_if() -> RawAlternativePointerT { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - - if (Index == pThis->m_index) + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + if (holds_alternative()) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value); + using AlternativePtrT = RawAlternativePointerT; + AlternativePtrT value = reinterpret_cast(m_storage); + return value; } else { - VisitorUtil(Index + 1), Second, Rest...>::Visit( - pThis, std::forward(visitor)); + return nullptr; } } - template - static void VisitBinary(Variant *pThis, Variant &&other, VisitorStruct &&visitor) - { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + template + using ConstRawAlternativePointerT = typename std::add_pointer< + typename std::add_const::type>::type>::type; - if (Index == pThis->m_index) + template auto get_if() const -> ConstRawAlternativePointerT + { + static_assert(Index < AlternativeCount, "Unknown alternative index to get"); + if (holds_alternative()) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value, other.get()); + using AlternativePtrT = ConstRawAlternativePointerT; + AlternativePtrT value = reinterpret_cast(m_storage); + return value; } else { - VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( - pThis, std::forward>(other), std::forward(visitor)); + return nullptr; } } - template - static void VisitBinary(Variant *pThis, const Variant &other, VisitorStruct &&visitor) + std::size_t index() const { return m_index; } + + ~VariantImpl() { Destroy(); } + + template void Visit(VisitorT &&visitor) { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + return VisitorUtil<0, Ts...>::Visit(this, std::forward(visitor)); + } - if (Index == pThis->m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - const AlternativeT &otherValue = other.get(); - visitor(*value, otherValue); - } - else + private: + static constexpr std::size_t STORAGE_SIZE = VariantDetail::ParameterPackSize::GetMaxSizeOf(); + + alignas(VariantDetail::ParameterPackSize::AlignAsPack()) char m_storage[STORAGE_SIZE]; + IndexT m_index = -1; +#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG) + VariantDetail::VariantDebug::VariantDebugBrowser browser = m_storage; +#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ + + template constexpr bool holds_alternative() const { return Index == m_index; } + + struct Destroyer + { + template void operator()(AlternativeT &&value) const { - VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( - pThis, other, std::forward(visitor)); + (void)value; + using PlaintT = typename std::remove_reference::type; + value.~PlaintT(); } + }; + + void Destroy() + { + AWS_FATAL_ASSERT(m_index != -1); + Visit(Destroyer()); + + m_index = -1; } - }; - template struct VisitorUtil - { - template static void Visit(Variant *pThis, VisitorStruct &&visitor) + struct CopyMoveConstructor { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + template void operator()(AlternativeT &&value, AlternativeT &&other) const + { + using PlaintT = typename std::remove_reference::type; + new (&value) PlaintT(std::move(other)); + } - if (Index == pThis->m_index) + template + void operator()(AlternativeT &&value, ConstAlternativeT &other) const { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value); + using PlaintT = typename std::remove_reference::type; + using PlaintOtherT = + typename std::remove_const::type>::type; + static_assert(std::is_same::value, "Incompatible types"); + + new (&value) PlaintT(other); } - else + }; + + struct CopyMoveAssigner + { + template void operator()(AlternativeT &&value, AlternativeT &&other) const { - AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + value = std::move(other); } - } - template - static void VisitBinary(Variant *pThis, Variant &&other, VisitorStruct &&visitor) + template + void operator()(AlternativeT &&value, ConstAlternativeT &other) const + { + using PlaintT = typename std::remove_reference::type; + using PlaintOtherT = + typename std::remove_const::type>::type; + static_assert(std::is_same::value, "Incompatible types"); + + value = other; + } + }; + + template struct VisitorUtil; + + template + struct VisitorUtil { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + template static void Visit(VariantImpl *pThis, VisitorStruct &&visitor) + { + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value); + } + else + { + VisitorUtil(Index + 1), Second, Rest...>::Visit( + pThis, std::forward(visitor)); + } + } - if (Index == pThis->m_index) + template + static void VisitBinary( + VariantImpl *pThis, + VariantImpl &&other, + VisitorStruct &&visitor) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - visitor(*value, other.get()); + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value, other.get()); + } + else + { + VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( + pThis, std::forward>(other), std::forward(visitor)); + } } - else + + template + static void VisitBinary( + VariantImpl *pThis, + const VariantImpl &other, + VisitorStruct &&visitor) { - AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + const AlternativeT &otherValue = other.get(); + visitor(*value, otherValue); + } + else + { + VisitorUtil(Index + 1), Second, Rest...>::VisitBinary( + pThis, other, std::forward(visitor)); + } } - } + }; - template - static void VisitBinary(Variant *pThis, const Variant &other, VisitorStruct &&visitor) + template struct VisitorUtil { - static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + template static void Visit(VariantImpl *pThis, VisitorStruct &&visitor) + { + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value); + } + else + { + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + } + } - if (Index == pThis->m_index) + template + static void VisitBinary( + VariantImpl *pThis, + VariantImpl &&other, + VisitorStruct &&visitor) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(pThis->m_storage); - const AlternativeT &otherValue = other.get(); - visitor(*value, otherValue); + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value, other.get()); + } + else + { + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + } } - else + + template + static void VisitBinary( + VariantImpl *pThis, + const VariantImpl &other, + VisitorStruct &&visitor) { - AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); + + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + const AlternativeT &otherValue = other.get(); + visitor(*value, otherValue); + } + else + { + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + } } - } + }; }; - }; - /* Helper template to get an actual type from an Index */ - template class VariantAlternative + /* Helper template to get an actual type from an Index */ + template class VariantAlternative + { + public: + // uses std::tuple as a helper struct to provide index-based access of a parameter pack + using type = typename std::tuple_element>::type; + + VariantAlternative(const VariantImpl &) {} + + VariantAlternative(const VariantImpl *) {} + }; + + template class VariantSize + { + constexpr static const std::size_t Value = T::AlternativeCount; + }; + + } // namespace VariantDetail + + /** + * Custom implementation of a Variant type. std::variant requires C++17. + * @tparam Ts Types of the variant value. + */ + template + class Variant + : public VariantDetail::MovableVariant...>::value>, + public VariantDetail::CopyableVariant...>::value> { + /* Copyability and Movability depend only on constructors (copy and move correspondingly) of the + * underlying types. This means that a class with move constructor but with no move assignment operator + * might cause compilation issues. If such a type ever needs to be supported, two additional base classes + * need to be introduced: CopyAssignable and MoveAssignable. For now, it'll be overengineering. The same + * applies to copy constructor and copy assignment operator. */ + + private: + template using ThisVariantAlternative = VariantDetail::VariantAlternative; + + template + using EnableIfOtherIsThisVariantAlternative = typename std:: + enable_if::type, Ts...>::value, int>::type; + public: - // uses std::tuple as a helper struct to provide index-based access of a parameter pack - using type = typename std::tuple_element>::type; + using IndexT = VariantDetail::Index::VariantIndex; + static constexpr std::size_t AlternativeCount = sizeof...(Ts); - VariantAlternative(const Variant &) {} + // TODO Check NoDefaultConstructible. + Variant() = default; - VariantAlternative(const Variant *) {} - }; + template = 1> Variant(const T &val) : m_variant(val) + { + } - template class VariantSize - { - constexpr static const std::size_t Value = T::AlternativeCount; + template = 1> + Variant(T &&val) : m_variant(std::forward(val)) + { + } + + template + explicit Variant(Aws::Crt::InPlaceTypeT ipt, Args &&...args) + : m_variant(ipt, std::forward(args)...) + { + } + + template = 1> + T &emplace(Args &&...args) + { + return m_variant.template emplace(std::forward(args)...); + } + + template + auto emplace(Args &&...args) -> typename ThisVariantAlternative::type & + { + return m_variant.template emplace(std::forward(args)...); + } + + template = 1> bool holds_alternative() const + { + return m_variant.template holds_alternative(); + } + + template = 1> T &get() + { + return m_variant.template get(); + } + + template = 1> T *get_if() + { + return m_variant.template get_if(); + } + + template auto get() -> typename ThisVariantAlternative::type & + { + return m_variant.template get(); + } + + template = 1> const T &get() const + { + return m_variant.template get(); + } + + template = 1> const T *get_if() const + { + return m_variant.template get_if(); + } + + template auto get() const -> const typename ThisVariantAlternative::type & + { + return m_variant.template get(); + } + + template + using RawAlternativePointerT = + typename std::add_pointer::type>::type; + + template auto get_if() -> RawAlternativePointerT + { + return m_variant.template get_if(); + } + + template + using ConstRawAlternativePointerT = typename std::add_pointer< + typename std::add_const::type>::type>::type; + + template auto get_if() const -> ConstRawAlternativePointerT + { + return m_variant.template get_if(); + } + + std::size_t index() const { return m_variant.index(); } + + template void Visit(VisitorT &&visitor) + { + m_variant.Visit(std::forward(visitor)); + } + + private: + VariantDetail::VariantImpl m_variant; }; + } // namespace Crt } // namespace Aws From ff7837c0407e1e6bdee587a1186515aad59d04a8 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 21:45:10 -0700 Subject: [PATCH 18/30] Add copy-only test --- tests/CMakeLists.txt | 1 + tests/VariantTest.cpp | 46 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 62011a0f7..f43e8df0a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -107,6 +107,7 @@ add_test_case(VariantCompiles) add_test_case(VariantConstructor) add_test_case(VariantAssignmentOperator) add_test_case(VariantWithMoveOnlyUnderlyingType) +add_test_case(VariantWithCopyOnlyUnderlyingType) add_test_case(VariantEmplace) add_test_case(VariantVisitor) add_test_case(StreamTestCreateDestroyWrapper) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 0644b645c..c7c66ffe3 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -216,14 +216,14 @@ static int s_VariantAssignmentOperator(struct aws_allocator *allocator, void *ct AWS_TEST_CASE(VariantAssignmentOperator, s_VariantAssignmentOperator) +// Test Variant with move-only underlying type. +// If it compiles, it's considered success. static int s_VariantWithMoveOnlyUnderlyingType(struct aws_allocator *allocator, void *ctx) { (void)ctx; Aws::Crt::ApiHandle apiHandle(allocator); - // test with a move-only type - struct MoveOnlyTestType { MoveOnlyTestType() = default; @@ -258,6 +258,48 @@ static int s_VariantWithMoveOnlyUnderlyingType(struct aws_allocator *allocator, AWS_TEST_CASE(VariantWithMoveOnlyUnderlyingType, s_VariantWithMoveOnlyUnderlyingType) +// Test Variant with copy-only underlying type. +// If it compiles, it's considered success. +static int s_VariantWithCopyOnlyUnderlyingType(struct aws_allocator *allocator, void *ctx) +{ + (void)ctx; + + Aws::Crt::ApiHandle apiHandle(allocator); + + struct CopyOnlyTestType + { + CopyOnlyTestType() = default; + + CopyOnlyTestType(CopyOnlyTestType &&) = default; + CopyOnlyTestType &operator=(CopyOnlyTestType &&) = default; + + CopyOnlyTestType(const CopyOnlyTestType &) = delete; + CopyOnlyTestType &operator=(const CopyOnlyTestType &) = delete; + }; + + using CopyOnlyVariant = Aws::Crt::Variant; + + /* Regression test. + * The __declspec(dllexport) directive exports class member function on Windows platform. We enable it when + * building shared libraries. In the past, this directive caused msvc to generate special copy members for classes + * containing Crt::Variant with copy-only underlying types, which led to compile-time errors. */ + +#if defined(_WIN32) +# define AWS_VARIANTTEST_WINDOWS_API __declspec(dllexport) +#else +# define AWS_VARIANTTEST_WINDOWS_API +#endif + + struct AWS_VARIANTTEST_WINDOWS_API FailVariantTestResult + { + CopyOnlyVariant m_result; + }; + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(VariantWithCopyOnlyUnderlyingType, s_VariantWithCopyOnlyUnderlyingType) + struct TestStringOnlyVisitor { /* can't specialize member function templates, so using such syntax of dummy structs */ From 7db8322946f7a426f75e114b0dee17f1dd5f19b3 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 21:55:38 -0700 Subject: [PATCH 19/30] Fix naming in tests --- tests/VariantTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index c7c66ffe3..9974e963b 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -248,7 +248,7 @@ static int s_VariantWithMoveOnlyUnderlyingType(struct aws_allocator *allocator, # define AWS_VARIANTTEST_WINDOWS_API #endif - struct AWS_VARIANTTEST_WINDOWS_API FailVariantTestResult + struct AWS_VARIANTTEST_WINDOWS_API MoveOnlyVariantTestResult { MoveOnlyVariant m_result; }; @@ -290,7 +290,7 @@ static int s_VariantWithCopyOnlyUnderlyingType(struct aws_allocator *allocator, # define AWS_VARIANTTEST_WINDOWS_API #endif - struct AWS_VARIANTTEST_WINDOWS_API FailVariantTestResult + struct AWS_VARIANTTEST_WINDOWS_API CopyOnlyVariantTestResult { CopyOnlyVariant m_result; }; From 892c0b0c3ce4b0e908e012479e0922873aba1906 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 22:02:42 -0700 Subject: [PATCH 20/30] Extract Conjunction into type_traits header --- include/aws/crt/TypeTraits.h | 14 ++++++++++++++ include/aws/crt/Variant.h | 26 ++------------------------ 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/include/aws/crt/TypeTraits.h b/include/aws/crt/TypeTraits.h index 72ed9d9a6..ffc0c192f 100644 --- a/include/aws/crt/TypeTraits.h +++ b/include/aws/crt/TypeTraits.h @@ -26,5 +26,19 @@ namespace Aws struct IsSpecializationOf, Primary> : std::true_type { }; + + /** + * Forms the logical conjunction of the type traits Args..., effectively performing a logical AND on the + * sequence of traits. + */ + template struct Conjunction : std::true_type + { + }; + + template + struct Conjunction : std::conditional, std::false_type>::type + { + }; + } // namespace Crt } // namespace Aws diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 0ba82289b..93089497d 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -15,20 +15,6 @@ namespace Aws { namespace Crt { - namespace detail - { - template struct Conjunction : std::true_type - { - }; - - template - struct Conjunction - : std::conditional, std::false_type>::type - { - }; - - } // namespace detail - namespace VariantDetail { // TODO Pass values instead of refs. @@ -195,13 +181,6 @@ namespace Aws using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); - static constexpr bool is_copy_constructible = - detail::Conjunction...>::value; - static constexpr bool is_copy_assignable = detail::Conjunction...>::value; - static constexpr bool is_move_constructible = - detail::Conjunction...>::value; - static constexpr bool is_move_assignable = detail::Conjunction...>::value; - VariantImpl() { using FirstAlternative = typename ThisVariantAlternative<0>::type; @@ -673,9 +652,8 @@ namespace Aws * @tparam Ts Types of the variant value. */ template - class Variant - : public VariantDetail::MovableVariant...>::value>, - public VariantDetail::CopyableVariant...>::value> + class Variant : public VariantDetail::MovableVariant...>::value>, + public VariantDetail::CopyableVariant...>::value> { /* Copyability and Movability depend only on constructors (copy and move correspondingly) of the * underlying types. This means that a class with move constructor but with no move assignment operator From 9db24fc7402b1ed3d4d01c934f285644ea5c3d95 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Mon, 11 Aug 2025 22:03:11 -0700 Subject: [PATCH 21/30] Add test case for nothrow constructible --- tests/CMakeLists.txt | 1 + tests/VariantTest.cpp | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f43e8df0a..999a994e9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -108,6 +108,7 @@ add_test_case(VariantConstructor) add_test_case(VariantAssignmentOperator) add_test_case(VariantWithMoveOnlyUnderlyingType) add_test_case(VariantWithCopyOnlyUnderlyingType) +add_test_case(VariantNoexceptConstructible) add_test_case(VariantEmplace) add_test_case(VariantVisitor) add_test_case(StreamTestCreateDestroyWrapper) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 9974e963b..8236a9030 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -300,6 +300,26 @@ static int s_VariantWithCopyOnlyUnderlyingType(struct aws_allocator *allocator, AWS_TEST_CASE(VariantWithCopyOnlyUnderlyingType, s_VariantWithCopyOnlyUnderlyingType) +static int s_VariantNoexceptConstructible(struct aws_allocator *allocator, void *ctx) +{ + (void)ctx; + + Aws::Crt::ApiHandle apiHandle(allocator); + + struct NothorwConstructibleTestType + { + NothorwConstructibleTestType() noexcept = default; + }; + + using NothrowConstructibleVariant = Aws::Crt::Variant; + + ASSERT_INT_EQUALS(1, std::is_nothrow_constructible::value); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(VariantNoexceptConstructible, s_VariantNoexceptConstructible) + struct TestStringOnlyVisitor { /* can't specialize member function templates, so using such syntax of dummy structs */ From 0b713fb6f75cbd7cba56f3409d41e4dca2acbdc0 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 12 Aug 2025 11:55:08 -0700 Subject: [PATCH 22/30] More tests --- include/aws/crt/Variant.h | 8 +++- tests/CMakeLists.txt | 4 +- tests/VariantTest.cpp | 81 ++++++++++++++++++++++++++++++--------- 3 files changed, 71 insertions(+), 22 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 93089497d..772be8ce7 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -668,12 +668,16 @@ namespace Aws using EnableIfOtherIsThisVariantAlternative = typename std:: enable_if::type, Ts...>::value, int>::type; + using FirstAlternative = typename ThisVariantAlternative<0>::type; + + static constexpr bool isFirstAlternativeNothrowDefaultConstructible = + std::is_nothrow_default_constructible::value; + public: using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); - // TODO Check NoDefaultConstructible. - Variant() = default; + Variant() noexcept(isFirstAlternativeNothrowDefaultConstructible) = default; template = 1> Variant(const T &val) : m_variant(val) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 999a994e9..3ccecc58e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -108,7 +108,9 @@ add_test_case(VariantConstructor) add_test_case(VariantAssignmentOperator) add_test_case(VariantWithMoveOnlyUnderlyingType) add_test_case(VariantWithCopyOnlyUnderlyingType) -add_test_case(VariantNoexceptConstructible) +add_test_case(VariantWithNoDefaultConstructibleUnderlyingType) +add_test_case(VariantNothrowConstructible) +add_test_case(VariantThrowConstructible) add_test_case(VariantEmplace) add_test_case(VariantVisitor) add_test_case(StreamTestCreateDestroyWrapper) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 8236a9030..6cb6f7014 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -6,6 +6,12 @@ #include #include +#if defined(_WIN32) +# define AWS_VARIANTTEST_WINDOWS_API __declspec(dllexport) +#else +# define AWS_VARIANTTEST_WINDOWS_API +#endif + const char *s_variant_test_str = "This is a string, that should be long enough to avoid small string optimizations"; static int s_VariantBasicOperandsCompile(struct aws_allocator *allocator, void *ctx) @@ -241,13 +247,6 @@ static int s_VariantWithMoveOnlyUnderlyingType(struct aws_allocator *allocator, * The __declspec(dllexport) directive exports class member function on Windows platform. We enable it when * building shared libraries. In the past, this directive caused msvc to generate special copy members for classes * containing Crt::Variant with move-only underlying types, which led to compile-time errors. */ - -#if defined(_WIN32) -# define AWS_VARIANTTEST_WINDOWS_API __declspec(dllexport) -#else -# define AWS_VARIANTTEST_WINDOWS_API -#endif - struct AWS_VARIANTTEST_WINDOWS_API MoveOnlyVariantTestResult { MoveOnlyVariant m_result; @@ -283,13 +282,6 @@ static int s_VariantWithCopyOnlyUnderlyingType(struct aws_allocator *allocator, * The __declspec(dllexport) directive exports class member function on Windows platform. We enable it when * building shared libraries. In the past, this directive caused msvc to generate special copy members for classes * containing Crt::Variant with copy-only underlying types, which led to compile-time errors. */ - -#if defined(_WIN32) -# define AWS_VARIANTTEST_WINDOWS_API __declspec(dllexport) -#else -# define AWS_VARIANTTEST_WINDOWS_API -#endif - struct AWS_VARIANTTEST_WINDOWS_API CopyOnlyVariantTestResult { CopyOnlyVariant m_result; @@ -300,25 +292,76 @@ static int s_VariantWithCopyOnlyUnderlyingType(struct aws_allocator *allocator, AWS_TEST_CASE(VariantWithCopyOnlyUnderlyingType, s_VariantWithCopyOnlyUnderlyingType) -static int s_VariantNoexceptConstructible(struct aws_allocator *allocator, void *ctx) +// Test Variant with underlying type without default constructor. +// If it compiles, it's considered success. +static int s_VariantWithNoDefaultConstructibleUnderlyingType(struct aws_allocator *allocator, void *ctx) { (void)ctx; Aws::Crt::ApiHandle apiHandle(allocator); - struct NothorwConstructibleTestType + struct NoDefaultConstructibleTestType { - NothorwConstructibleTestType() noexcept = default; + explicit NoDefaultConstructibleTestType(int) {} + NoDefaultConstructibleTestType() = delete; }; - using NothrowConstructibleVariant = Aws::Crt::Variant; + using NoDefaultConstructibleVariant = Aws::Crt::Variant; + /* Regression test. + * The __declspec(dllexport) directive exports class member function on Windows platform. We enable it when + * building shared libraries. In the past, this directive caused msvc to generate special copy members for classes + * containing Crt::Variant with copy-only underlying types, which led to compile-time errors. */ + struct AWS_VARIANTTEST_WINDOWS_API NoDefaultConstructibleVariantTestResult + { + NoDefaultConstructibleVariant m_result; + }; + + NoDefaultConstructibleTestType testType(1); + NoDefaultConstructibleVariant variant(testType); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(VariantWithNoDefaultConstructibleUnderlyingType, s_VariantWithNoDefaultConstructibleUnderlyingType) + +static int s_VariantNothrowConstructible(struct aws_allocator *allocator, void *ctx) +{ + (void)ctx; + + Aws::Crt::ApiHandle apiHandle(allocator); + + struct NothrowConstructibleTestType + { + NothrowConstructibleTestType() noexcept = default; + }; + using NothrowConstructibleVariant = Aws::Crt::Variant; ASSERT_INT_EQUALS(1, std::is_nothrow_constructible::value); return AWS_OP_SUCCESS; } -AWS_TEST_CASE(VariantNoexceptConstructible, s_VariantNoexceptConstructible) +AWS_TEST_CASE(VariantNothrowConstructible, s_VariantNothrowConstructible) + +static int s_VariantThrowConstructible(struct aws_allocator *allocator, void *ctx) +{ + (void)ctx; + + Aws::Crt::ApiHandle apiHandle(allocator); + + struct ThrowConstructibleTestType + { + // Must be user-defined to be non-nothrow. + ThrowConstructibleTestType() {} + }; + + using ThrowConstructibleVariant = Aws::Crt::Variant; + ASSERT_INT_EQUALS(0, std::is_nothrow_constructible::value); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(VariantThrowConstructible, s_VariantThrowConstructible) struct TestStringOnlyVisitor { From 9cd1ce1e1523792fd448f2baf46cac2f8b069a71 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 12 Aug 2025 11:58:41 -0700 Subject: [PATCH 23/30] Clean test --- tests/VariantTest.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 6cb6f7014..ddcf2b15c 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -308,10 +308,6 @@ static int s_VariantWithNoDefaultConstructibleUnderlyingType(struct aws_allocato using NoDefaultConstructibleVariant = Aws::Crt::Variant; - /* Regression test. - * The __declspec(dllexport) directive exports class member function on Windows platform. We enable it when - * building shared libraries. In the past, this directive caused msvc to generate special copy members for classes - * containing Crt::Variant with copy-only underlying types, which led to compile-time errors. */ struct AWS_VARIANTTEST_WINDOWS_API NoDefaultConstructibleVariantTestResult { NoDefaultConstructibleVariant m_result; @@ -319,6 +315,7 @@ static int s_VariantWithNoDefaultConstructibleUnderlyingType(struct aws_allocato NoDefaultConstructibleTestType testType(1); NoDefaultConstructibleVariant variant(testType); + NoDefaultConstructibleVariantTestResult testResult{variant}; return AWS_OP_SUCCESS; } From 151688af2a14d8dc4597fbf8f689d81069f7cdf4 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 12 Aug 2025 12:50:45 -0700 Subject: [PATCH 24/30] Add missing header --- include/aws/crt/Variant.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 772be8ce7..7a6996b57 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -5,6 +5,7 @@ */ #include +#include #include #include From 06a9ad3c2f3dcc7759fa1f7e9b26113d6cd10848 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 12 Aug 2025 13:02:18 -0700 Subject: [PATCH 25/30] Check noexcept in VariantImpl --- include/aws/crt/Variant.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 7a6996b57..110200446 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -178,11 +178,16 @@ namespace Aws VariantDetail::Checker::HasType::type, Ts...>::value, int>::type; + using FirstAlternative = typename ThisVariantAlternative<0>::type; + + static constexpr bool isFirstAlternativeNothrowDefaultConstructible = + std::is_nothrow_default_constructible::value; + public: using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); - VariantImpl() + VariantImpl() noexcept(isFirstAlternativeNothrowDefaultConstructible) { using FirstAlternative = typename ThisVariantAlternative<0>::type; new (m_storage) FirstAlternative(); @@ -669,16 +674,11 @@ namespace Aws using EnableIfOtherIsThisVariantAlternative = typename std:: enable_if::type, Ts...>::value, int>::type; - using FirstAlternative = typename ThisVariantAlternative<0>::type; - - static constexpr bool isFirstAlternativeNothrowDefaultConstructible = - std::is_nothrow_default_constructible::value; - public: using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); - Variant() noexcept(isFirstAlternativeNothrowDefaultConstructible) = default; + Variant() = default; template = 1> Variant(const T &val) : m_variant(val) { From 1f69433b882ae7e4461909b799773cac9ba60acb Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 12 Aug 2025 13:47:39 -0700 Subject: [PATCH 26/30] Try this ugly solution for non default constructible --- include/aws/crt/Variant.h | 41 +++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 110200446..7a040e3ad 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -119,8 +119,23 @@ namespace Aws } // namespace VariantDebug #endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ + template class DefaultConstructibleVariant; + + template <> class DefaultConstructibleVariant + { + public: + explicit DefaultConstructibleVariant(int) {} + DefaultConstructibleVariant() = default; + }; + template <> class DefaultConstructibleVariant + { + public: + explicit DefaultConstructibleVariant(int) {} + DefaultConstructibleVariant() = delete; + }; + /* Depending on the Variant types, this struct either deletes special move members or defaults them. */ - template class MovableVariant; + template class MovableVariant; template <> class MovableVariant { @@ -142,7 +157,7 @@ namespace Aws }; /* Depending on the Variant types, this struct either deletes special copy members or defaults them. */ - template class CopyableVariant; + template class CopyableVariant; template <> class CopyableVariant { @@ -178,18 +193,17 @@ namespace Aws VariantDetail::Checker::HasType::type, Ts...>::value, int>::type; + public: using FirstAlternative = typename ThisVariantAlternative<0>::type; static constexpr bool isFirstAlternativeNothrowDefaultConstructible = std::is_nothrow_default_constructible::value; - public: using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); VariantImpl() noexcept(isFirstAlternativeNothrowDefaultConstructible) { - using FirstAlternative = typename ThisVariantAlternative<0>::type; new (m_storage) FirstAlternative(); m_index = 0; } @@ -658,7 +672,9 @@ namespace Aws * @tparam Ts Types of the variant value. */ template - class Variant : public VariantDetail::MovableVariant...>::value>, + class Variant : public VariantDetail::DefaultConstructibleVariant< + VariantDetail::VariantImpl::isFirstAlternativeNothrowDefaultConstructible>, + public VariantDetail::MovableVariant...>::value>, public VariantDetail::CopyableVariant...>::value> { /* Copyability and Movability depend only on constructors (copy and move correspondingly) of the @@ -674,24 +690,33 @@ namespace Aws using EnableIfOtherIsThisVariantAlternative = typename std:: enable_if::type, Ts...>::value, int>::type; + static constexpr bool isFirstAlternativeNothrowDefaultConstructible = + VariantDetail::VariantImpl::isFirstAlternativeNothrowDefaultConstructible; + public: using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); Variant() = default; - template = 1> Variant(const T &val) : m_variant(val) + template = 1> + Variant(const T &val) + : VariantDetail::DefaultConstructibleVariant(1), + m_variant(val) { } template = 1> - Variant(T &&val) : m_variant(std::forward(val)) + Variant(T &&val) + : VariantDetail::DefaultConstructibleVariant(1), + m_variant(std::forward(val)) { } template explicit Variant(Aws::Crt::InPlaceTypeT ipt, Args &&...args) - : m_variant(ipt, std::forward(args)...) + : VariantDetail::DefaultConstructibleVariant(1), + m_variant(ipt, std::forward(args)...) { } From dbb5d550d86f7c1c28dd6701d9ebbe78b34e82d4 Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Wed, 13 Aug 2025 09:35:36 -0700 Subject: [PATCH 27/30] Use sfinae with default ctor --- include/aws/crt/Variant.h | 46 +++++++++++++++------------------------ 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 7a040e3ad..8be361b36 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -119,21 +119,6 @@ namespace Aws } // namespace VariantDebug #endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ - template class DefaultConstructibleVariant; - - template <> class DefaultConstructibleVariant - { - public: - explicit DefaultConstructibleVariant(int) {} - DefaultConstructibleVariant() = default; - }; - template <> class DefaultConstructibleVariant - { - public: - explicit DefaultConstructibleVariant(int) {} - DefaultConstructibleVariant() = delete; - }; - /* Depending on the Variant types, this struct either deletes special move members or defaults them. */ template class MovableVariant; @@ -199,6 +184,9 @@ namespace Aws static constexpr bool isFirstAlternativeNothrowDefaultConstructible = std::is_nothrow_default_constructible::value; + static constexpr bool isFirstAlternativeDefaultConstructible = + std::is_nothrow_constructible::value; + using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); @@ -672,9 +660,7 @@ namespace Aws * @tparam Ts Types of the variant value. */ template - class Variant : public VariantDetail::DefaultConstructibleVariant< - VariantDetail::VariantImpl::isFirstAlternativeNothrowDefaultConstructible>, - public VariantDetail::MovableVariant...>::value>, + class Variant : public VariantDetail::MovableVariant...>::value>, public VariantDetail::CopyableVariant...>::value> { /* Copyability and Movability depend only on constructors (copy and move correspondingly) of the @@ -697,26 +683,30 @@ namespace Aws using IndexT = VariantDetail::Index::VariantIndex; static constexpr std::size_t AlternativeCount = sizeof...(Ts); - Variant() = default; + template < + typename T = typename VariantDetail::VariantImpl::FirstAlternative, + typename std::enable_if::value, bool>::type = true> + Variant() noexcept(isFirstAlternativeNothrowDefaultConstructible) + { + } + + template < + typename T = typename VariantDetail::VariantImpl::FirstAlternative, + typename std::enable_if::value, bool>::type = true> + Variant() = delete; - template = 1> - Variant(const T &val) - : VariantDetail::DefaultConstructibleVariant(1), - m_variant(val) + template = 1> Variant(const T &val) : m_variant(val) { } template = 1> - Variant(T &&val) - : VariantDetail::DefaultConstructibleVariant(1), - m_variant(std::forward(val)) + Variant(T &&val) : m_variant(std::forward(val)) { } template explicit Variant(Aws::Crt::InPlaceTypeT ipt, Args &&...args) - : VariantDetail::DefaultConstructibleVariant(1), - m_variant(ipt, std::forward(args)...) + : m_variant(ipt, std::forward(args)...) { } From dc5124cc3eebcaaf02f61b2dfbc4fd87032f2a3a Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Wed, 13 Aug 2025 15:24:26 -0700 Subject: [PATCH 28/30] Cleanup --- include/aws/crt/Variant.h | 6 +++--- tests/VariantTest.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index 8be361b36..fbf4467a8 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -665,9 +665,9 @@ namespace Aws { /* Copyability and Movability depend only on constructors (copy and move correspondingly) of the * underlying types. This means that a class with move constructor but with no move assignment operator - * might cause compilation issues. If such a type ever needs to be supported, two additional base classes - * need to be introduced: CopyAssignable and MoveAssignable. For now, it'll be overengineering. The same - * applies to copy constructor and copy assignment operator. */ + * might cause compilation issues. If such a type ever needs to be supported, additional base class needs + * to be introduced: MoveAssignable. For now, it'll be overengineering. + * The same applies to copy constructor and copy assignment operator. */ private: template using ThisVariantAlternative = VariantDetail::VariantAlternative; diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index ddcf2b15c..65fcf7ee5 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -333,7 +333,7 @@ static int s_VariantNothrowConstructible(struct aws_allocator *allocator, void * NothrowConstructibleTestType() noexcept = default; }; using NothrowConstructibleVariant = Aws::Crt::Variant; - ASSERT_INT_EQUALS(1, std::is_nothrow_constructible::value); + ASSERT_TRUE(std::is_nothrow_constructible::value); return AWS_OP_SUCCESS; } @@ -353,7 +353,7 @@ static int s_VariantThrowConstructible(struct aws_allocator *allocator, void *ct }; using ThrowConstructibleVariant = Aws::Crt::Variant; - ASSERT_INT_EQUALS(0, std::is_nothrow_constructible::value); + ASSERT_FALSE(std::is_nothrow_constructible::value); return AWS_OP_SUCCESS; } From ea23221285cf7a2892a7008177e176bb71012f8f Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Fri, 15 Aug 2025 11:26:29 -0700 Subject: [PATCH 29/30] More tests on exception safety --- include/aws/crt/Variant.h | 16 ++++++--- tests/CMakeLists.txt | 5 +-- tests/VariantTest.cpp | 73 ++++++++++++++++++++++++++++++++------- 3 files changed, 75 insertions(+), 19 deletions(-) diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index fbf4467a8..0a7fd6db6 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -210,7 +210,9 @@ namespace Aws VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor()); } - template = 1> VariantImpl(const T &val) + template = 1> + VariantImpl(const T &val) noexcept( + std::is_nothrow_constructible::type, decltype(val)>::value) { static_assert( VariantDetail::Checker::HasType::type, Ts...>::value, @@ -225,7 +227,9 @@ namespace Aws AWS_ASSERT(m_index != -1); } - template = 1> VariantImpl(T &&val) + template = 1> + VariantImpl(T &&val) noexcept( + std::is_nothrow_constructible::type, decltype(val)>::value) { static_assert( VariantDetail::Checker::HasType::type, Ts...>::value, @@ -695,12 +699,16 @@ namespace Aws typename std::enable_if::value, bool>::type = true> Variant() = delete; - template = 1> Variant(const T &val) : m_variant(val) + template = 1> + Variant(const T &val) noexcept( + std::is_nothrow_constructible::type, decltype(val)>::value) + : m_variant(val) { } template = 1> - Variant(T &&val) : m_variant(std::forward(val)) + Variant(T &&val) noexcept(std::is_nothrow_constructible::type, decltype(val)>::value) + : m_variant(std::forward(val)) { } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3ccecc58e..fc6743b4a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -109,8 +109,9 @@ add_test_case(VariantAssignmentOperator) add_test_case(VariantWithMoveOnlyUnderlyingType) add_test_case(VariantWithCopyOnlyUnderlyingType) add_test_case(VariantWithNoDefaultConstructibleUnderlyingType) -add_test_case(VariantNothrowConstructible) -add_test_case(VariantThrowConstructible) +add_test_case(VariantExceptionSafety_DefaultConstructor) +add_test_case(VariantExceptionSafety_MoveConstructor) +add_test_case(VariantExceptionSafety_CopyConstructor) add_test_case(VariantEmplace) add_test_case(VariantVisitor) add_test_case(StreamTestCreateDestroyWrapper) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 65fcf7ee5..e8dba41cc 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -322,43 +322,90 @@ static int s_VariantWithNoDefaultConstructibleUnderlyingType(struct aws_allocato AWS_TEST_CASE(VariantWithNoDefaultConstructibleUnderlyingType, s_VariantWithNoDefaultConstructibleUnderlyingType) -static int s_VariantNothrowConstructible(struct aws_allocator *allocator, void *ctx) +static int s_VariantExceptionSafety_DefaultConstructor(struct aws_allocator *allocator, void *ctx) { (void)ctx; Aws::Crt::ApiHandle apiHandle(allocator); - struct NothrowConstructibleTestType + struct NothrowDefaultConstructibleTestType { - NothrowConstructibleTestType() noexcept = default; + NothrowDefaultConstructibleTestType() noexcept = default; + NothrowDefaultConstructibleTestType(const NothrowDefaultConstructibleTestType &) noexcept(false) = default; + NothrowDefaultConstructibleTestType(NothrowDefaultConstructibleTestType &&) noexcept(false) = default; }; - using NothrowConstructibleVariant = Aws::Crt::Variant; - ASSERT_TRUE(std::is_nothrow_constructible::value); + + using NothrowDefaultConstructibleVariant = Aws::Crt::Variant; + + ASSERT_TRUE(std::is_nothrow_constructible::value); + ASSERT_FALSE(( + std::is_nothrow_constructible:: + value)); + ASSERT_FALSE( + (std::is_nothrow_constructible:: + value)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(VariantExceptionSafety_DefaultConstructor, s_VariantExceptionSafety_DefaultConstructor) + +static int s_VariantExceptionSafety_MoveConstructor(struct aws_allocator *allocator, void *ctx) +{ + (void)ctx; + + Aws::Crt::ApiHandle apiHandle(allocator); + + struct NothrowMoveConstructibleTestType + { + NothrowMoveConstructibleTestType(NothrowMoveConstructibleTestType &&) noexcept = default; + + NothrowMoveConstructibleTestType() noexcept(false) = default; + NothrowMoveConstructibleTestType(const NothrowMoveConstructibleTestType &) noexcept(false) = default; + }; + + using NothrowMoveConstructibleVariant = Aws::Crt::Variant; + + ASSERT_TRUE( + (std::is_nothrow_constructible::value)); + ASSERT_FALSE(std::is_nothrow_constructible::value); + ASSERT_FALSE( + (std::is_nothrow_constructible:: + value)); return AWS_OP_SUCCESS; } -AWS_TEST_CASE(VariantNothrowConstructible, s_VariantNothrowConstructible) +AWS_TEST_CASE(VariantExceptionSafety_MoveConstructor, s_VariantExceptionSafety_MoveConstructor) -static int s_VariantThrowConstructible(struct aws_allocator *allocator, void *ctx) +static int s_VariantExceptionSafety_CopyConstructor(struct aws_allocator *allocator, void *ctx) { (void)ctx; Aws::Crt::ApiHandle apiHandle(allocator); - struct ThrowConstructibleTestType + struct NothrowCopyConstructibleTestType { - // Must be user-defined to be non-nothrow. - ThrowConstructibleTestType() {} + NothrowCopyConstructibleTestType(const NothrowCopyConstructibleTestType &) noexcept = default; + + NothrowCopyConstructibleTestType() noexcept(false) = default; + NothrowCopyConstructibleTestType(NothrowCopyConstructibleTestType &&) noexcept(false) = default; }; - using ThrowConstructibleVariant = Aws::Crt::Variant; - ASSERT_FALSE(std::is_nothrow_constructible::value); + using NothrowCopyConstructibleVariant = Aws::Crt::Variant; + + ASSERT_TRUE( + (std::is_nothrow_constructible:: + value)); + + ASSERT_FALSE(std::is_nothrow_constructible::value); + ASSERT_FALSE( + (std::is_nothrow_constructible::value)); return AWS_OP_SUCCESS; } -AWS_TEST_CASE(VariantThrowConstructible, s_VariantThrowConstructible) +AWS_TEST_CASE(VariantExceptionSafety_CopyConstructor, s_VariantExceptionSafety_CopyConstructor) struct TestStringOnlyVisitor { From 351ab6e22001b46f2f2fd0f3525cfd13e8d7a1fa Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Fri, 15 Aug 2025 11:44:28 -0700 Subject: [PATCH 30/30] Dont use default members because of old gcc --- tests/VariantTest.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index e8dba41cc..80e6a7c8a 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -330,9 +330,9 @@ static int s_VariantExceptionSafety_DefaultConstructor(struct aws_allocator *all struct NothrowDefaultConstructibleTestType { - NothrowDefaultConstructibleTestType() noexcept = default; - NothrowDefaultConstructibleTestType(const NothrowDefaultConstructibleTestType &) noexcept(false) = default; - NothrowDefaultConstructibleTestType(NothrowDefaultConstructibleTestType &&) noexcept(false) = default; + NothrowDefaultConstructibleTestType() noexcept {} + NothrowDefaultConstructibleTestType(const NothrowDefaultConstructibleTestType &) noexcept(false) {} + NothrowDefaultConstructibleTestType(NothrowDefaultConstructibleTestType &&) noexcept(false) {} }; using NothrowDefaultConstructibleVariant = Aws::Crt::Variant; @@ -358,10 +358,10 @@ static int s_VariantExceptionSafety_MoveConstructor(struct aws_allocator *alloca struct NothrowMoveConstructibleTestType { - NothrowMoveConstructibleTestType(NothrowMoveConstructibleTestType &&) noexcept = default; + NothrowMoveConstructibleTestType(NothrowMoveConstructibleTestType &&) noexcept {} - NothrowMoveConstructibleTestType() noexcept(false) = default; - NothrowMoveConstructibleTestType(const NothrowMoveConstructibleTestType &) noexcept(false) = default; + NothrowMoveConstructibleTestType() noexcept(false) {} + NothrowMoveConstructibleTestType(const NothrowMoveConstructibleTestType &) noexcept(false) {} }; using NothrowMoveConstructibleVariant = Aws::Crt::Variant; @@ -386,10 +386,10 @@ static int s_VariantExceptionSafety_CopyConstructor(struct aws_allocator *alloca struct NothrowCopyConstructibleTestType { - NothrowCopyConstructibleTestType(const NothrowCopyConstructibleTestType &) noexcept = default; + NothrowCopyConstructibleTestType(const NothrowCopyConstructibleTestType &) noexcept {} - NothrowCopyConstructibleTestType() noexcept(false) = default; - NothrowCopyConstructibleTestType(NothrowCopyConstructibleTestType &&) noexcept(false) = default; + NothrowCopyConstructibleTestType() noexcept(false) {} + NothrowCopyConstructibleTestType(NothrowCopyConstructibleTestType &&) noexcept(false) {} }; using NothrowCopyConstructibleVariant = Aws::Crt::Variant;