diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 137e745..21a7d28 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -65,6 +65,15 @@ jobs: } ], "clang": [ + { "versions": ["trunk"], + "tests": [ + { "cxxversions": ["c++26"], + "tests": [ + {"stdlibs": ["libstdc++", "libc++"], "tests": ["Release.Default"]} + ] + } + ] + }, { "versions": ["23"], "tests": [ {"cxxversions": ["c++26"], diff --git a/include/beman/inplace_vector/config.hpp b/include/beman/inplace_vector/config.hpp index 01aa0ca..43f2649 100644 --- a/include/beman/inplace_vector/config.hpp +++ b/include/beman/inplace_vector/config.hpp @@ -10,4 +10,12 @@ #define BEMAN_INPLACE_VECTOR_NO_EXCEPTIONS() 0 #endif +#ifndef BEMAN_INPLACE_VECTOR_HAS_TRIVIAL_UNION +#if defined(__cpp_trivial_union) && __cpp_trivial_union >= 202602L +#define BEMAN_INPLACE_VECTOR_HAS_TRIVIAL_UNION 1 +#else +#define BEMAN_INPLACE_VECTOR_HAS_TRIVIAL_UNION 0 +#endif +#endif + #endif diff --git a/include/beman/inplace_vector/inplace_vector.hpp b/include/beman/inplace_vector/inplace_vector.hpp index 20ce557..50f839d 100644 --- a/include/beman/inplace_vector/inplace_vector.hpp +++ b/include/beman/inplace_vector/inplace_vector.hpp @@ -69,7 +69,12 @@ concept satisfy_triviality = std::is_trivially_copyable_v && std::is_trivially_destructible_v; template -concept satisfy_constexpr = N == 0 || satisfy_triviality; +concept satisfy_constexpr = +#if BEMAN_INPLACE_VECTOR_HAS_TRIVIAL_UNION + true; +#else + N == 0 || satisfy_triviality; +#endif template concept lessthan_comparable = requires(const T &a, const T &b) { @@ -140,6 +145,7 @@ template struct trivial { // which makes inplace_vector of non-trivial types non-constexpr-friendly. // // Note: This is not used if trivial union is supported. +#if !BEMAN_INPLACE_VECTOR_HAS_TRIVIAL_UNION template struct raw_byte_based_storage { alignas(T) std::byte _d[sizeof(T) * N]; T *storage_data(size_t i) noexcept { @@ -193,13 +199,60 @@ template struct non_trivial { std::destroy(storage_data(), storage_data() + storage_size()); } }; +#else +template struct union_storage { + static_assert(!satisfy_triviality, + "use storage::trivial for Trivial elements"); + static_assert(N != size_t{0}, "use storage::zero for N==0"); + +protected: + using size_type = smallest_size_t; + +private: + union { + T storage_[N]; + }; + size_type storage_size_ = 0; + +protected: + constexpr const T *storage_data() const noexcept { return storage_; } + constexpr auto storage_data() noexcept { return storage_; } + constexpr size_type storage_size() const noexcept { return storage_size_; } + constexpr void unsafe_set_size(size_t new_size) noexcept { + IV_EXPECT(size_type(new_size) <= N && "new_size out-of-bounds [0, N)"); + storage_size_ = size_type(new_size); + } + +public: + constexpr union_storage() noexcept { std::start_lifetime(storage_); } + constexpr union_storage(const union_storage &) noexcept = default; + constexpr union_storage &operator=(const union_storage &) noexcept = default; + constexpr union_storage(union_storage &&) noexcept = default; + constexpr union_storage &operator=(union_storage &&) noexcept = default; + + constexpr ~union_storage() + requires(std::is_trivially_destructible_v) + = default; + constexpr ~union_storage() + requires(!std::is_trivially_destructible_v) + { + std::destroy(storage_data(), storage_data() + storage_size()); + } +}; +#endif // Selects the vector storage. template -using storage_for = std::conditional_t< - !satisfy_constexpr, non_trivial, - std::conditional_t, trivial>>; - +using storage_for = +#if BEMAN_INPLACE_VECTOR_HAS_TRIVIAL_UNION + std::conditional_t, + std::conditional_t, trivial, + union_storage>>; +#else + std::conditional_t< + !satisfy_constexpr, non_trivial, + std::conditional_t, trivial>>; +#endif } // namespace storage template diff --git a/tests/beman/inplace_vector/constexpr.test.cpp b/tests/beman/inplace_vector/constexpr.test.cpp index ace31b2..e472bab 100644 --- a/tests/beman/inplace_vector/constexpr.test.cpp +++ b/tests/beman/inplace_vector/constexpr.test.cpp @@ -45,6 +45,34 @@ static_assert(!has_constexpr_support>); static_assert(has_constexpr_support, 0>>); static_assert(!has_constexpr_support, 50>>); +constexpr bool test_non_trivial_constexpr() { + inplace_vector v; + + // Construction and emplacement in constant evaluation. + v.emplace_back("hello"); + v.emplace_back("world"); + + if (v.size() != 2) + return false; + + if (v[0] != "hello" || v[1] != "world") + return false; + + // Modification of an existing element. + v[0] = "goodbye"; + + if (v[0] != "goodbye") + return false; + + // Destruction of the vector with live non-trivial elements is + // also exercised when leaving this function. + return true; +} + +#if BEMAN_INPLACE_VECTOR_HAS_TRIVIAL_UNION +static_assert(test_non_trivial_constexpr()); +#endif + #define TEST(NAME) \ static_assert(std::invoke([]() { \ NAME>(); \