Merge branch 'development' into feature/update-builtin-detection-and-etl-traits

This commit is contained in:
John Wellbelove 2026-10-12 16:31:34 +02:00 committed by GitHub
commit 5624ec0461
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 206 additions and 52 deletions

View File

@ -94,8 +94,8 @@ namespace etl
static void destroy(char*, size_t) {}
static void copy(char*, const char*, size_t) {}
static void move(char*, const char*, size_t) {}
static void copy_assign(char*, const char*, size_t) {}
static void move_assign(char*, const char*, size_t) {}
static void copy_assign_or_construct(char*, const char*, size_t, bool) {}
static void move_assign_or_construct(char*, const char*, size_t, bool) {}
};
// Recursive case.
@ -148,27 +148,49 @@ namespace etl
}
}
static void copy_assign(char* dst, const char* src, size_t type_id)
//*************************************************************************
// copy_assign_or_construct / move_assign_or_construct
//
// One dispatch on the source alternative serves both halves of variant
// assignment: the alternative is assigned when the destination already
// holds it, otherwise it is constructed into storage the caller has
// already emptied. Sharing the dispatch keeps variant assignment small.
//*************************************************************************
static void copy_assign_or_construct(char* dst, const char* src, size_t type_id, bool construct)
{
if (type_id == Index)
{
if (construct)
{
copy_impl(dst, src, etl::integral_constant<bool, etl::is_copy_constructible<THead>::value>{});
}
else
{
copy_assign_impl(dst, src, etl::integral_constant<bool, etl::is_copy_assignable<THead>::value>{});
}
}
else
{
variant_operations<Index + 1, TRest...>::copy_assign(dst, src, type_id);
variant_operations<Index + 1, TRest...>::copy_assign_or_construct(dst, src, type_id, construct);
}
}
static void move_assign(char* dst, const char* src, size_t type_id)
static void move_assign_or_construct(char* dst, const char* src, size_t type_id, bool construct)
{
if (type_id == Index)
{
move_assign_impl(dst, src, etl::integral_constant<bool, etl::is_move_assignable<THead>::value>{});
if (construct)
{
move_impl(dst, src, etl::integral_constant<bool, etl::is_move_constructible<THead>::value>{});
}
else
{
variant_operations<Index + 1, TRest...>::move_assign(dst, src, type_id);
move_assign_impl(dst, src, etl::integral_constant<bool, etl::is_move_assignable<THead>::value>{});
}
}
else
{
variant_operations<Index + 1, TRest...>::move_assign_or_construct(dst, src, type_id, construct);
}
}
@ -521,13 +543,7 @@ namespace etl
{
if (this != &other)
{
if ((type_id != variant_npos) && (type_id == other.type_id))
{
variant_operations<0, TTypes...>::copy_assign(data, other.data, type_id);
}
else
{
if (type_id != variant_npos)
if ((type_id != variant_npos) && (type_id != other.type_id))
{
variant_operations<0, TTypes...>::destroy(data, type_id);
type_id = variant_npos;
@ -535,11 +551,10 @@ namespace etl
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::copy(data, other.data, other.type_id);
variant_operations<0, TTypes...>::copy_assign_or_construct(data, other.data, other.type_id, type_id == variant_npos);
type_id = other.type_id;
}
}
}
return *this;
}
@ -549,13 +564,7 @@ namespace etl
{
if (this != &other)
{
if ((type_id != variant_npos) && (type_id == other.type_id))
{
variant_operations<0, TTypes...>::move_assign(data, other.data, type_id);
}
else
{
if (type_id != variant_npos)
if ((type_id != variant_npos) && (type_id != other.type_id))
{
variant_operations<0, TTypes...>::destroy(data, type_id);
type_id = variant_npos;
@ -563,11 +572,10 @@ namespace etl
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::move(data, other.data, other.type_id);
variant_operations<0, TTypes...>::move_assign_or_construct(data, other.data, other.type_id, type_id == variant_npos);
type_id = other.type_id;
}
}
}
return *this;
}
@ -687,13 +695,7 @@ namespace etl
void copy_assign_from(const variant_trivially_destructible_base& other)
{
if ((type_id != variant_npos) && (type_id == other.type_id))
{
variant_operations<0, TTypes...>::copy_assign(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), type_id);
return;
}
if (type_id != variant_npos)
if ((type_id != variant_npos) && (type_id != other.type_id))
{
variant_operations<0, TTypes...>::destroy(reinterpret_cast<char*>(&data), type_id);
type_id = variant_npos;
@ -701,20 +703,15 @@ namespace etl
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::copy(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
variant_operations<0, TTypes...>::copy_assign_or_construct(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data),
other.type_id, type_id == variant_npos);
type_id = other.type_id;
}
}
void move_assign_from(variant_trivially_destructible_base& other)
{
if ((type_id != variant_npos) && (type_id == other.type_id))
{
variant_operations<0, TTypes...>::move_assign(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), type_id);
return;
}
if (type_id != variant_npos)
if ((type_id != variant_npos) && (type_id != other.type_id))
{
variant_operations<0, TTypes...>::destroy(reinterpret_cast<char*>(&data), type_id);
type_id = variant_npos;
@ -722,7 +719,8 @@ namespace etl
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::move(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
variant_operations<0, TTypes...>::move_assign_or_construct(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data),
other.type_id, type_id == variant_npos);
type_id = other.type_id;
}
}

View File

