diff --git a/include/aws/crt/Variant.h b/include/aws/crt/Variant.h index ed2886eb0..ff4997851 100644 --- a/include/aws/crt/Variant.h +++ b/include/aws/crt/Variant.h @@ -83,7 +83,7 @@ namespace Aws static const bool value = ContainsType(); }; } // namespace Checker -#if defined(DEBUG_BUILD) +#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG) namespace VariantDebug { template class VariantDebugBrowser @@ -93,7 +93,7 @@ namespace Aws std::tuple::type...> as_tuple; private: - template + template void InitTuple(char *storage) { First *value = reinterpret_cast(storage); @@ -101,14 +101,14 @@ namespace Aws InitTuple(storage); } - template void InitTuple(char *storage) + template void InitTuple(char *storage) { Last *value = reinterpret_cast(storage); std::get(as_tuple) = value; } }; } // namespace VariantDebug -#endif /* defined(DEBUG_BUILD) */ +#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ } // namespace VariantDetail template class VariantAlternative; @@ -124,20 +124,17 @@ namespace Aws */ template class Variant { - public: - static constexpr std::size_t AlternativeCount = sizeof...(Ts); - + private: template using ThisVariantAlternative = VariantAlternative; - using ThisVariantT = Variant; - template - using EnableIfOtherIsSameVariantType = - typename std::enable_if, ThisVariantT>::value, int>::type; - 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); + Variant() { using FirstAlternative = typename ThisVariantAlternative<0>::type; @@ -147,19 +144,19 @@ namespace Aws Variant(const Variant &other) { - auto otherIdx = other.m_index; - if (otherIdx != -1) + m_index = other.m_index; + if (other.m_index != -1) { - CopyConstructUtil<0, Ts...>(other); + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); } } Variant(Variant &&other) { - auto otherIdx = other.m_index; - if (otherIdx != -1) + m_index = other.m_index; + if (other.m_index != -1) { - MoveConstructUtil<0, Ts...>(std::move(other)); + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); } } @@ -169,12 +166,12 @@ namespace Aws VariantDetail::Checker::HasType::type, Ts...>::value, "This variant does not have such alternative T."); static_assert( - sizeof(T) >= STORAGE_SIZE, + 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(); + m_index = VariantDetail::Index::GetIndexOf(); AWS_ASSERT(m_index != -1); } @@ -189,7 +186,7 @@ namespace Aws using PlainT = typename std::decay::type; new (m_storage) PlainT(std::forward(val)); - m_index = VariantDetail::Index::GetIndexOf(); + m_index = VariantDetail::Index::GetIndexOf(); AWS_ASSERT(m_index != -1); } @@ -205,22 +202,24 @@ namespace Aws using PlainT = typename std::decay::type; new (m_storage) PlainT(std::forward(args)...); - m_index = VariantDetail::Index::GetIndexOf(); + m_index = VariantDetail::Index::GetIndexOf(); AWS_ASSERT(m_index != -1); } Variant &operator=(const Variant &other) { - if (this != &other) + AWS_ASSERT(other.m_index != -1); + if (this != &other && other.m_index != -1) { if (m_index != other.m_index) { Destroy(); - CopyConstructUtil<0, Ts...>(other); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); } else { - CopyAssignUtil<0, Ts...>(other); + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner()); } } return *this; @@ -228,16 +227,18 @@ namespace Aws Variant &operator=(Variant &&other) { - if (this != &other) + AWS_ASSERT(other.m_index != -1); + if (this != &other && other.m_index != -1) { if (m_index != other.m_index) { Destroy(); - MoveConstructUtil<0, Ts...>(std::move(other)); + m_index = other.m_index; + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor()); } else { - MoveAssignUtil<0, Ts...>(std::move(other)); + VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner()); }; } return *this; @@ -258,7 +259,7 @@ namespace Aws using PlainT = typename std::decay::type; new (m_storage) PlainT(std::forward(args)...); - m_index = VariantDetail::Index::GetIndexOf(); + m_index = VariantDetail::Index::GetIndexOf(); AWS_ASSERT(m_index != -1); T *value = reinterpret_cast(m_storage); @@ -271,7 +272,7 @@ namespace Aws static_assert(Index < AlternativeCount, "Unknown alternative index to emplace"); using AlternativeT = typename ThisVariantAlternative::type; - return emplace(args...); + return emplace(std::forward(args)...); } template = 1> bool holds_alternative() const @@ -280,13 +281,10 @@ namespace Aws return m_index == VariantDetail::Index::GetIndexOf(); } - template constexpr bool holds_alternative() const { return Index < AlternativeCount; } - - template bool holds_alternative() const { return false; } - /* non-const get */ template = 1> T &get() { + AWS_FATAL_ASSERT(holds_alternative()); T *value = reinterpret_cast(m_storage); return *value; } @@ -307,7 +305,7 @@ namespace Aws template auto get() -> typename ThisVariantAlternative::type & { static_assert(Index < AlternativeCount, "Unknown alternative index to get"); - AWS_ASSERT(Index == m_index); + AWS_FATAL_ASSERT(holds_alternative()); using AlternativeT = typename ThisVariantAlternative::type; AlternativeT *ret = reinterpret_cast(m_storage); return *ret; @@ -387,192 +385,192 @@ namespace Aws ~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]; - VariantDetail::Index::VariantIndex m_index = -1; -#if defined(DEBUG_BUILD) + IndexT m_index = -1; +#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG) VariantDetail::VariantDebug::VariantDebugBrowser browser = m_storage; -#endif /* defined(DEBUG_BUILD) */ - - void Destroy() - { - if (m_index != -1) - { - DestroyUtil(m_index); - } - m_index = -1; - } +#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */ - template - void DestroyUtil(const std::size_t requestedToDestroy, std::size_t curIndex = 0) - { - if (curIndex == requestedToDestroy) - { - First *value = reinterpret_cast(m_storage); - value->~First(); - } - else - { - DestroyUtil(requestedToDestroy, ++curIndex); - } - } + template constexpr bool holds_alternative() const { return Index == m_index; } - template void DestroyUtil(const std::size_t requestedToDestroy, std::size_t curIndex = 0) + struct Destroyer { - AWS_ASSERT( - requestedToDestroy == curIndex); // attempting to destroy unknown alternative type in a Variant - if (curIndex == requestedToDestroy) + template void operator()(AlternativeT &&value) const { - Last *value = reinterpret_cast(m_storage); - value->~Last(); + using PlaintT = typename std::remove_reference::type; + value.~PlaintT(); } - } + }; - template - void CopyConstructUtil(const Variant &other) + void Destroy() { - static_assert(Index < AlternativeCount, "Attempting to construct unknown Index Type"); + AWS_FATAL_ASSERT(m_index != -1); + Visit(Destroyer()); - if (Index == other.m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - new (m_storage) AlternativeT(other.get()); - m_index = Index; - AWS_ASSERT(m_index != -1); - } - else - { - CopyConstructUtil(other); - } + m_index = -1; } - template void CopyConstructUtil(const Variant &other) + struct CopyMoveConstructor { - static_assert(Index < AlternativeCount, "Attempting to construct unknown Index Type"); - - if (Index == other.m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - new (m_storage) AlternativeT(other.get()); - m_index = Index; - AWS_ASSERT(m_index != -1); - } - else + template void operator()(AlternativeT &&value, AlternativeT &&other) const { - AWS_ASSERT(!"Unknown variant alternative type in other!"); + using PlaintT = typename std::remove_reference::type; + new (&value) PlaintT(std::move(other)); } - } - - template - void MoveConstructUtil(Variant &&other) - { - static_assert(Index < AlternativeCount, "Attempting to construct unknown Index Type"); - if (Index == other.m_index) + template + void operator()(AlternativeT &&value, ConstAlternativeT &other) const { - using AlternativeT = typename ThisVariantAlternative::type; - new (m_storage) AlternativeT(std::move(other.get())); - m_index = Index; - AWS_ASSERT(m_index != -1); - } - else - { - MoveConstructUtil(std::move(other)); + 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 void MoveConstructUtil(Variant &&other) + struct CopyMoveAssigner { - static_assert(Index < AlternativeCount, "Attempting to construct unknown Index Type"); - - if (Index == other.m_index) + template void operator()(AlternativeT &&value, AlternativeT &&other) const { - using AlternativeT = typename ThisVariantAlternative::type; - new (m_storage) AlternativeT(std::move(other.get())); - m_index = Index; - AWS_ASSERT(m_index != -1); + value = std::move(other); } - else + + template + void operator()(AlternativeT &&value, ConstAlternativeT &other) const { - AWS_ASSERT(!"Unknown variant alternative type in other!"); + using PlaintT = typename std::remove_reference::type; + using PlaintOtherT = + typename std::remove_const::type>::type; + static_assert(std::is_same::value, "Incompatible types"); + + value = std::move(other); } - } + }; - template - void CopyAssignUtil(const Variant &other) - { - static_assert(Index < AlternativeCount, "Attempting to construct unknown Index Type"); + template struct VisitorUtil; - if (Index == other.m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(m_storage); - *value = other.get(); - m_index = Index; - AWS_ASSERT(m_index != -1); - } - else + template + struct VisitorUtil + { + template static void Visit(Variant *pThis, VisitorStruct &&visitor) { - CopyAssignUtil(other); - } - } + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - template void CopyAssignUtil(const Variant &other) - { - static_assert(Index < AlternativeCount, "Attempting to construct unknown Index Type"); + if (Index == pThis->m_index) + { + using AlternativeT = typename ThisVariantAlternative::type; + AlternativeT *value = reinterpret_cast(pThis->m_storage); + visitor(*value); + } + else + { + VisitorUtil::Visit(pThis, std::forward(visitor)); + } + } - if (Index == other.m_index) + template + static void VisitBinary(Variant *pThis, Variant &&other, VisitorStruct &&visitor) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(m_storage); - *value = other.get(); - m_index = Index; - AWS_ASSERT(m_index != -1); + 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); + AlternativeT &&otherValue = other.get(); + visitor(*value, std::move(otherValue)); + } + else + { + VisitorUtil::Visit( + pThis, std::move>(other), std::forward(visitor)); + } } - else + + template + static void VisitBinary(Variant *pThis, const Variant &other, VisitorStruct &&visitor) { - AWS_ASSERT(!"Unknown variant alternative type in other!"); + 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::VisitBinary( + pThis, other, std::forward(visitor)); + } } - } + }; - template - void MoveAssignUtil(Variant &&other) + template struct VisitorUtil { - static_assert(Index < AlternativeCount, "Attempting to construct unknown Index Type"); - - if (Index == other.m_index) - { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(m_storage); - *value = std::move(other.get()); - m_index = Index; - AWS_ASSERT(m_index != -1); - } - else + template static void Visit(Variant *pThis, VisitorStruct &&visitor) { - MoveAssignUtil(std::move(other)); - } - } + static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type"); - template void MoveAssignUtil(Variant &&other) - { - static_assert(Index < AlternativeCount, "Attempting to construct 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 == other.m_index) + template + static void VisitBinary(Variant *pThis, Variant &&other, VisitorStruct &&visitor) { - using AlternativeT = typename ThisVariantAlternative::type; - AlternativeT *value = reinterpret_cast(m_storage); - *value = std::move(other.get()); - m_index = Index; - AWS_ASSERT(m_index != -1); + 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); + AlternativeT &&otherValue = other.get(); + visitor(*value, otherValue); + } + else + { + AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!"); + } } - else + + template + static void VisitBinary(Variant *pThis, const Variant &other, VisitorStruct &&visitor) { - AWS_ASSERT(!"Unknown variant alternative type in other!"); + 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 */ diff --git a/tests/VariantTest.cpp b/tests/VariantTest.cpp index 2baff2f2c..d8d62b5c6 100644 --- a/tests/VariantTest.cpp +++ b/tests/VariantTest.cpp @@ -208,6 +208,32 @@ static int s_VariantOperatorEquals(struct aws_allocator *allocator, void *ctx) AWS_TEST_CASE(VariantOperatorEquals, s_VariantOperatorEquals) +struct TestStringOnlyVisitor +{ + /* can't specialize member function templates, so using such syntax of dummy structs */ + template struct MyVisitUtil + { + static void Visit(Args &...args) + { + ; // not a string + } + }; + + template void operator()(AlternativeT &val) const + { + MyVisitUtil::type>::Visit(val); + } +}; + +template <> struct TestStringOnlyVisitor::MyVisitUtil +{ + static void Visit(Aws::Crt::String &val) + { + auto index = val.find("another"); + val.replace(index, 7, "visited"); + } +}; + static int s_VariantEmplace(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -223,8 +249,17 @@ static int s_VariantEmplace(struct aws_allocator *allocator, void *ctx) var1.emplace(65535); ASSERT_INT_EQUALS(65535, var1.get()); + var1.emplace<0>(1337); + ASSERT_INT_EQUALS(1337, var1.get()); + var1.emplace(Aws::Crt::String("This is a string.")); ASSERT_STR_EQUALS("This is a string.", var1.get().c_str()); + + var1.emplace<2>(Aws::Crt::String("This is another string.")); + ASSERT_STR_EQUALS("This is another string.", var1.get().c_str()); + + var1.Visit(TestStringOnlyVisitor()); + ASSERT_STR_EQUALS("This is visited string.", var1.get().c_str()); } }