@ -543,6 +543,30 @@ namespace
bool copy_assigned;
bool move_assigned;
};
//*********************************************
// Counts destructor calls, to verify that variant assignment destroys the
// previous alternative only when the alternative changes.
struct DestructorCounter
{
DestructorCounter() {}
DestructorCounter(const DestructorCounter&) {}
~DestructorCounter()
{
++destructor_calls;
}
DestructorCounter& operator=(const DestructorCounter&)
{
return *this;
}
static int destructor_calls;
};
int DestructorCounter::destructor_calls = 0;
} // namespace
// Moved from the top of the file otherwise clang has issues with
@ -1160,6 +1184,73 @@ namespace
CHECK_EQUAL(etl::variant_npos, v.index());
}
}
//*************************************************************************
// Assigning a different alternative whose copy constructor throws must
// leave the variant valueless-after-exception.
struct ThrowOnCopy
{
struct exception
{
};
ThrowOnCopy() {}
ThrowOnCopy(const ThrowOnCopy&)
{
throw exception();
}
ThrowOnCopy& operator=(const ThrowOnCopy&)
{
return *this;
}
};
TEST(test_copy_assign_throwing_different_type_is_valueless_by_exception)
{
// Trivially destructible suite (variadic_union storage).
{
etl::variant<int, ThrowOnCopy> v1(etl::in_place_type_t<int>{}, 42);
etl::variant<int, ThrowOnCopy> v2;
v2.emplace<ThrowOnCopy>();
bool threw = false;
try
{
v1 = v2;
}
catch (const ThrowOnCopy::exception&)
{
threw = true;
}
CHECK(threw);
CHECK(v1.valueless_by_exception());
CHECK_EQUAL(etl::variant_npos, v1.index());
}
// Non-trivially destructible suite (uninitialized_buffer storage).
{
etl::variant<std::string, ThrowOnCopy> v1(etl::in_place_type_t<std::string>{}, "Some Text");
etl::variant<std::string, ThrowOnCopy> v2;
v2.emplace<ThrowOnCopy>();
bool threw = false;
try
{
v1 = v2;
}
catch (const ThrowOnCopy::exception&)
{
threw = true;
}
CHECK(threw);
CHECK(v1.valueless_by_exception());
CHECK_EQUAL(etl::variant_npos, v1.index());
}
}
#endif
//*************************************************************************
@ -1324,6 +1415,71 @@ namespace
CHECK(etl::get<TrivialAssignTracker>(variant_1).move_assigned);
}
//*************************************************************************
TEST(test_move_assign_different_type_reconstructs)
{
typedef etl::variant<int, AssignTracker> tracker_variant;
tracker_variant variant_1(etl::in_place_type_t<int>{}, 5);
tracker_variant variant_2(etl::in_place_type_t<AssignTracker>{}, 2);
variant_1 = etl::move(variant_2);
CHECK_EQUAL(1U, variant_1.index());
CHECK_EQUAL(2, etl::get<AssignTracker>(variant_1).value);
CHECK(etl::get<AssignTracker>(variant_1).constructed);
CHECK(!etl::get<AssignTracker>(variant_1).copy_assigned);
CHECK(!etl::get<AssignTracker>(variant_1).move_assigned);
}
//*************************************************************************
TEST(test_assign_different_type_reconstructs_trivial)
{
typedef etl::variant<int, TrivialAssignTracker> tracker_variant;
tracker_variant variant_1(etl::in_place_type_t<int>{}, 5);
tracker_variant variant_2(etl::in_place_type_t<TrivialAssignTracker>{}, 2);
variant_1 = variant_2;
CHECK_EQUAL(1U, variant_1.index());
CHECK_EQUAL(2, etl::get<TrivialAssignTracker>(variant_1).value);
CHECK(!etl::get<TrivialAssignTracker>(variant_1).copy_assigned);
CHECK(!etl::get<TrivialAssignTracker>(variant_1).move_assigned);
tracker_variant variant_3(etl::in_place_type_t<int>{}, 6);
tracker_variant variant_4(etl::in_place_type_t<TrivialAssignTracker>{}, 4);
variant_3 = etl::move(variant_4);
CHECK_EQUAL(1U, variant_3.index());
CHECK_EQUAL(4, etl::get<TrivialAssignTracker>(variant_3).value);
CHECK(!etl::get<TrivialAssignTracker>(variant_3).copy_assigned);
CHECK(!etl::get<TrivialAssignTracker>(variant_3).move_assigned);
}
//*************************************************************************
TEST(test_assign_destroys_previous_alternative_only_when_it_changes)
{
typedef etl::variant<DestructorCounter, int> counter_variant;
counter_variant variant_1(etl::in_place_type_t<DestructorCounter>{});
counter_variant variant_2(etl::in_place_type_t<DestructorCounter>{});
counter_variant variant_3(etl::in_place_type_t<int>{}, 3);
DestructorCounter::destructor_calls = 0;
variant_1 = variant_2;
CHECK_EQUAL(0, DestructorCounter::destructor_calls);
CHECK_EQUAL(0U, variant_1.index());
variant_1 = variant_3;
CHECK_EQUAL(1, DestructorCounter::destructor_calls);
CHECK_EQUAL(1U, variant_1.index());
CHECK_EQUAL(3, etl::get<int>(variant_1));
}
//*************************************************************************
// Issue #1567: the variant propagates trivial copyability from its
// alternatives, as required by P0602R4.