diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 8fe6056..65b2ccc 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -37,7 +37,7 @@ jobs: c: gcc - cpp: clang++ c: clang - cpp_version: [23] + cpp_version: [20, 23] cmake_args: - description: "Default" args: "" @@ -50,15 +50,7 @@ jobs: compiler: cpp: g++ c: gcc - cpp_version: 23 - cmake_args: - description: "Werror" - args: "-DCMAKE_CXX_FLAGS='-Wall -Wextra -Wpedantic -Werror'" - - platform: ubuntu-24.04 - compiler: - cpp: g++ - c: gcc - cpp_version: 23 + cpp_version: 20 cmake_args: description: "Dynamic" cmake_args: "-DBUILD_SHARED_LIBS=on" diff --git a/CMakeLists.txt b/CMakeLists.txt index e7d6744..6c6a78f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ option( ${PROJECT_IS_TOP_LEVEL} ) +include(FetchContent) include(GNUInstallDirs) add_library(beman.inplace_vector INTERFACE) @@ -59,6 +60,22 @@ install( if(BEMAN_INPLACE_VECTOR_BUILD_TESTS) include(CTest) + enable_testing() + + # Fetch GoogleTest + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG + f8d7d77c06936315286eb55f8de22cd23c188571 # release-1.14.0 + EXCLUDE_FROM_ALL + ) + + block() + set(INSTALL_GTEST OFF) # Disable GoogleTest installation + FetchContent_MakeAvailable(googletest) + endblock() + add_subdirectory(tests/beman/inplace_vector) endif() diff --git a/include/beman/inplace_vector/inplace_vector.hpp b/include/beman/inplace_vector/inplace_vector.hpp index 7aa037f..6ee058e 100644 --- a/include/beman/inplace_vector/inplace_vector.hpp +++ b/include/beman/inplace_vector/inplace_vector.hpp @@ -312,7 +312,7 @@ template [[noreturn]] static constexpr void __assert_failure(char const *__file, int __line, char const *__msg) { - if consteval { + if (std::is_constant_evaluated()) { throw __msg; // TODO: std lib implementer, do better here } else { fprintf(stderr, "%s(%d): %s\n", __file, __line, __msg); @@ -459,7 +459,11 @@ template struct __non_trivial { constexpr __non_trivial &operator=(__non_trivial const &) noexcept = default; constexpr __non_trivial(__non_trivial &&) noexcept = default; constexpr __non_trivial &operator=(__non_trivial &&) noexcept = default; - constexpr ~__non_trivial() = default; + + constexpr ~__non_trivial() + requires(is_trivially_destructible_v<__T>) + = default; + constexpr ~__non_trivial() { destroy(__data(), __data() + __size()); } }; // Selects the vector storage. @@ -500,7 +504,7 @@ struct inplace_vector : private __iv_detail::__storage::_t<__T, __N> { using const_reverse_iterator = ::std::reverse_iterator; // [containers.sequences.inplace_vector.cons], construct/copy/destroy - constexpr inplace_vector() noexcept { __unsafe_set_size(0); } + constexpr inplace_vector() noexcept = default; // constexpr explicit inplace_vector(size_type __n); // constexpr inplace_vector(size_type __n, const __T& __value); // template // BUGBUG: why not model input_iterator? @@ -694,11 +698,12 @@ struct inplace_vector : private __iv_detail::__storage::_t<__T, __N> { } template - constexpr void emplace_back(__Args &&...__args) + constexpr __T &emplace_back(__Args &&...__args) requires(constructible_from<__T, __Args...>) { if (!try_emplace_back(::std::forward<__Args>(__args)...)) [[unlikely]] throw bad_alloc(); + return back(); } constexpr __T &push_back(const __T &__x) requires(constructible_from<__T, const __T &>) @@ -769,7 +774,8 @@ struct inplace_vector : private __iv_detail::__storage::_t<__T, __N> { movable<__T>) { __assert_iterator_in_range(__position); - __assert_valid_iterator_pair(__first, __last); + // __assert_valid_iterator_pair(__first, __last); // does not work with + // arbitrary InputIterators if constexpr (random_access_iterator<__InputIterator>) { if (size() + static_cast(distance(__first, __last)) > capacity()) [[unlikely]] @@ -934,27 +940,60 @@ struct inplace_vector : private __iv_detail::__storage::_t<__T, __N> { } constexpr inplace_vector(const inplace_vector &__x) - requires(copyable<__T>) + requires(__N == 0 || is_trivially_copy_constructible_v<__T>) + = default; + + constexpr inplace_vector(const inplace_vector &__x) + requires(__N != 0 && !is_trivially_copy_constructible_v<__T> && + copyable<__T>) { for (auto &&__e : __x) emplace_back(__e); } + constexpr inplace_vector(inplace_vector &&__x) - requires(movable<__T>) + requires(__N == 0 || is_trivially_move_constructible_v<__T>) + = default; + + constexpr inplace_vector(inplace_vector &&__x) + requires(__N != 0 && !is_trivially_move_constructible_v<__T> && + movable<__T>) { for (auto &&__e : __x) emplace_back(::std::move(__e)); } + constexpr inplace_vector &operator=(const inplace_vector &__x) - requires(copyable<__T>) + requires(__N == 0 || (std::is_trivially_destructible_v<__T> && + std::is_trivially_copy_constructible_v<__T> && + std::is_trivially_copy_assignable_v<__T>)) + = default; + + constexpr inplace_vector &operator=(const inplace_vector &__x) + requires(__N != 0 && + !(std::is_trivially_destructible_v<__T> && + std::is_trivially_copy_constructible_v<__T> && + std::is_trivially_copy_assignable_v<__T>) && + copyable<__T>) { clear(); for (auto &&__e : __x) emplace_back(__e); return *this; } + constexpr inplace_vector &operator=(inplace_vector &&__x) - requires(movable<__T>) + requires(__N == 0 || (std::is_trivially_destructible_v<__T> && + std::is_trivially_move_constructible_v<__T> && + std::is_trivially_move_assignable_v<__T>)) + = default; + + constexpr inplace_vector &operator=(inplace_vector &&__x) + requires(__N != 0 && + !(std::is_trivially_destructible_v<__T> && + std::is_trivially_move_constructible_v<__T> && + std::is_trivially_move_assignable_v<__T>) && + movable<__T>) { clear(); for (auto &&__e : __x) @@ -985,7 +1024,7 @@ struct inplace_vector : private __iv_detail::__storage::_t<__T, __N> { requires(constructible_from<__T, ranges::range_reference_t<__R>> && movable<__T>) { - assign(begin(__rg), end(__rg)); + assign(std::begin(__rg), std::end(__rg)); } constexpr void assign(size_type __n, const __T &__u) requires(constructible_from<__T, const __T &> && movable<__T>) diff --git a/tests/beman/inplace_vector/CMakeLists.txt b/tests/beman/inplace_vector/CMakeLists.txt index 9b10181..24126a6 100644 --- a/tests/beman/inplace_vector/CMakeLists.txt +++ b/tests/beman/inplace_vector/CMakeLists.txt @@ -20,3 +20,23 @@ add_test( NAME beman.inplace_vector.ref-test COMMAND beman.inplace_vector.ref-test ) + +# GoogleTest based tests +include(GoogleTest) + +function(add_gtest NAME) + add_executable(beman.inplace_vector.tests.${NAME}) + target_sources(beman.inplace_vector.tests.${NAME} PRIVATE ${NAME}.test.cpp) + target_link_libraries( + beman.inplace_vector.tests.${NAME} + PRIVATE beman.inplace_vector GTest::gtest GTest::gtest_main + ) + gtest_add_tests(beman.inplace_vector.tests.${NAME} "" AUTO) +endfunction() + +# Tests for official specs +add_gtest(container_requirements) +add_gtest(triviality) +add_gtest(constructors) +add_gtest(size_n_data) +add_gtest(erasure) diff --git a/tests/beman/inplace_vector/README.md b/tests/beman/inplace_vector/README.md new file mode 100644 index 0000000..97cd8ee --- /dev/null +++ b/tests/beman/inplace_vector/README.md @@ -0,0 +1,109 @@ +# Inplace Vector Testing Suite + +This folder contains tests for `inplace_vector` implementation. + +The aim for the test cases are to keep the implementation in-check with its standing in the latest C++ draft. + +You can checkout `inplace_vector`'s current state in C++ draft [here](https://eel.is/c++draft/inplace.vector). + +## C++ Draft paragraph to test file + +`inplace_vector`'s chapter number in the latest C++ draft is **23.3.14**. + +### Overview (23.3.14.1) + +[Overview](https://eel.is/c++draft/inplace.vector#overview) has 6 clauses. + +#### 6.1 Overview + +> An inplace_vector is a contiguous container. Its capacity is fixed and its +elements are stored within the inplace_vector object itself. + +This is not testable. + +#### 6.2 Container Requirements + +> An inplace_vector meets all of the requirements of a container ([container.reqmts]), +> of a reversible container ([container.rev.reqmts]), of a contiguous container, +> and of a sequence container, including most of the optional sequence +container requirements ([sequence.reqmts]). The exceptions are the push_front, +> prepend_range, pop_front, and emplace_front member functions, +> which are not provided. +> Descriptions are provided here only for operations on inplace_vector that +> are not described in one of these tables or for operations where there is +> additional semantic information. + +See [container_requirements.test.cpp](container_requirements.test.cpp). + +#### 6.3 Constexpr Iterator + +> For any N, `inplace_vector​::​iterator` and +> `inplace_vector​::​const_iterator` meet the constexpr iterator +> requirements. + +Not tested for now. + +#### 6.4 Constexpr member functions + +> For any $N>0$, if T is not trivially copyable or +> `is_trivially_default_constructible_v` is false, +> then no `inplace_vector` member functions are usable in +> constant expressions. + +Not tested for now. + +#### 6.5 Bad alloc requirement + +> Any member function of `inplace_vector` that would cause the size to +> exceed N throws an exception of type bad_alloc. + +These are tested with individual functions. + +#### 6.6 Triviality + +Let IV denote a specialization of `inplace_vector`. +> If N is zero, then IV is trivially copyable and empty, +> and std​::​is_trivially_default_constructible_v is true. +> (Sub-clauses omitted) + +See [triviality.test.cpp](triviality.test.cpp) + +### Constructors (23.3.14.2) + +See [constructors.test.cpp](constructors.test.cpp) + +### Size and capacity (23.3.14.3) + +See [size_n_data.test.cpp](size_n_data.test.cpp) + +### Data (23.3.14.4) + +See [size_n_data.test.cpp](size_n_data.test.cpp) + +### Modifiers (23.3.14.5) + +See [modifiers.test.cpp](modifiers.test.cpp) + +### Erasure (23.3.14.6) + +See [erasure.test.cpp](erasure.test.cpp) + +## Other tests + +- [ref_impl.test.cpp](ref_impl.test.cpp): + Is the test suite imported from reference implementation in + [P0843R14](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p0843r14.html). + Originally included [here on godbolt](https://godbolt.org/z/5P78aG5xE). +- [inplace_vector.test.cpp](inplace_vector.test.cpp): + A minimal test suite by @Hels15 for their implementation. + +## Known Issues/ Missed Tests + +- Constexpr related functionalities. +- Emplacement minimal copy/ construction. +- Exception safety on mutation. + +## Special Thanks + +Special thanks to Jan Babst (@jbab) for his contribution at setting up the +Google Test infrastructure. diff --git a/tests/beman/inplace_vector/constructors.test.cpp b/tests/beman/inplace_vector/constructors.test.cpp new file mode 100644 index 0000000..00c21a4 --- /dev/null +++ b/tests/beman/inplace_vector/constructors.test.cpp @@ -0,0 +1,139 @@ +#include +#include +#include + +#include "gtest_setup.hpp" + +namespace { +// 23.3.14.2 Constructors [inplace.vector.cons] + +template class Constructors : public IVBasicTest {}; +TYPED_TEST_SUITE(Constructors, IVAllTypes); + +TYPED_TEST(Constructors, SizedDefault) { + // constexpr explicit inplace_vector(size_type n); + // Preconditions: T is Cpp17DefaultInsertable into inplace_vector. + // Effects: Constructs an inplace_vector with n default-inserted elements. + // Complexity : Linear in n. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + EXPECT_EQ(IV(0), IV{}); + + EXPECT_THROW(IV(IV::capacity() + 1), std::bad_alloc); + + constexpr auto mid_size = std::midpoint(0ul, IV::capacity()); + IV mid(mid_size); + EXPECT_EQ(mid.size(), mid_size); + if (std::is_same_v || + std::is_same_v) { + + IV mid_correct; + for (auto i = 0ul; i < mid_size; ++i) + mid_correct.emplace_back(); + + EXPECT_EQ(mid, mid_correct); + } + + IV full((IV::capacity())); + EXPECT_EQ(full.size(), IV::capacity()); + if (std::is_same_v || + std::is_same_v) { + + IV full_correct; + for (auto i = 0ul; i < full.size(); ++i) + full_correct.emplace_back(); + + EXPECT_EQ(full, full_correct); + } +} + +TYPED_TEST(Constructors, SizedValue) { + // constexpr inplace_vector(size_type n, const T& value); + // Preconditions: T is Cpp17CopyInsertable into inplace_vector. + // Effects: Constructs an inplace_vector with n copies of value. + // Complexity: Linear in n. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + { + T value{3519}; + IV device(0, value); + EXPECT_EQ(device, IV{}); + + EXPECT_THROW(IV(IV::capacity() + 1, value), std::bad_alloc); + } + + if (IV::capacity() < 1) + return; + + { + T value{6810}; + IV device(1, value); + EXPECT_EQ(device, IV{value}); + } + + { + T value{8194}; + IV device(IV::capacity(), value); + + IV correct; + for (auto i = 0ul; i < device.size(); ++i) + correct.push_back(value); + + EXPECT_EQ(std::count(device.begin(), device.end(), value), IV::capacity()); + } +} + +TYPED_TEST(Constructors, CopyIter) { + // template + // constexpr inplace_vector(InputIterator first, InputIterator last); + // Effects: Constructs an inplace_vector equal to the range [first, last). + // Complexity: Linear in distance(first, last). + + using IV = TestFixture::IV; + using InputIterator = TestFixture::InputIterator; + + IV a(InputIterator{0}, InputIterator{IV::max_size() / 2}); + EXPECT_EQ(a.size(), IV::max_size() / 2); + if (!a.empty()) { + EXPECT_EQ(a.back().value, IV::max_size() / 2 - 1); + } + + IV b(a.begin(), a.end()); + EXPECT_EQ(b, a); +} + +TYPED_TEST(Constructors, CopyRanges) { + // template R> + // constexpr inplace_vector(from_range_t, R&& rg); + // Effects: Constructs an inplace_vector with the elements of the range rg. + // Complexity: Linear in ranges::distance(rg). + + using IV = TestFixture::IV; + + auto reference = this->unique(); + + { + IV device(beman::from_range, reference); + EXPECT_EQ(device, reference); + } + + if (IV::capacity() == 0) + return; + + { + IV device(beman::from_range, reference | std::ranges::views::take(1)); + EXPECT_EQ(device, IV{reference.front()}); + } + + { + auto mid = std::midpoint(0ul, reference.size()); + IV device(beman::from_range, reference | std::ranges::views::take(mid)); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + mid)); + } +} + +}; // namespace diff --git a/tests/beman/inplace_vector/container_requirements.test.cpp b/tests/beman/inplace_vector/container_requirements.test.cpp new file mode 100644 index 0000000..f38f99f --- /dev/null +++ b/tests/beman/inplace_vector/container_requirements.test.cpp @@ -0,0 +1,1023 @@ +#include "gtest/gtest.h" +#include +#include +#include +#include + +#include "gtest_setup.hpp" + +namespace { + +// 2 An inplace_vector meets all of the requirements of a container (23.2.2.2), +// of a reversible container (23.2.2.3), of a contiguous container, and of a +// sequence container, including most of the optional sequence container +// requirements (23.2.4). The exceptions are the push_front, prepend_range, +// pop_front, and emplace_front member functions, which are not provided. +// Descriptions are provided here only for operations on inplace_- vector that +// are not described in one of these tables or for operations where there is +// additional semantic information. + +// [container.rqmts] +template +class ContainerRequirements : public IVBasicTest {}; +TYPED_TEST_SUITE(ContainerRequirements, IVAllTypes); + +TYPED_TEST(ContainerRequirements, ValueType) { + using T = TestFixture::T; + using X = TestFixture::X; + + // typename X::value_type + // Result: T + // Preconditions: T is Cpp17Erasable from X (see [container.alloc.reqmts]). + EXPECT_TRUE((std::is_same_v)); +} + +TYPED_TEST(ContainerRequirements, Reference) { + using T = TestFixture::T; + using X = TestFixture::X; + + // typename X::reference + // Result: T& + EXPECT_TRUE((std::is_same_v)); + + // typename X::const_reference + // Result: const T& + EXPECT_TRUE((std::is_same_v)); +} + +TYPED_TEST(ContainerRequirements, Iterator) { + using T = TestFixture::T; + using X = TestFixture::X; + + // typename X::iterator + // Result: A type that meets the forward iterator requirements + // ([forward.iterators]) with value type T. The type X​::​iterator is + // convertible to X​::​const_iterator. + EXPECT_TRUE(std::forward_iterator); + EXPECT_TRUE( + std::equality_comparable); // required by + // [forward.iterators], + // but not by + // std::forward_iterator + EXPECT_TRUE((std::is_same_v()), + T &>)); // required by [forward.iterators], but + // not by std::forward_iterator + EXPECT_TRUE(( + std::is_convertible_v)); + + // typename X::const_iterator + // Result: A type that meets the requirements of a constant iterator and + // those of a forward iterator with value type T. + EXPECT_TRUE(std::forward_iterator); + EXPECT_TRUE(std::equality_comparable< + typename X::const_iterator>); // required by + // [forward.iterators], but + // not by + // std::forward_iterator + EXPECT_TRUE( + (std::is_same_v()), + const T &>)); // required by [forward.iterators], + // but not by std::forward_iterator +} + +TYPED_TEST(ContainerRequirements, DifferenceType) { + using X = TestFixture::X; + + // typename X::difference_type + // Result: A signed integer type, identical to the difference type of + // X​::​iterator and X​::​const_iterator. + EXPECT_TRUE(std::is_signed_v); + EXPECT_TRUE((std::is_same_v::difference_type>)); + EXPECT_TRUE( + (std::is_same_v::difference_type>)); +} + +TYPED_TEST(ContainerRequirements, SizeType) { + using X = TestFixture::X; + + // typename X::size_type + // Result: An unsigned integer type that can represent any non-negative + // value of X​::​difference_type. + EXPECT_TRUE(std::is_unsigned_v); + EXPECT_GE(sizeof(typename X::size_type), sizeof(typename X::difference_type)); +} + +TYPED_TEST(ContainerRequirements, DefaultConstructor) { + using X = TestFixture::X; + + // X u; + // X u = X(); + // Postconditions: u.empty() + // Complexity: Constant. + { + X u; + EXPECT_TRUE(u.empty()); + // How to test complexity? + } + { + X u = X(); + EXPECT_TRUE(u.empty()); + // How to test complexity? + } +} + +TYPED_TEST(ContainerRequirements, CopyConstructor) { + using X = TestFixture::X; + + // X u(v); + // X u = v; + // Preconditions: T is Cpp17CopyInsertable into X (see below). + // Postconditions: u == v. + // Complexity: Linear. + X const v(TestFixture::unique()); + { + X u(v); + EXPECT_EQ(u, v); + // How to test complexity? + } + { + X u = v; + EXPECT_EQ(u, v); + // How to test complexity? + } +} + +TYPED_TEST(ContainerRequirements, MoveConstructor) { + using X = TestFixture::X; + + // X u(rv); + // X u = rv; + // Postconditions: u is equal to the value that rv had before this + // construction. + // Complexity: Linear. + X const v(TestFixture::unique()); + auto const rv = [&v]() { return v; }; + { + X u(rv()); + EXPECT_EQ(u, v); + // How to test complexity? + } + { + X u = rv(); + EXPECT_EQ(u, v); + // How to test complexity? + } +} + +TYPED_TEST(ContainerRequirements, CopyAssignment) { + using X = TestFixture::X; + + // t = v; + // Result: X&. + // Postconditions: t == v. + // Complexity: Linear. + X const v(TestFixture::unique(X::max_size() / 2)); + for (typename X::size_type n = 0; n <= X::max_size(); ++n) { + X t(n); + t = v; + EXPECT_TRUE((std::is_same_v)); + EXPECT_EQ(t, v); + } + // How to test complexity? +} + +TYPED_TEST(ContainerRequirements, MoveAssignment) { + using T = TestFixture::T; + using X = TestFixture::X; + + // t = rv + // Result: X&. + // Effects: All existing elements of t are either move assigned to or + // destroyed. + // Postconditions: If t and rv do not refer to the same object, t + // is equal to the value that rv had before this assignment. + // Complexity: Linear. + X const v(TestFixture::unique(X::max_size() / 2)); + auto const rv = [&v]() { return v; }; + for (typename X::size_type n = 0; n <= X::max_size(); ++n) { + if constexpr (counts_objects_v) { + T::num_objects = 0; + } + X t(n); + if constexpr (counts_objects_v) { + ASSERT_EQ(T::num_objects, t.size()); + } + t = rv(); + EXPECT_TRUE((std::is_same_v)); + if constexpr (counts_objects_v) { + EXPECT_EQ(T::num_objects, v.size()); + } + EXPECT_EQ(t, v); + } + // How to test complexity? +} + +TYPED_TEST(ContainerRequirements, Destructor) { + using T = TestFixture::T; + using X = TestFixture::X; + + // a.~X() + // Result: void. + // Effects: Destroys every element of a; any memory obtained is deallocated. + // Complexity: Linear. + if constexpr (counts_objects_v) { + T::num_objects = 0; + } + alignas(X) std::byte storage[sizeof(X)]; + X *pa = new (static_cast(storage)) X(X::max_size()); + X &a = *pa; + if constexpr (counts_objects_v) { + ASSERT_EQ(T::num_objects, X::max_size()); + } + a.~X(); + EXPECT_TRUE(std::is_void_v); + if constexpr (counts_objects_v) { + EXPECT_EQ(T::num_objects, 0); + } + // How to test complexity? +} + +TYPED_TEST(ContainerRequirements, Begin) { + using X = TestFixture::X; + + // b.begin() + // Result: iterator; const_iterator for constant b. + // Returns: An iterator referring to the first element in the container. + /// Complexity: Constant. + // b.cbegin() + // Result: const_iterator. + // Returns: const_cast(b).begin() + // Complexity: Constant. + + for (typename X::size_type n = 0; n <= X::max_size(); ++n) { + X b(n); + X const cb(n); + EXPECT_TRUE((std::is_same_v)); + EXPECT_TRUE( + (std::is_same_v)); + EXPECT_TRUE( + (std::is_same_v)); + EXPECT_EQ(b.cbegin(), const_cast(b).begin()); + if (n > 0) { + EXPECT_EQ(std::addressof(*b.begin()), std::addressof(b.data()[0])); + EXPECT_EQ(std::addressof(*cb.begin()), std::addressof(cb.data()[0])); + EXPECT_EQ(std::addressof(*b.cbegin()), std::addressof(b.data()[0])); + } + // How to test complexity? + } +} + +TYPED_TEST(ContainerRequirements, End) { + using X = TestFixture::X; + + // b.end() + // Result: iterator; const_iterator for constant b. + // Returns: An iterator which is the past-the-end value for the container. + /// Complexity: Constant. + // b.cend() + // Result: const_iterator. + // Returns: const_cast(b).end() + // Complexity: Constant. + + for (typename X::size_type n = 0; n <= X::max_size(); ++n) { + X b(n); + X const cb(n); + EXPECT_TRUE((std::is_same_v)); + EXPECT_TRUE( + (std::is_same_v)); + EXPECT_TRUE( + (std::is_same_v)); + EXPECT_EQ(b.cend(), const_cast(b).end()); + if (n > 0) { + EXPECT_EQ(std::addressof(*(b.end() - 1)), + std::addressof(b.data()[b.size() - 1])); + EXPECT_EQ(std::addressof(*(cb.end() - 1)), + std::addressof(cb.data()[cb.size() - 1])); + EXPECT_EQ(std::addressof(*(b.cend() - 1)), + std::addressof(b.data()[b.size() - 1])); + } + // How to test complexity? + } +} + +TYPED_TEST(ContainerRequirements, Ordering) { + using X = TestFixture::X; + + // i <=> j + // Result: strong_ordering. + // Constraints: X​::​iterator meets the random access iterator + // requirements. + // Complexity: Constant. + EXPECT_TRUE(std::random_access_iterator); + EXPECT_TRUE(std::random_access_iterator); + EXPECT_TRUE((std::is_same_v() <=> + std::declval()), + std::strong_ordering>)); + EXPECT_TRUE( + (std::is_same_v() <=> + std::declval()), + std::strong_ordering>)); + EXPECT_TRUE( + (std::is_same_v() <=> + std::declval()), + std::strong_ordering>)); + EXPECT_TRUE( + (std::is_same_v() <=> + std::declval()), + std::strong_ordering>)); + + // How to test complexity? +} + +TYPED_TEST(ContainerRequirements, Equality) { + using X = TestFixture::X; + + // c == b + // Preconditions: T meets the Cpp17EqualityComparable requirements. + // Result: bool. + // Returns: equal(c.begin(), c.end(), b.begin(), b.end()) + // [Note 1: The algorithm equal is defined in [alg.equal]. — end note] + // Complexity: Constant if c.size() != b.size(), linear otherwise. + // Remarks: == is an equivalence relation. + // c != b + // Effects: Equivalent to !(c == b). + std::array values; + values[0] = X::max_size() > 0 ? TestFixture::unique(X::max_size() - 1) + : X{}; // { 0, 1, ... } + values[1] = values[0]; + if (values[1].size() < X::max_size()) { + values[1].push_back(TestFixture::unique(1)[0]); + } // { 0, 1, 2, ... } + values[2] = X::max_size() > 0 ? X(X::max_size() - 1) : X{}; // { 0, 0, ... } + for (X const &c : values) { + EXPECT_TRUE(c == c); + for (X const &b : values) { + EXPECT_TRUE((std::is_same_v)); + EXPECT_EQ(c == b, (std::equal(c.begin(), c.end(), b.begin(), b.end()))); + EXPECT_EQ(c == b, b == c); + EXPECT_EQ(c != b, !(c == b)); + for (X const &a : values) { + EXPECT_TRUE(a == b && b == c ? a == c : true); + } + } + } + // How to test complexity? +} + +TYPED_TEST(ContainerRequirements, Swap) { + using X = TestFixture::X; + + // t.swap(s) + // Result: void. + // Effects: Exchanges the contents of t and s. + // Complexity: Linear. + // swap(t, s) + // Effects: Equivalent to t.swap(s). + + X const t_proto(TestFixture::unique()); + X const s_proto(X::max_size()); + X t(t_proto); + X s(s_proto); + + EXPECT_TRUE(std::is_void_v); + t.swap(s); + EXPECT_EQ(t, s_proto); + EXPECT_EQ(s, t_proto); + EXPECT_TRUE(std::is_void_v); + swap(t, s); + EXPECT_EQ(t, t_proto); + EXPECT_EQ(s, s_proto); + + // How to test complexity? +} + +TYPED_TEST(ContainerRequirements, Size) { + using X = TestFixture::X; + + // c.size() + // Result: size_type. + // Returns: distance(c.begin(), c.end()), i.e., the number of elements in + // the container. + // Complexity: Constant. + // Remarks: The number of elements is + // defined by the rules of constructors, inserts, and erases. + + for (typename X::size_type n = 0; n <= X::max_size(); ++n) { + X c(n); + EXPECT_TRUE((std::is_same_v)); + EXPECT_EQ(c.size(), std::distance(c.begin(), c.end())); + } + // How to test complexity? +} + +TYPED_TEST(ContainerRequirements, MaxSize) { + using X = TestFixture::X; + constexpr auto N = TestFixture::N; + + // c.max_size() + // Result: size_type. + // Returns: distance(begin(), end()) for the largest possible container. + // Complexity: Constant. + X c(N); + EXPECT_TRUE((std::is_same_v)); + EXPECT_EQ(c.max_size(), std::distance(c.begin(), c.end())); + // How to test complexity? +} + +TYPED_TEST(ContainerRequirements, Empty) { + using X = TestFixture::X; + + // c.empty() + // Result: bool. + // Returns: c.begin() == c.end() + // Complexity: Constant. + // Remarks: If the container is empty, then c.empty() is true.} + + for (typename X::size_type n = 0; n <= X::max_size(); ++n) { + X c(n); + EXPECT_TRUE((std::is_same_v)); + EXPECT_EQ(c.empty(), c.begin() == c.end()); + } + // How to test complexity? +} + +// Still [container.reqmts]: +// Unless otherwise specified (see [associative.reqmts.except], +// [unord.req.except], [deque.modifiers], [inplace.vector.modifiers], and +// [vector.modifiers]) all container types defined in this Clause meet the +// following additional requirements: +// - If an exception is thrown by an insert() or emplace() function while +// inserting a single element, that function has no effects. +// --> specified in [inplace.vector.modifiers] +// - If an exception is thrown by a push_back(), push_front(), emplace_back(), +// or emplace_front() function, that function has no effects. +// --> push_front()/emplace_front() n.a. for inplace_vector, +// push_back()/emplace_back() specified in [inplace.vector.modifiers] +// - No erase(), clear(), pop_back() or pop_front() function throws an +// exception. +// --> erase() specified in [inplace.vector.modifiers], pop_front() +// n.a. for inplace_vector +TYPED_TEST(ContainerRequirements, NothrowClear) { + using X = TestFixture::X; + + EXPECT_TRUE(noexcept(std::declval().clear())); +} +TYPED_TEST(ContainerRequirements, NothrowPopBack) { + using X = TestFixture::X; + + // pop_back() has a narrow contract, therefore we cannot check noexcept(). + for (typename X::size_type n = 0; n <= X::max_size(); ++n) { + X c(n); + if (n > 0) { + EXPECT_NO_THROW(c.pop_back()); + } + } +} +// - No copy constructor or assignment operator of a returned iterator throws an +// exception. +TYPED_TEST(ContainerRequirements, NothrowIterator) { + using X = TestFixture::X; + + EXPECT_TRUE(std::is_nothrow_copy_constructible_v); + EXPECT_TRUE(std::is_nothrow_copy_constructible_v); + EXPECT_TRUE(std::is_nothrow_move_constructible_v); + EXPECT_TRUE(std::is_nothrow_move_constructible_v); + EXPECT_TRUE(std::is_nothrow_copy_assignable_v); + EXPECT_TRUE(std::is_nothrow_copy_assignable_v); + EXPECT_TRUE(std::is_nothrow_move_assignable_v); + EXPECT_TRUE(std::is_nothrow_move_assignable_v); +} +// - No swap() function throws an exception. +// --> Specified in [inplace.vector.overview] +// - No swap() function invalidates any references, pointers, or iterators +// referring to the elements of the containers being swapped. +// --> Waived by previous paragraph in [container.reqmts] + +// [container.rev.reqmts] +template +class ReversibleContainerRequirements : public IVBasicTest {}; +TYPED_TEST_SUITE(ReversibleContainerRequirements, IVAllTypes); + +TYPED_TEST(ReversibleContainerRequirements, ReverseIterator) { + using T = TestFixture::T; + using X = TestFixture::X; + + // typename X::reverse_iterator + // Result: The type reverse_iterator, an iterator type + // whose value type is T. + EXPECT_TRUE((std::is_same_v>)); + EXPECT_TRUE((std::is_same_v::value_type, + T>)); + + // typename X::const_reverse_iterator + // Result: The type reverse_iterator, a constant + // iterator type whose value type is T. + EXPECT_TRUE( + (std::is_same_v>)); + EXPECT_TRUE( + (std::is_same_v::value_type, + T>)); +} + +TYPED_TEST(ReversibleContainerRequirements, RBegin) { + using X = TestFixture::X; + + // a.rbegin() + // Result: reverse_iterator; const_reverse_iterator for constant a. + // Returns: reverse_iterator(end()) + // Complexity: Constant. + // a.crbegin() + // Result: const_reverse_iterator. + // Returns: const_cast(a).rbegin() + // Complexity: Constant. + + for (typename X::size_type n = 0; n <= X::max_size(); ++n) { + X a(n); + X const ca(n); + EXPECT_TRUE( + (std::is_same_v)); + EXPECT_TRUE((std::is_same_v)); + EXPECT_EQ(a.rbegin(), typename X::reverse_iterator(a.end())); + EXPECT_EQ(ca.rbegin(), typename X::const_reverse_iterator(ca.end())); + EXPECT_TRUE((std::is_same_v)); + EXPECT_EQ(a.crbegin(), typename X::const_reverse_iterator(a.cend())); + EXPECT_EQ(a.crbegin(), const_cast(a).rbegin()); + // How to test complexity? + } +} + +TYPED_TEST(ReversibleContainerRequirements, REnd) { + using X = TestFixture::X; + + // a.rend() + // Result: reverse_iterator; const_reverse_iterator for constant a. + // Returns: reverse_iterator(begin()) + // Complexity: Constant. + // a.crend() + // Result: const_reverse_iterator. + // Returns: const_cast(a).rend() + // Complexity: Constant. + + for (typename X::size_type n = 0; n <= X::max_size(); ++n) { + X a(n); + X const ca(n); + EXPECT_TRUE( + (std::is_same_v)); + EXPECT_TRUE((std::is_same_v)); + EXPECT_EQ(a.rend(), typename X::reverse_iterator(a.begin())); + EXPECT_EQ(ca.rend(), typename X::const_reverse_iterator(ca.begin())); + EXPECT_TRUE((std::is_same_v)); + EXPECT_EQ(a.crend(), typename X::const_reverse_iterator(a.cbegin())); + EXPECT_EQ(a.crend(), const_cast(a).rend()); + // How to test complexity? + } +} + +// [sequence.reqmts] +template +class SequenceContainerRequirements : public IVBasicTest {}; +TYPED_TEST_SUITE(SequenceContainerRequirements, IVAllTypes); + +// X u(n, t); +// Preconditions: T is Cpp17CopyInsertable into X. +// Effects: Constructs a sequence container with n copies of t. +// Postconditions: distance(u.begin(), u.end()) == n is true. + +// See: Constructors/SizedValue + +// X u(i, j); +// Preconditions: T is Cpp17EmplaceConstructible into X from *i. For vector, if +// the iterator does not meet the Cpp17ForwardIterator requirements +// ([forward.iterators]), T is also Cpp17MoveInsertable into X. Effects: +// Constructs a sequence container equal to the range [i, j). Each iterator in +// the range [i, j) is dereferenced exactly once. Postconditions: +// distance(u.begin(), u.end()) == distance(i, j) is true. + +// See: Constructors/CopyIter + +// X(from_range, rg) +// Preconditions: T is Cpp17EmplaceConstructible into X from +// *ranges​::​begin(rg). For vector, if R models neither +// ranges​::​sized_range nor ranges​::​forward_range, T is also +// Cpp17MoveInsertable into X. Effects: Constructs a sequence container equal to +// the range rg. Each iterator in the range rg is dereferenced exactly once. +// Postconditions: distance(begin(), end()) == ranges​::​distance(rg) is +// true. + +// See: Constructors/CopyRanges + +// X(il) +// Effects: Equivalent to X(il.begin(), il.end()). + +TYPED_TEST(SequenceContainerRequirements, ConstructorInitializerList) { + using IV = TestFixture::IV; + using T = TestFixture::T; + + if (IV::capacity() == 0) { + EXPECT_THROW(IV({T{20}}), std::bad_alloc); + return; + } + + IV device({T{20}}); + + IV correct; + correct.emplace_back(20); + EXPECT_EQ(device, correct); + + if (IV::capacity() == 1) + return; + + device = IV({T{20}, T{21}}); + correct.emplace_back(21); + + EXPECT_EQ(device, correct); +} + +// a = il +// Result: X&. +// Preconditions: T is Cpp17CopyInsertable into X and Cpp17CopyAssignable. +// Effects: Assigns the range [il.begin(), il.end()) into a. All existing +// elements of a are either assigned to or destroyed. Returns: *this. + +TYPED_TEST(SequenceContainerRequirements, AssignInitializerList) { + using IV = TestFixture::IV; + using T = TestFixture::T; + + if (IV::capacity() == 0) { + IV device; + EXPECT_THROW(device = {T{52}}, std::bad_alloc); + return; + } + + IV device; + device = {T{20}}; + EXPECT_EQ(device, IV{T{20}}); +} + +// a.emplace(p, args) +// Result: iterator. +// Preconditions: T is Cpp17EmplaceConstructible into X from args. For vector, +// inplace_vector, and deque, T is also Cpp17MoveInsertable into X and +// Cpp17MoveAssignable. Effects: Inserts an object of type T constructed with +// std​::​forward(args)... before p. [Note 1: args can directly or +// indirectly refer to a value in a. — end note] Returns: An iterator that +// points to the new element. + +// See Modifiers/InsertEmplace + +// a.insert(p, t) +// Result: iterator. +// Preconditions: T is Cpp17CopyInsertable into X. For vector, inplace_vector, +// and deque, T is also Cpp17CopyAssignable. Effects: Inserts a copy of t before +// p. Returns: An iterator that points to the copy of t inserted into a. + +// See Modifiers/InsertSingleConstRef + +// a.insert(p, rv) +// Result: iterator. +// Preconditions: T is Cpp17MoveInsertable into X. For vector, inplace_vector, +// and deque, T is also Cpp17MoveAssignable. Effects: Inserts a copy of rv +// before p. Returns: An iterator that points to the copy of rv inserted into a. + +// See Modifiers/InsertSingleRV + +// a.insert(p, n, t) +// Result: iterator. +// Preconditions: T is Cpp17CopyInsertable into X and Cpp17CopyAssignable. +// Effects: Inserts n copies of t before p. +// Returns: An iterator that points to the copy of the first element inserted +// into a, or p if n == 0. + +// See Modifiers/InsertMulti + +// a.insert(p, i, j) +// Result: iterator. +// Preconditions: T is Cpp17EmplaceConstructible into X from *i. For vector, +// inplace_vector, and deque, T is also Cpp17MoveInsertable into X, and T meets +// the Cpp17MoveConstructible, Cpp17MoveAssignable, and Cpp17Swappable +// ([swappable.requirements]) requirements. Neither i nor j are iterators into +// a. Effects: Inserts copies of elements in [i, j) before p. Each iterator in +// the range [i, j) shall be dereferenced exactly once. Returns: An iterator +// that points to the copy of the first element inserted into a, or p if i == j. + +// See Modifiere/InsertItrRange + +// a.insert_range(p, rg) +// Result: iterator. +// Preconditions: T is Cpp17EmplaceConstructible into X from +// *ranges​::​begin(rg). For vector, inplace_vector, and deque, T is also +// Cpp17MoveInsertable into X, and T meets the Cpp17MoveConstructible, +// Cpp17MoveAssignable, and Cpp17Swappable ([swappable.requirements]) +// requirements. rg and a do not overlap. Effects: Inserts copies of elements in +// rg before p. Each iterator in the range rg is dereferenced exactly once. +// Returns: An iterator that points to the copy of the first element inserted +// into a, or p if rg is empty. + +// See Modifiers/InsertRange + +// a.insert(p, il) +// Effects: Equivalent to a.insert(p, il.begin(), il.end()). +// a.erase(q) +// Result: iterator. +// Preconditions: For vector, inplace_vector, and deque, T is +// Cpp17MoveAssignable. Effects: Erases the element pointed to by q. Returns: An +// iterator that points to the element immediately following q prior to the +// element being erased. If no such element exists, a.end() is returned. + +// See Modifiers/InsertInitList + +// a.erase(q1, q2) +// Result: iterator. +// Preconditions: For vector, inplace_vector, and deque, T is +// Cpp17MoveAssignable. Effects: Erases the elements in the range [q1, q2). +// Returns: An iterator that points to the element pointed to by q2 prior to any +// elements being erased. If no such element exists, a.end() is returned. + +// See Modifiers/EraseRange + +// a.clear() +// Result: void +// Effects: Destroys all elements in a. Invalidates all references, pointers, +// and iterators referring to the elements of a and may invalidate the +// past-the-end iterator. Postconditions: a.empty() is true. Complexity: Linear. + +TYPED_TEST(SequenceContainerRequirements, Clear) { + using IV = TestFixture::IV; + + auto device = this->unique(); + device.clear(); + EXPECT_EQ(device, IV{}); + EXPECT_TRUE(device.empty()); +} + +// a.assign(i, j) +// Result: void +// Preconditions: T is Cpp17EmplaceConstructible into X from *i and assignable +// from *i. For vector, if the iterator does not meet the forward iterator +// requirements ([forward.iterators]), T is also Cpp17MoveInsertable into X. +// Neither i nor j are iterators into a. +// Effects: Replaces elements in a with a copy of [i, j). Invalidates all +// references, pointers and iterators referring to the elements of a. For vector +// and deque, also invalidates the past-the-end iterator. Each iterator in the +// range [i, j) is dereferenced exactly once. + +TYPED_TEST(SequenceContainerRequirements, AssignIterRange) { + using IV = TestFixture::IV; + using T = TestFixture::T; + using InputIterator = TestFixture::InputIterator; + + { + auto device = this->unique(); + + const auto correct = this->unique(); + + device.assign(correct.begin(), correct.end()); + EXPECT_EQ(device, correct); + + std::array ref{}; + EXPECT_THROW(device.assign(ref.begin(), ref.end()), std::bad_alloc); + } + + { + IV device; + device.assign(InputIterator{0}, InputIterator{IV::max_size()}); + EXPECT_EQ(device.size(), IV::max_size()); + // Each iterator in the range [i, j) is dereferenced exactly once. + if (!device.empty()) { + EXPECT_EQ(device.back(), T{static_cast(IV::max_size() - 1)}); + } + + // [containers.sequences.inplace.vector.overview] + // 5. Any member function of inplace_vector that would cause the size + // to exceed N throws an exception of type bad_alloc. + EXPECT_THROW( + device.assign(InputIterator{0}, InputIterator{IV::max_size() + 1}), + std::bad_alloc); + } +} + +// a.assign_range(rg) +// Result: void +// Mandates: assignable_from> is modeled. +// Preconditions: T is Cpp17EmplaceConstructible into X from +// *ranges​::​begin(rg). For vector, if R models neither +// ranges​::​sized_range nor ranges​::​forward_range, T is also +// Cpp17MoveInsertable into X. rg and a do not overlap. Effects: Replaces +// elements in a with a copy of each element in rg. Invalidates all references, +// pointers, and iterators referring to the elements of a. For vector and deque, +// also invalidates the past-the-end iterator. Each iterator in the range rg is +// dereferenced exactly once. + +TYPED_TEST(SequenceContainerRequirements, AssignRange) { + using IV = TestFixture::IV; + using T = TestFixture::T; + + auto device = this->unique(); + auto correct = this->unique(); + + device.assign_range(correct); + EXPECT_EQ(device, correct); + + std::array ref; + std::copy(correct.begin(), correct.end(), ref.begin()); + ref.back() = T{5}; + EXPECT_THROW(device.assign_range(ref), std::bad_alloc); +} + +// a.assign(il) +// Effects: Equivalent to a.assign(il.begin(), il.end()). + +TYPED_TEST(SequenceContainerRequirements, AssignFuncInitializerList) { + using IV = TestFixture::IV; + using T = TestFixture::T; + + auto device = this->unique(); + + if (device.capacity() == 0) { + EXPECT_THROW(device.assign({T{50}}), std::bad_alloc); + return; + } + + device.assign({T{50}}); + EXPECT_EQ(device, IV{T{50}}); +} + +// a.assign(n, t) +// Result: void +// Preconditions: T is Cpp17CopyInsertable into X and Cpp17CopyAssignable. t is +// not a reference into a. +// Effects: Replaces elements in a with n copies of t. +// Invalidates all references, pointers and iterators referring to the elements +// of a. For vector and deque, also invalidates the past-the-end iterator. For +// every sequence container defined in this Clause and in [strings]: +// +// If the constructor +// template +// X(InputIterator first, InputIterator last, +// const allocator_type& alloc = allocator_type()); +// +// is called with a type InputIterator that does not qualify as an input +// iterator, then the constructor shall not participate in overload resolution. +// +// If the member functions of the forms: +// template +// return-type F(const_iterator p, +// InputIterator first, InputIterator last); // such as +// insert +// +// template +// return-type F(InputIterator first, InputIterator last); // such as +// append, assign +// +// template +// return-type F(const_iterator i1, const_iterator i2, +// InputIterator first, InputIterator last); // such as +// replace +// +// are called with a type InputIterator that does not qualify as an input +// iterator, then these functions shall not participate in overload resolution. +// A deduction guide for a sequence container shall not participate in overload +// resolution if it has an InputIterator template parameter and a type that does +// not qualify as an input iterator is deduced for that parameter, or if it has +// an Allocator template parameter and a type that does not qualify as an +// allocator is deduced for that parameter. The following operations are +// provided for some types of sequence containers but not others. Operations +// other than prepend_range and append_range are implemented so as to take +// amortized constant time. + +TYPED_TEST(SequenceContainerRequirements, AssignMulti) { + using IV = TestFixture::IV; + using T = TestFixture::T; + + auto device = this->unique(); + device.assign(0, T{6312}); + + EXPECT_EQ(device, IV()); + + if (device.capacity() > 0) { + device.assign(1, T{6312}); + + EXPECT_EQ(device, IV{T{6312}}); + + device.assign(device.capacity(), T{5972}); + EXPECT_EQ(device, IV(IV::capacity(), T{5972})); + } + + device.clear(); + EXPECT_THROW(device.assign(device.capacity() + 1, T{12}), std::bad_alloc); +} + +// a.front() +// Result: reference; const_reference for constant a. +// Returns: *a.begin() + +TYPED_TEST(SequenceContainerRequirements, Front) { + using X = TestFixture::X; + if constexpr (X::capacity() == 0) { + return; + } + + auto a = this->unique(); + auto const ca = this->unique(); + + EXPECT_TRUE((std::is_same_v)); + EXPECT_TRUE( + (std::is_same_v)); + EXPECT_EQ(a.front(), *a.begin()); + EXPECT_EQ(ca.front(), *ca.begin()); +} + +// a.back() +// Result: reference; const_reference for constant a. +// Effects: Equivalent to: +// auto tmp = a.end(); +// --tmp; +// return *tmp; + +TYPED_TEST(SequenceContainerRequirements, Back) { + using X = TestFixture::X; + if constexpr (X::capacity() == 0) { + return; + } + + auto a = this->unique(); + auto const ca = this->unique(); + + EXPECT_TRUE((std::is_same_v)); + EXPECT_TRUE( + (std::is_same_v)); + EXPECT_EQ(a.back(), *(a.end() - 1)); + EXPECT_EQ(ca.back(), *(ca.end() - 1)); +} + +// a.emplace_back(args) +// Returns: a.back(). + +// See: Modifiers/EmplaceBack + +// a.push_back(t) +// Result: void +// Preconditions: T is Cpp17CopyInsertable into X. +// Effects: Appends a copy of t. + +// See: Modifiers/EmplaceBack + +// a.push_back(rv) +// Result: void +// Preconditions: T is Cpp17MoveInsertable into X. +// Effects: Appends a copy of rv. + +// See: Modifiers/PushBackRV + +// a.pop_back() +// Result: void +// Preconditions: a.empty() is false. +// Effects: Destroys the last element. + +// See: Modifiers/PopBack + +// a[n] +// Result: reference; const_reference for constant +// Effects: Equivalent to: return *(a.begin() + n); + +TYPED_TEST(SequenceContainerRequirements, ElementAccess) { + using IV = TestFixture::IV; + using T = TestFixture::T; + + auto device = this->unique(); + + for (auto i = 0uz; i < device.size(); ++i) + EXPECT_EQ(device[i], *(device.begin() + i)); +} + +// a.at(n) +// Result: reference; const_reference for constant a +// Returns: *(a.begin() + n) +// Throws: out_of_range if n >= a.size(). + +TYPED_TEST(SequenceContainerRequirements, ElementAccessAt) { + using IV = TestFixture::IV; + using T = TestFixture::T; + + auto device = this->unique(); + + for (auto i = 0ul; i < device.size(); ++i) { + EXPECT_EQ(device.at(i), *(device.begin() + i)); + } + + EXPECT_THROW(device.at(IV::capacity()), std::out_of_range); +} + +}; // namespace diff --git a/tests/beman/inplace_vector/erasure.test.cpp b/tests/beman/inplace_vector/erasure.test.cpp new file mode 100644 index 0000000..a3f442e --- /dev/null +++ b/tests/beman/inplace_vector/erasure.test.cpp @@ -0,0 +1,71 @@ +#include + +#include "gtest_setup.hpp" + +namespace { +// 23.3.14.6 Erasure [inplace.vector.erasure] + +template class Erasure : public IVBasicTest {}; +TYPED_TEST_SUITE(Erasure, IVAllTypes); + +TYPED_TEST(Erasure, ByValue) { + // template + // constexpr size_t erase(inplace_vector& c, const U& value); + // + // Effects: Equivalent to: + // auto it = remove(c.begin(), c.end(), value); + // auto r = distance(it, c.end()); + // c.erase(it, c.end()); + // return r; + + using T = TestFixture::T; + using IV = TestFixture::IV; + + IV device; + if constexpr (device.capacity() == 0) + return; + + T duplicates{4612}; + auto uniques = this->unique(device.capacity() / 2); + + for (auto i = 0ul; i < uniques.size(); ++i) { + device.push_back(uniques[i]); + if (device.size() != device.capacity()) + device.push_back(duplicates); + } + + // TODO: uncomment this after erase is implemented + // beman::erase(device, duplicates); + // EXPECT_EQ(uniques, device); + + GTEST_SKIP() << "Not implemented"; +} + +TYPED_TEST(Erasure, ByPred) { + // template + // constexpr size_t erase_if(inplace_vector& c, Predicate pred); + // + // Effects: Equivalent to: + // auto it = remove_if(c.begin(), c.end(), pred); + // auto r = distance(it, c.end()); + // c.erase(it, c.end()); + // return r; + + using T = TestFixture::T; + using IV = TestFixture::IV; + + IV device; + if constexpr (device.capacity() == 0) + return; + + for (auto i = 0; i < static_cast(device.capacity()); ++i) + device.push_back(T{i}); + + // TODO: complete this when its implemented + // beman::erase_if(device, + // [&](auto &v) { return v.value > (device.capacity() / 2); + // }); + + GTEST_SKIP() << "Not implemented"; +} +}; // namespace diff --git a/tests/beman/inplace_vector/gtest_setup.hpp b/tests/beman/inplace_vector/gtest_setup.hpp new file mode 100644 index 0000000..b09c90d --- /dev/null +++ b/tests/beman/inplace_vector/gtest_setup.hpp @@ -0,0 +1,387 @@ +#include +#include + +#include "beman/inplace_vector/inplace_vector.hpp" + +// We run the tests on various element types with the help of GoogleTest's +// typed tests. Which types shall we use? +// We know from the specification of inplace_vector that there are separate +// implementation branches for trivial and non-trivial types (due to constexpr +// support). Furthermore, we have the requirements that the triviality of +// inplace_vector's SMFs depends on the triviality of the element type T: +// 1. If T is trivially copy constructible, then inplace_vector is +// trivially copy constructible. +// 2. If T is trivially move constructible, then inplace_vector is +// trivially move constructible. +// 3. If T is trivially destructible, then inplace_vector is trivially +// destructible. +// 4. If T is trivially destructible, trivially copy constructible and trivially +// copy assignable, then inplace_vector is trivially copy assignable. +// 5. If T is trivially destructible, trivially move constructible and trivially +// move assignable, then inplace_vector is trivially move assignable. +// +// In cases 1, 2, 3, where there is only dependence on _one_ property of T, we +// have to run tests with at least three different types: +// - A trivial type, in order to cover the "trivial" implementation branch. For +// such a type, the condition in 1, 2, or 3 is always fulfilled implicitly. +// - A non-trivial type, in order to cover the "non-trivial" implementation +// branch, and where the condition in 1, 2, or 3 is fulfilled; e.g. a type which +// is trivially copy constructible, but not trivial. A type where only the +// default constructor is non-trivial can serve here and can be reused for all +// similar cases. +// - A type where the condition in 1, 2, or 3 is not fulfilled, this implies +// that the type is non-trivial. A type which has no trivial SMFs at all can +// serve here for simplicity and reuse. +// +// The cases 4 and 5 depend on three properties of T each. This means, for full +// coverage of all combinations we need 2^3 + 1 types (the + 1 is for the +// "trivial" implementation branch) in theory. In fact, there are fewer +// combinations, since it is not possible to create a type which is not +// trivially destructible and trivially copy or move constructible at the same +// time. The combination where T is non-trivial, but all three properties are +// fulfilled, can be covered by the type with a non-trivial default constructor, +// which was already mentioned above. The combination where none of the +// properties is fulfilled is covered by the type with no trivial SMF at all, +// also reused from above. For the rest of the combinations, we need to create +// additional types with the appropriate properties. +// +// The following table provides an overview and proof that all combinations for +// the SMFs are really covered. All types have the same interface, this +// makes it possible to write generic tests. Once the types and the test +// suite(s) are set up, we do not need to mention the types again, so the actual +// test cases will look quite clean. All tests are run with all types, even if +// not strictly necessary by the analysis above. This does not harm (except run +// time for the tests) and is an additional safe guard against implementation +// errors. +// clang-format off +/* + | (Trivially default | Trivially | Trivially copy | Trivially move | Trivially copy | Trivially move | Trivial* | Type + | constructible) | destructible | constructible | constructible | assignable | assignable | | +--------+--------------------+--------------+----------------+----------------+----------------+----------------+----------+---------------------------------- +Copy | (YES) | - | YES | - | - | - | YES | Trivial +c'tor | (NO) | - | YES | - | - | - | NO | NonTriviallyDefaultConstructible + | - | - | NO | - | - | - | NO | NonTrivial +--------+--------------------+--------------+----------------+----------------+----------------+----------------+----------+---------------------------------- +Move | (YES) | - | - | YES | - | - | YES | Trivial +c'tor | (NO) | - | - | YES | - | - | NO | NonTriviallyDefaultConstructible + | - | - | - | NO | - | - | NO | NonTrivial +--------+--------------------+--------------+----------------+----------------+----------------+----------------+----------+---------------------------------- + | (YES) | YES | - | - | - | - | YES | Trivial +D'tor | (NO) | YES | - | - | - | - | NO | NonTriviallyDefaultConstructible + | - | NO | - | - | - | - | NO | NonTrivial +--------+--------------------+--------------+----------------+----------------+----------------+----------------+----------+---------------------------------- + | (YES) | YES | YES | - | YES | - | YES | Trivial + | (NO) | YES | YES | - | YES | - | NO | NonTriviallyDefaultConstructible +Copy | - | YES | YES | - | NO | | NO | NonTriviallyCopyAssignable +assign- | - | YES | NO | - | YES | | NO | NonTriviallyCopyConstructible +meant | - | YES | NO | - | NO | | NO | TriviallyDestructible + | - | NO | NO** | - | YES | | NO | TriviallyAssignable + | - | NO | NO** | - | NO | | NO | NonTrivial +--------+--------------------+--------------+----------------+----------------+----------------+----------------+----------+---------------------------------- + | (YES) | YES | - | YES | - | YES | YES | Trivial + | (NO) | YES | - | YES | - | YES | NO | NonTriviallyDefaultConstructible +Move | - | YES | - | YES | - | NO | NO | NonTriviallyMoveAssignable +assign- | - | YES | - | NO | - | YES | NO | NonTriviallyMoveConstructible +meant | - | YES | - | NO | - | NO | NO | TriviallyDestructible + | - | NO | - | NO** | - | YES | NO | TriviallyAssignable + | - | NO | - | NO** | - | NO | NO | NonTrivial + +*) The values in this column do not vary independently, they are implied by the other properties +**) Implied by "not trivially destructible" +*/ + +// A trivial type +struct Trivial { + int value; + friend constexpr bool operator==(Trivial x, Trivial y) = default; +}; +static_assert(std::is_trivial_v ); +static_assert(std::is_trivially_default_constructible_v); +static_assert(std::is_trivially_copy_constructible_v ); +static_assert(std::is_trivially_move_constructible_v ); +static_assert(std::is_trivially_destructible_v ); +static_assert(std::is_trivially_copy_assignable_v ); +static_assert(std::is_trivially_move_assignable_v ); + +// A type which is not trivially default constructible (and thus not trivial), +// and all other SMFs are trivial. +struct NonTriviallyDefaultConstructible { + int value = 0; + friend constexpr bool operator==(NonTriviallyDefaultConstructible x, NonTriviallyDefaultConstructible y) = default; +}; +static_assert(not std::is_trivial_v ); +static_assert(not std::is_trivially_default_constructible_v); +static_assert( std::is_default_constructible_v ); +static_assert( std::is_trivially_copy_constructible_v ); +static_assert( std::is_trivially_move_constructible_v ); +static_assert( std::is_trivially_destructible_v ); +static_assert( std::is_trivially_copy_assignable_v ); +static_assert( std::is_trivially_move_assignable_v ); + +// A type which is not trivially copy constructible (and thus not trivial), +// and all other SMFs are trivial. +struct NonTriviallyCopyConstructible { + int value; + constexpr NonTriviallyCopyConstructible() noexcept = default; + constexpr NonTriviallyCopyConstructible(int v) noexcept : value(v) {} + constexpr NonTriviallyCopyConstructible(NonTriviallyCopyConstructible const &other) noexcept : value(other.value) {} + constexpr NonTriviallyCopyConstructible(NonTriviallyCopyConstructible &&) noexcept = default; + constexpr NonTriviallyCopyConstructible &operator=(NonTriviallyCopyConstructible const &) noexcept = default; + friend constexpr bool operator==(NonTriviallyCopyConstructible x, NonTriviallyCopyConstructible y) = default; +}; +static_assert(not std::is_trivial_v ); +static_assert( std::is_trivially_default_constructible_v ); +static_assert(not std::is_trivially_copy_constructible_v ); +static_assert( std::is_copy_constructible_v ); +static_assert( std::is_trivially_move_constructible_v ); +static_assert( std::is_trivially_destructible_v ); +static_assert( std::is_trivially_move_assignable_v ); +static_assert( std::is_trivially_copy_assignable_v ); + +// A type which is not trivially move constructible (and thus not trivial), +// and all other SMFs are trivial. +struct NonTriviallyMoveConstructible { + int value; + constexpr NonTriviallyMoveConstructible() noexcept = default; + constexpr NonTriviallyMoveConstructible(int v) noexcept : value(v) {} + constexpr NonTriviallyMoveConstructible(NonTriviallyMoveConstructible const &) noexcept = default; + constexpr NonTriviallyMoveConstructible(NonTriviallyMoveConstructible &&other) noexcept : value(other.value) {} + constexpr NonTriviallyMoveConstructible &operator=(NonTriviallyMoveConstructible const &) noexcept = default; + friend constexpr bool operator==(NonTriviallyMoveConstructible x, NonTriviallyMoveConstructible y) = default; +}; +static_assert(not std::is_trivial_v ); +static_assert( std::is_trivially_default_constructible_v ); +static_assert( std::is_trivially_copy_constructible_v ); +static_assert(not std::is_trivially_move_constructible_v ); +static_assert( std::is_move_constructible_v ); +static_assert( std::is_trivially_destructible_v ); +static_assert( std::is_trivially_copy_assignable_v ); +static_assert( std::is_trivially_move_assignable_v ); + +// A type which is not trivially copy assignable (and thus not trivial), +// and all other SMFs are trivial. +struct NonTriviallyCopyAssignable { + int value; + constexpr NonTriviallyCopyAssignable() noexcept = default; + constexpr NonTriviallyCopyAssignable(int v) noexcept : value(v) {} + constexpr NonTriviallyCopyAssignable(NonTriviallyCopyAssignable const &) noexcept = default; + + constexpr NonTriviallyCopyAssignable &operator=(NonTriviallyCopyAssignable const &other) noexcept { + value = other.value; + return *this; + } + + constexpr NonTriviallyCopyAssignable &operator=(NonTriviallyCopyAssignable &&) noexcept = default; + friend constexpr bool operator==(NonTriviallyCopyAssignable x, NonTriviallyCopyAssignable y) = default; +}; +static_assert(not std::is_trivial_v ); +static_assert( std::is_trivially_default_constructible_v); +static_assert( std::is_trivially_copy_constructible_v ); +static_assert( std::is_trivially_move_constructible_v ); +static_assert( std::is_trivially_destructible_v ); +static_assert(not std::is_trivially_copy_assignable_v ); +static_assert( std::is_copy_assignable_v ); +static_assert( std::is_trivially_move_assignable_v ); + +// A type which is not trivially move assignable (and thus not trivial), +// and all other SMFs are trivial. +struct NonTriviallyMoveAssignable { + int value; + constexpr NonTriviallyMoveAssignable() noexcept = default; + constexpr NonTriviallyMoveAssignable(int v) noexcept : value(v) {} + constexpr NonTriviallyMoveAssignable(NonTriviallyMoveAssignable const &) noexcept = default; + constexpr NonTriviallyMoveAssignable &operator=(NonTriviallyMoveAssignable const &) noexcept = default; + + constexpr NonTriviallyMoveAssignable &operator=(NonTriviallyMoveAssignable &&other) noexcept { + value = other.value; + return *this; + } + + friend constexpr bool operator==(NonTriviallyMoveAssignable x, NonTriviallyMoveAssignable y) = default; +}; +static_assert(not std::is_trivial_v ); +static_assert( std::is_trivially_default_constructible_v); +static_assert( std::is_trivially_copy_constructible_v ); +static_assert( std::is_trivially_move_constructible_v ); +static_assert( std::is_trivially_destructible_v ); +static_assert( std::is_trivially_copy_assignable_v ); +static_assert(not std::is_trivially_move_assignable_v ); +static_assert( std::is_move_assignable_v ); + +// A type which is trivially copy assignable and trivially move assignable, +// and all other SMS are non-trivial. +struct TriviallyAssignable { + int value; + constexpr ~TriviallyAssignable() {} + friend constexpr bool operator==(TriviallyAssignable x, TriviallyAssignable y) = default; +}; +static_assert(not std::is_trivial_v ); +static_assert(not std::is_trivially_default_constructible_v); +static_assert( std::is_default_constructible_v ); +static_assert(not std::is_trivially_copy_constructible_v ); +static_assert( std::is_copy_constructible_v ); +static_assert(not std::is_trivially_move_constructible_v ); +static_assert( std::is_move_constructible_v ); +static_assert(not std::is_trivially_destructible_v ); +static_assert( std::is_destructible_v ); +static_assert( std::is_trivially_copy_assignable_v ); +static_assert( std::is_trivially_move_assignable_v ); + +// A type which is trivially destructible, and all other SMFs are non-trivial. +struct TriviallyDestructible { + int value; + constexpr TriviallyDestructible() noexcept : value() {} + constexpr TriviallyDestructible(int v) noexcept : value(v) {} + constexpr TriviallyDestructible(TriviallyDestructible const &other) noexcept : value(other.value) {} + constexpr ~TriviallyDestructible() = default; + + constexpr TriviallyDestructible &operator=(TriviallyDestructible const &other) noexcept { + value = other.value; + return *this; + } + + friend constexpr bool operator==(TriviallyDestructible x, TriviallyDestructible y) = default; +}; +static_assert(not std::is_trivial_v ); +static_assert(not std::is_trivially_default_constructible_v); +static_assert( std::is_default_constructible_v ); +static_assert(not std::is_trivially_copy_constructible_v ); +static_assert( std::is_copy_constructible_v ); +static_assert(not std::is_trivially_move_constructible_v ); +static_assert( std::is_move_constructible_v ); +static_assert( std::is_trivially_destructible_v ); +static_assert(not std::is_trivially_copy_assignable_v ); +static_assert( std::is_copy_assignable_v ); +static_assert(not std::is_trivially_move_assignable_v ); +static_assert( std::is_move_assignable_v ); + +// A type with no trivial member function at all. +struct NonTrivial { + static std::size_t num_objects; + int value; + + constexpr NonTrivial() noexcept : NonTrivial(0) {} + constexpr NonTrivial(int v) noexcept : value(v) { + if (not std::is_constant_evaluated()) { + ++num_objects; + } + } + constexpr NonTrivial(NonTrivial const &other) noexcept + : NonTrivial(other.value) {} + + constexpr NonTrivial &operator=(NonTrivial const &other) noexcept { + if (not std::is_constant_evaluated()) { + ++num_objects; + } + value = other.value; + return *this; + } + + constexpr ~NonTrivial() { + if (not std::is_constant_evaluated()) { + --num_objects; + } + } + friend constexpr bool operator==(NonTrivial x, NonTrivial y) = default; +}; +std::size_t NonTrivial::num_objects; + +template +struct counts_objects : std::false_type {}; +template +struct counts_objects> + : std::true_type {}; +template +inline constexpr bool counts_objects_v = counts_objects::value; + +static_assert(not std::is_trivial_v ); +static_assert(not std::is_trivially_default_constructible_v); +static_assert( std::is_default_constructible_v ); +static_assert(not std::is_trivially_copy_constructible_v ); +static_assert( std::is_copy_constructible_v ); +static_assert(not std::is_trivially_move_constructible_v ); +static_assert( std::is_move_constructible_v ); +static_assert(not std::is_trivially_destructible_v ); +static_assert( std::is_destructible_v ); +static_assert(not std::is_trivially_copy_assignable_v ); +static_assert( std::is_copy_assignable_v ); +static_assert(not std::is_trivially_move_assignable_v ); +static_assert( std::is_move_assignable_v ); + +// clang-format on + +template struct TestParam { + using value_type = T; + inline static constexpr std::size_t capacity = N; +}; + +using IVAllTypes = ::testing::Types< + TestParam, TestParam, TestParam, + TestParam, TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, + TestParam, TestParam, + TestParam, TestParam, + TestParam, TestParam, + TestParam, TestParam, + TestParam, TestParam, + TestParam, TestParam>; + +template class IVBasicTest : public ::testing::Test { +public: + using T = Param::value_type; + inline static constexpr std::size_t N = Param::capacity; + using X = beman::inplace_vector; + using IV = X; + + // Returns IV of size n with unique values + static IV unique(typename IV::size_type n = IV::max_size()) { + static T val = T{}; + IV res; + while (n > 0) { + res.push_back(val); + ++val.value; + --n; + } + return res; + } + + struct InputIterator { + T value; + using difference_type = std::ptrdiff_t; + using value_type = T; + + constexpr InputIterator() noexcept : value{0} {} + constexpr InputIterator(int i) noexcept : value{i} {} + + T operator*() const { return value; }; + + InputIterator &operator++() { + ++value.value; + return *this; + }; + void operator++(int) { ++*this; } + + constexpr bool operator==(InputIterator other) noexcept { + return value.value == other.value.value; + } + }; + static_assert(std::input_iterator); +}; diff --git a/tests/beman/inplace_vector/modifiers.test.cpp b/tests/beman/inplace_vector/modifiers.test.cpp new file mode 100644 index 0000000..f29658f --- /dev/null +++ b/tests/beman/inplace_vector/modifiers.test.cpp @@ -0,0 +1,892 @@ +#include + +#include "gtest_setup.hpp" + +namespace { +// 23.3.14.5 Modifiers [inplace.vector.modifiers] +template class Modifiers : public IVBasicTest {}; +TYPED_TEST_SUITE(Modifiers, IVAllTypes); + +TYPED_TEST(Modifiers, InsertSingleConstRef) { + // constexpr iterator insert(const_iterator position, const T& x); + + using IV = TestFixture::IV; + using T = TestFixture::T; + + IV device; + IV reference; + + if (device.capacity() > 0) { + reference = this->unique(); + + auto res = device.insert(device.begin(), reference[0]); + EXPECT_EQ(res, device.begin()); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + 1)); + + if (device.capacity() > 1) { + res = device.insert(device.end(), reference.back()); + EXPECT_EQ(res, device.end() - 1); + EXPECT_EQ(device, IV({reference[0], reference.back()})); + + for (auto i = 1ul; i < (reference.size() - 1); ++i) { + res = device.insert(device.end() - 1, reference[i]); + EXPECT_EQ(res, device.begin() + i); + + IV correct(reference.begin(), reference.begin() + i + 1); + correct.push_back(reference.back()); + + EXPECT_EQ(device, correct); + } + } + } + + T val{272}; + EXPECT_THROW(device.insert(device.begin(), val), std::bad_alloc); + EXPECT_EQ(device, reference); + + EXPECT_THROW(device.insert(device.begin(), val), std::bad_alloc); + EXPECT_EQ(device, reference); +} + +TYPED_TEST(Modifiers, InsertSingleRV) { + // constexpr iterator insert(const_iterator position, T&& x); + + using IV = TestFixture::IV; + using T = TestFixture::T; + + IV device; + IV reference; + + if (device.capacity() > 0) { + reference = this->unique(); + + auto res = device.insert(device.begin(), T{reference[0]}); + EXPECT_EQ(res, device.begin()); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + 1)); + + if (device.capacity() > 1) { + res = device.insert(device.end(), T{reference.back()}); + EXPECT_EQ(res, device.end() - 1); + EXPECT_EQ(device, IV({reference[0], reference.back()})); + + for (auto i = 1ul; i < (reference.size() - 1); ++i) { + res = device.insert(device.end() - 1, T{reference[i]}); + EXPECT_EQ(res, device.begin() + i); + + IV correct(reference.begin(), reference.begin() + i + 1); + correct.push_back(reference.back()); + + EXPECT_EQ(device, correct); + } + } + } + + EXPECT_THROW(device.insert(device.begin(), T{272}), std::bad_alloc); + EXPECT_EQ(device, reference); + + EXPECT_THROW(device.insert(device.begin(), T{272}), std::bad_alloc); + EXPECT_EQ(device, reference); +} + +TYPED_TEST(Modifiers, InsertEmplace) { + // constexpr iterator emplace(const_iterator position, Args&&... args); + + using IV = TestFixture::IV; + + IV device; + IV reference; + + if (device.capacity() > 0) { + reference = this->unique(); + + auto res = device.emplace(device.begin(), reference[0].value); + EXPECT_EQ(res, device.begin()); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + 1)); + + if (device.capacity() > 1) { + res = device.emplace(device.end(), reference.back().value); + EXPECT_EQ(res, device.end() - 1); + EXPECT_EQ(device, IV({reference[0], reference.back()})); + + for (auto i = 1ul; i < (reference.size() - 1); ++i) { + res = device.emplace(device.end() - 1, reference[i].value); + EXPECT_EQ(res, device.begin() + i); + + IV correct(reference.begin(), reference.begin() + i + 1); + correct.push_back(reference.back()); + + EXPECT_EQ(device, correct); + } + } + } + + EXPECT_THROW(device.emplace(device.begin(), 272), std::bad_alloc); + EXPECT_EQ(device, reference); + + EXPECT_THROW(device.emplace(device.begin(), 272), std::bad_alloc); + EXPECT_EQ(device, reference); +} + +TYPED_TEST(Modifiers, InsertMulti) { + // constexpr iterator insert(const_iterator position, size_type n, const T& + // x); + // + // Complexity: Linear in the number of elements inserted plus the distance + // to the end of the vector. + // Remarks: If an exception is thrown other than by the copy constructor, + // move constructor, assignment operator, or move assignment operator of T or + // by any InputIterator operation, there are no effects. Otherwise, if an + // exception is thrown, then size()  ≥ n and elements in the range begin() + + // [0, n) are not modified. + + using IV = TestFixture::IV; + + IV device; + + if (device.capacity() > 0) { + auto duplicate = this->unique(1)[0]; + IV reference(device.capacity(), duplicate); + + if (device.capacity() > 1) { + auto front = this->unique(1)[0]; + reference[0] = front; + device.push_back(front); + } + + auto num_fill = device.capacity() - device.size(); + device.insert(device.end(), num_fill, duplicate); + + EXPECT_EQ(device, IV(reference.begin(), reference.end())); + } + + EXPECT_NO_THROW(device.insert(device.begin(), 0, {2538})); + EXPECT_THROW(device.insert(device.begin(), 1, {2538}), std::bad_alloc); +} + +TYPED_TEST(Modifiers, InsertInitList) { + // constexpr iterator insert(const_iterator position, initializer_list il); + // + // Let n be the value of size() before this call for the append_range + // overload, and distance(begin, position) otherwise. + // Complexity: Linear in the number of elements inserted plus the distance + // to the end of the vector. + // Remarks: If an exception is thrown other than by the copy constructor, + // move constructor, assignment operator, or move assignment operator of T or + // by any InputIterator operation, there are no effects. Otherwise, if an + // exception is thrown, then size()  ≥ n and elements in the range begin() + + // [0, n) are not modified. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + IV device; + auto res = device.insert(device.end(), {}); + EXPECT_EQ(res, device.end()); + EXPECT_EQ(device, IV()); + + if (device.capacity() > 0) { + res = device.insert(device.begin(), {T{0}}); + EXPECT_EQ(res, device.begin()); + EXPECT_EQ(device, IV{T{0}}); + + if (device.capacity() >= 3) { + res = device.insert(device.begin(), {T{1}, T{2}}); + EXPECT_EQ(res, device.begin()); + + IV expected{T{1}, T{2}, T{0}}; + EXPECT_EQ(device, expected); + } + } + + auto full = this->unique(); + EXPECT_NO_THROW(full.insert(full.begin(), {})); + EXPECT_THROW(full.insert(full.begin(), {T{25}}), std::bad_alloc); +} + +TYPED_TEST(Modifiers, InsertRange) { + // template R> + // constexpr iterator insert_range(const_iterator position, R&& rg); + // + // Let n be the value of size() before this call for the append_range + // overload, and distance(begin, position) otherwise. + // Complexity: Linear in the number of elements inserted plus the distance + // to the end of the vector. + // Remarks: If an exception is thrown other than by the copy constructor, + // move constructor, assignment operator, or move assignment operator of T or + // by any InputIterator operation, there are no effects. Otherwise, if an + // exception is thrown, then size()  ≥ n and elements in the range begin() + + // [0, n) are not modified. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + IV device; + auto reference = this->unique(); + + auto res = device.insert_range(device.end(), reference | std::views::take(0)); + EXPECT_EQ(res, device.end()); + + res = device.insert_range(device.end(), reference); + EXPECT_EQ(res, device.begin()); + EXPECT_EQ(device, reference); + device.clear(); + + if (device.capacity() > 0) { + res = device.insert_range(device.end(), reference | std::views::take(1)); + EXPECT_EQ(res, device.begin()); + EXPECT_EQ(device, IV({reference.front()})); + + if (device.capacity() > 1) { + res = device.insert_range( + device.begin() + 1, + std::ranges::subrange(reference.end() - 1, reference.end())); + EXPECT_EQ(res, device.begin() + 1); + EXPECT_EQ(device, IV({reference.front(), reference.back()})); + + if (device.capacity() > 2) { + res = device.insert_range(device.begin() + 1, + reference | std::views::drop(1) | + std::views::take(reference.size() - 2)); + EXPECT_EQ(res, device.begin() + 1); + EXPECT_EQ(device, reference); + } + } + } + + EXPECT_NO_THROW(device.insert_range(device.begin(), std::array{})); + EXPECT_EQ(device, reference); + + EXPECT_THROW(device.insert_range(device.begin(), std::array{T{25}}), + std::bad_alloc); +} + +TYPED_TEST(Modifiers, InsertItrRange) { + // constexpr iterator emplace(const_iterator position, Args&&... args); + // template R> + // constexpr void append_range(R&& rg); + // + // Let n be the value of size() before this call for the append_range + // overload, and distance(begin, position) otherwise. + // Complexity: Linear in the number of elements inserted plus the distance + // to the end of the vector. + // Remarks: If an exception is thrown other than by the copy constructor, + // move constructor, assignment operator, or move assignment operator of T or + // by any InputIterator operation, there are no effects. Otherwise, if an + // exception is thrown, then size()  ≥ n and elements in the range begin() + + // [0, n) are not modified. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + IV device; + auto reference = this->unique(); + + auto res = device.insert(device.end(), reference.end(), reference.end()); + EXPECT_EQ(res, device.end()); + + res = device.insert(device.end(), reference.begin(), reference.end()); + EXPECT_EQ(res, device.begin()); + EXPECT_EQ(device, reference); + device.clear(); + + if (device.capacity() > 0) { + res = device.insert(device.end(), reference.begin(), reference.begin() + 1); + EXPECT_EQ(res, device.begin()); + EXPECT_EQ(device, IV({reference.front()})); + + if (device.capacity() > 1) { + res = device.insert(device.begin() + 1, reference.end() - 1, + reference.end()); + EXPECT_EQ(res, device.begin() + 1); + EXPECT_EQ(device, IV({reference.front(), reference.back()})); + + if (device.capacity() > 2) { + res = device.insert(device.begin() + 1, reference.begin() + 1, + reference.end() - 1); + EXPECT_EQ(res, device.begin() + 1); + EXPECT_EQ(device, reference); + } + } + } + + EXPECT_NO_THROW( + device.insert(device.begin(), reference.end(), reference.end())); + EXPECT_EQ(device, reference); + + std::array single_array{T{25}}; + EXPECT_THROW( + device.insert(device.begin(), single_array.begin(), single_array.end()), + std::bad_alloc); +} + +TYPED_TEST(Modifiers, InsertAppendRange) { + // template R> + // constexpr void append_range(R&& rg); + // + // Let n be the value of size() before this call for the append_range + // overload, and distance(begin, position) otherwise. + // Complexity: Linear in the number of elements inserted plus the distance + // to the end of the vector. + // Remarks: If an exception is thrown other than by the copy constructor, + // move constructor, assignment operator, or move assignment operator of T or + // by any InputIterator operation, there are no effects. Otherwise, if an + // exception is thrown, then size()  ≥ n and elements in the range begin() + + // [0, n) are not modified. + + using IV = TestFixture::IV; + + IV device; + auto reference = this->unique(); + + device.append_range(reference | std::views::take(0)); + EXPECT_EQ(device, IV()); + + device.append_range(reference); + EXPECT_EQ(device, reference); + device.clear(); + + auto half_size = std::midpoint(0ul, reference.size()); + device.append_range(reference | std::views::take(half_size)); + device.append_range(reference | std::views::drop(half_size)); + EXPECT_EQ(device, reference); +} + +TYPED_TEST(Modifiers, PushBackConstRef) { + // constexpr reference push_back(const T& x); + // + // Returns: back(). + // Throws: bad_alloc or any exception thrown by the initialization of the + // inserted element. + // Complexity: Constant. + // Remarks: If an exception is thrown, there are no effects on *this. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + const auto reference = this->unique(); + + IV device; + for (auto i = 0ul; i < reference.size(); ++i) { + auto val = reference[i]; + auto res = device.push_back(val); + EXPECT_EQ(res, device.back()); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1)); + } + + T val{0}; + EXPECT_THROW(device.push_back(val), std::bad_alloc); +} + +TYPED_TEST(Modifiers, PushBackRV) { + // constexpr reference push_back(T&& x); + // + // Returns: back(). + // Throws: bad_alloc or any exception thrown by the initialization of the + // inserted element. + // Complexity: Constant. + // Remarks: If an exception is thrown, there are no effects on *this. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + const auto reference = this->unique(); + + IV device; + for (auto i = 0ul; i < reference.size(); ++i) { + T val{reference[i]}; + auto res = device.push_back(std::move(val)); + EXPECT_EQ(res, device.back()); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1)); + } + + T val{0}; + EXPECT_THROW(device.push_back(val), std::bad_alloc); +} + +// TODO: Check if there's extra copies + +TYPED_TEST(Modifiers, EmplaceBack) { + // template + // constexpr reference emplace_back(Args&&... args); + // + // Returns: back(). + // Throws: bad_alloc or any exception thrown by the initialization of the + // inserted element. + // Complexity: Constant. + // Remarks: If an exception is thrown, there are no effects on *this. + + using IV = TestFixture::IV; + + const auto reference = this->unique(); + + IV device; + for (auto i = 0ul; i < reference.size(); ++i) { + auto res = device.emplace_back(reference[i].value); + EXPECT_EQ(res, device.back()); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1)); + } + + EXPECT_THROW(device.emplace_back(0), std::bad_alloc); +} + +TYPED_TEST(Modifiers, TryEmplaceBack) { + // template + // constexpr pointer try_emplace_back(Args&&... args); + // + // Let vals denote a pack: + // (8.1) std::forward(args)... for the first overload, + // (8.2) x for the second overload, + // (8.3) std::move(x) for the third overload. + // + // Preconditions: value_type is Cpp17EmplaceConstructible into inplace_vector + // from vals.... + // Effects: If size() < capacity() is true, appends an object of type T + // direct-non-list-initialized with vals.... Otherwise, there are no effects. + // Returns: nullptr if size() == capacity() is true, otherwise + // addressof(back()). + // Throws: Nothing unless an exception is thrown by the initialization of the + // inserted element. + // Complexity: Constant. + // Remarks: If an exception is thrown, there are no effects on *this. + + using IV = TestFixture::IV; + + const auto reference = this->unique(); + IV device; + if (!reference.empty()) { + for (auto i = 0ul; i < reference.size(); ++i) { + auto res = device.try_emplace_back(reference[i].value); + EXPECT_EQ(res, std::addressof(device.back())); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1)); + } + + auto res = device.try_emplace_back(reference[0].value); + EXPECT_EQ(res, nullptr); + EXPECT_EQ(device, reference); + } else { + auto res = device.try_emplace_back(0); + EXPECT_EQ(res, nullptr); + EXPECT_EQ(device, IV()); + } +} + +TYPED_TEST(Modifiers, TryPushBackConstRef) { + // constexpr pointer try_push_back(const T& x); + // + // Let vals denote a pack: + // (8.1) std::forward(args)... for the first overload, + // (8.2) x for the second overload, + // (8.3) std::move(x) for the third overload. + // + // Preconditions: value_type is Cpp17EmplaceConstructible into inplace_vector + // from vals.... + // Effects: If size() < capacity() is true, appends an object of type T + // direct-non-list-initialized with vals.... Otherwise, there are no effects. + // Returns: nullptr if size() == capacity() is true, otherwise + // addressof(back()). + // Throws: Nothing unless an exception is thrown by the initialization of the + // inserted element. + // Complexity: Constant. + // Remarks: If an exception is thrown, there are no effects on *this. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + const auto reference = this->unique(); + IV device; + + if (!reference.empty()) { + for (auto i = 0ul; i < reference.size(); ++i) { + auto res = device.try_push_back(reference[i]); + EXPECT_EQ(res, std::addressof(device.back())); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1)); + } + + auto res = device.try_push_back(reference[0]); + EXPECT_EQ(res, nullptr); + EXPECT_EQ(device, reference); + } else { + T val{0}; + + auto res = device.try_push_back(val); + EXPECT_EQ(res, nullptr); + EXPECT_EQ(device, IV()); + } +} + +TYPED_TEST(Modifiers, TryPushBackRV) { + // constexpr pointer try_push_back(T&& x); + // + // Let vals denote a pack: + // (8.1) std::forward(args)... for the first overload, + // (8.2) x for the second overload, + // (8.3) std::move(x) for the third overload. + // + // Preconditions: value_type is Cpp17EmplaceConstructible into inplace_vector + // from vals.... + // Effects: If size() < capacity() is true, appends an object of type T + // direct-non-list-initialized with vals.... Otherwise, there are no effects. + // Returns: nullptr if size() == capacity() is true, otherwise + // addressof(back()). + // Throws: Nothing unless an exception is thrown by the initialization of the + // inserted element. + // Complexity: Constant. + // Remarks: If an exception is thrown, there are no effects on *this. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + const auto reference = this->unique(); + + IV device; + + if (!reference.empty()) { + for (auto i = 0ul; i < reference.size(); ++i) { + T val{reference[i].value}; + + auto res = device.try_push_back(std::move(val)); + EXPECT_EQ(res, std::addressof(device.back())); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1)); + } + + auto res = device.try_push_back(reference[0]); + EXPECT_EQ(res, nullptr); + EXPECT_EQ(device, reference); + } else { + T val{0}; + + auto res = device.try_push_back(std::move(val)); + EXPECT_EQ(res, nullptr); + EXPECT_EQ(device, IV()); + } +} + +TYPED_TEST(Modifiers, TryAppendRanges) { + // template R> + // constexpr ranges::borrowed_iterator_t try_append_range(R&& rg); + // + // Preconditions: value_type is Cpp17EmplaceConstructible into inplace_vector + // from *ranges::begin(rg). + // + // Effects: Appends copies of initial elements + // in rg before end(), until all elements are inserted or size() == capacity() + // is true. Each iterator in the range rg is dereferenced at most once. + // + // Returns: An iterator pointing to the first element of rg that was not + // inserted into *this, or ranges::end(rg) if no such element exists. + // Complexity: Linear in the number of elements inserted. + // + // Remarks: Let n be the value of size() prior to this call. If an exception + // is thrown after the insertion of k elements, then size() equals n + k , + // elements in the range begin() + [0, n) are not modified, and elements in + // the range begin() + [n, n + k) correspond to the inserted elements. + + // TODO + GTEST_SKIP(); +} + +TYPED_TEST(Modifiers, UncheckedEmplacedBack) { + // template + // constexpr reference unchecked_emplace_back(Args&&... args); + // + // Preconditions: size() < capacity() is true. + // Effects: Equivalent to: return + // *try_emplace_back(std::forward(args)...); + + using IV = TestFixture::IV; + + const auto reference = this->unique(); + + IV device; + for (auto i = 0ul; i < reference.size(); ++i) { + auto res = device.unchecked_emplace_back(reference[i].value); + EXPECT_EQ(res, device.back()); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1)); + } +} + +TYPED_TEST(Modifiers, UncheckedPushBackConstRef) { + // constexpr reference unchecked_push_back(const T& x); + // constexpr reference unchecked_push_back(T&& x); + // Preconditions: size() < capacity() is true. + // Effects: Equivalent to: return + // *try_push_back(std​::​forward(x)); + + using IV = TestFixture::IV; + + const auto reference = this->unique(); + + IV device; + for (auto i = 0ul; i < reference.size(); ++i) { + auto res = device.unchecked_push_back(reference[i]); + EXPECT_EQ(res, device.back()); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1)); + } +} + +TYPED_TEST(Modifiers, UncheckedPushBackRV) { + // constexpr reference unchecked_push_back(const T& x); + // constexpr reference unchecked_push_back(T&& x); + // Preconditions: size() < capacity() is true. + // Effects: Equivalent to: return + // *try_push_back(std​::​forward(x)); + + using IV = TestFixture::IV; + using T = TestFixture::T; + + const auto reference = this->unique(); + + IV device; + for (auto i = 0ul; i < reference.size(); ++i) { + T val{reference[i].value}; + + auto res = device.unchecked_push_back(std::move(val)); + EXPECT_EQ(res, device.back()); + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i + 1)); + } +} + +TYPED_TEST(Modifiers, ReserveNonEmpty) { + // static constexpr void reserve(size_type n); + // + // Effects: None. + // Throws: bad_alloc if n > capacity() is true. + + using IV = TestFixture::IV; + + const auto reference = this->unique(); + + IV device(reference); + + device.reserve(device.size()); + EXPECT_EQ(device, reference); + + device.reserve(0); + EXPECT_EQ(device, reference); + + device.reserve(device.capacity()); + EXPECT_EQ(device, reference); + + EXPECT_THROW(device.reserve(device.capacity() + 1), std::bad_alloc); +} + +TYPED_TEST(Modifiers, ReserveEmpty) { + // static constexpr void reserve(size_type n); + // + // Effects: None. + // Throws: bad_alloc if n > capacity() is true. + + using IV = TestFixture::IV; + + IV device; + + device.reserve(device.size()); + EXPECT_EQ(device, IV()); + + device.reserve(0); + EXPECT_EQ(device, IV()); + + device.reserve(device.capacity()); + EXPECT_EQ(device, IV()); + + EXPECT_THROW(device.reserve(device.capacity() + 1), std::bad_alloc); +} + +TYPED_TEST(Modifiers, ShrinkToFitNonEmpty) { + // static constexpr void shrink_to_fit() noexcept; + // Effects: None. + + using IV = TestFixture::IV; + + auto reference = this->unique(); + + IV device(reference); + reference.shrink_to_fit(); + + EXPECT_EQ(device, reference); +} + +TYPED_TEST(Modifiers, ShrinkToFitEmpty) { + // static constexpr void shrink_to_fit() noexcept; + // Effects: None. + + using IV = TestFixture::IV; + + IV device; + device.shrink_to_fit(); + + EXPECT_EQ(device, IV()); +} + +TYPED_TEST(Modifiers, EraseSingle) { + // constexpr iterator erase(const_iterator position); + // + // Effects: Invalidates iterators and references at or after the point of the + // erase. + // Throws: Nothing unless an exception is thrown by the assignment + // operator or move assignment operator of T. + + auto device = this->unique(); + + if (device.empty()) + return; + + auto itr = device.erase(device.begin()); + if (device.empty()) + return; + + EXPECT_EQ(itr, device.begin()); + + auto last_itr = device.end() - 1; + + itr = device.erase(last_itr); + EXPECT_EQ(itr, device.end()); + + auto mid_idx = device.size() / 2; + auto mid_itr = device.begin() + mid_idx; + itr = device.erase(mid_itr); + EXPECT_EQ(itr, device.begin() + mid_idx); + + auto size = device.size(); + for (auto i = 0ul; i < size; ++i) + device.erase(device.begin()); + + EXPECT_TRUE(device.empty()) + << "device still have " << device.size() << " elements"; +} + +TYPED_TEST(Modifiers, EraseSingleConst) { + // constexpr iterator erase(const_iterator position); + // + // Effects: Invalidates iterators and references at or after the point of the + // erase. + // Throws: Nothing unless an exception is thrown by the assignment + // operator or move assignment operator of T. + + auto device = this->unique(); + + if (device.empty()) + return; + + auto itr = device.erase(device.cbegin()); + if (device.empty()) + return; + + EXPECT_EQ(itr, device.cbegin()); + + auto last_itr = device.cend() - 1; + + itr = device.erase(last_itr); + EXPECT_EQ(itr, device.cend()); + + auto mid_idx = device.size() / 2; + auto mid_itr = device.cbegin() + mid_idx; + itr = device.erase(mid_itr); + EXPECT_EQ(itr, device.cbegin() + mid_idx); + + auto size = device.size(); + for (auto i = 0ul; i < size; ++i) + device.erase(device.cbegin()); + + EXPECT_TRUE(device.empty()) + << "device still have " << device.size() << " elements"; +} + +TYPED_TEST(Modifiers, EraseRange) { + // constexpr iterator erase(const_iterator first, const_iterator last); + // + // Effects: Invalidates iterators and references at or after the point of the + // erase. + // Throws: Nothing unless an exception is thrown by the assignment + // operator or move assignment operator of T. + // Complexity: The destructor of T is called the number of times equal to the + // number of the elements erased, but the assignment operator of T is called + // the number of times equal to the number of elements after the erased + // elements. + + using IV = TestFixture::IV; + + auto reference = this->unique(); + IV device(reference); + + auto itr = device.erase(device.begin(), device.begin()); + EXPECT_EQ(itr, device.begin()); + EXPECT_EQ(device, IV(reference)); + + if (device.empty()) + return; + + itr = device.erase(device.begin(), device.begin() + 1); + EXPECT_EQ(itr, device.begin()); + EXPECT_EQ(device, IV(reference.begin() + 1, reference.end())); + + if (device.empty()) + return; + + reference = IV(device); + + auto last_itr = device.end() - 1; + + itr = device.erase(last_itr, device.end()); + EXPECT_EQ(itr, device.end()); + EXPECT_EQ(device, IV(reference.begin(), reference.end() - 1)); + + if (device.size() >= 4) { + reference = IV(device); + + auto from_itr = device.begin() + 1; + auto to_itr = device.end() - 1; + + itr = device.erase(from_itr, to_itr); + EXPECT_EQ(itr, device.begin() + 1); + EXPECT_EQ(device, IV({reference[0], reference.back()})); + } +} + +TYPED_TEST(Modifiers, EraseRangeAll) { + // constexpr iterator erase(const_iterator first, const_iterator last); + // + // Effects: Invalidates iterators and references at or after the point of the + // erase. + // Throws: Nothing unless an exception is thrown by the assignment + // operator or move assignment operator of T. + // Complexity: The destructor of T is called the number of times equal to the + // number of the elements erased, but the assignment operator of T is called + // the number of times equal to the number of elements after the erased + // elements. + + auto device = this->unique(); + auto itr = device.erase(device.begin(), device.end()); + EXPECT_EQ(itr, device.end()); + EXPECT_TRUE(device.empty()); +} + +TYPED_TEST(Modifiers, PopBack) { + // constexpr void pop_back(); + // + // Effects: Invalidates iterators and references at or after the point of the + // erase. + // Throws: Nothing unless an exception is thrown by the assignment + // operator or move assignment operator of T. + // Complexity: The destructor of T is called the number of times equal to the + // number of the elements erased, but the assignment operator of T is called + // the number of times equal to the number of elements after the erased + // elements. + + using IV = TestFixture::IV; + + auto reference = this->unique(); + IV device(reference); + + if (reference.size() == 0) + return; + + for (auto i = int(reference.size()); i > 0; --i) { + EXPECT_EQ(device, IV(reference.begin(), reference.begin() + i)); + device.pop_back(); + } + + EXPECT_TRUE(device.empty()) + << "device still have " << device.size() << " elements"; +} +}; // namespace diff --git a/tests/beman/inplace_vector/ref_impl.test.cpp b/tests/beman/inplace_vector/ref_impl.test.cpp index 9ab7600..c2367b0 100644 --- a/tests/beman/inplace_vector/ref_impl.test.cpp +++ b/tests/beman/inplace_vector/ref_impl.test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #define CHECK(...) \ @@ -183,7 +184,7 @@ template constexpr void test_il_constructor() { for (size_t i = 0; i < N; ++i) CHECK(v[i] == T(i)); CHECK(11 > N); - if !consteval { + if (!std::is_constant_evaluated()) { CHECK_THROWS( ([&] { const vector x({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); }()), std::bad_alloc); @@ -212,7 +213,7 @@ template constexpr void test_il_assignment() { CHECK(v.size() == N); for (size_t i = 0; i < N; ++i) CHECK(v[i] == T(i)); - if !consteval { + if (!std::is_constant_evaluated()) { CHECK_THROWS(([&] { [[maybe_unused]] const vector x = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; diff --git a/tests/beman/inplace_vector/size_n_data.test.cpp b/tests/beman/inplace_vector/size_n_data.test.cpp new file mode 100644 index 0000000..b46414d --- /dev/null +++ b/tests/beman/inplace_vector/size_n_data.test.cpp @@ -0,0 +1,114 @@ +#include +#include + +#include "gtest_setup.hpp" + +namespace { +// 23.3.14.3 Size and capacity [inplace.vector.capacity] + +template class SizeNCapacity : public IVBasicTest {}; +TYPED_TEST_SUITE(SizeNCapacity, IVAllTypes); + +TYPED_TEST(SizeNCapacity, Capacity) { + // static constexpr size_type capacity() noexcept; + // static constexpr size_type max_size() noexcept; + // Returns: N. + + using IV = TestFixture::IV; + constexpr auto N = TestFixture::N; + + EXPECT_EQ(IV::capacity(), N); + IV device; + EXPECT_EQ(device.max_size(), N); +} + +TYPED_TEST(SizeNCapacity, ResizeDown) { + // constexpr void resize(size_type sz); + // Preconditions: T is Cpp17DefaultInsertable into inplace_vector. + // Effects: If sz < size(), erases the last size() - sz elements from the + // sequence. Otherwise, appends sz - size() default-inserted elements to the + // sequence. Remarks: If an exception is thrown, there are no effects on + // *this. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + auto device = this->unique(); + + auto mid_size = std::midpoint(0ul, device.size()); + device.resize(mid_size); + EXPECT_EQ(device, IV(device.begin(), device.begin() + mid_size)); + + device.resize(0); + EXPECT_EQ(device, IV{}); +} + +TYPED_TEST(SizeNCapacity, ResizeUp) { + // constexpr void resize(size_type sz); + // Preconditions: T is Cpp17DefaultInsertable into inplace_vector. + // Effects: If sz < size(), erases the last size() - sz elements from the + // sequence. Otherwise, appends sz - size() default-inserted elements to the + // sequence. Remarks: If an exception is thrown, there are no effects on + // *this. + + using IV = TestFixture::IV; + using T = TestFixture::T; + + IV device; + + EXPECT_THROW(device.resize(device.capacity() + 1), std::bad_alloc); + EXPECT_EQ(device, IV{}); + + if (device.capacity() == 0) + return; + + // Trying to pollute device[0] + device.push_back(T{255}); + device.pop_back(); + EXPECT_TRUE(device.empty()); + + device.resize(1); + EXPECT_EQ(device.size(), 1); + if (std::is_same_v || + std::is_same_v) + EXPECT_EQ(device, IV{T{0}}); + + T front{341}; + device[0] = front; + device.resize(device.capacity()); + EXPECT_EQ(device[0], front); + + if (std::is_same_v || + std::is_same_v) { + IV expected(device.capacity(), T{0}); + expected[0] = front; + EXPECT_EQ(device, expected); + } + + IV before_resize(device); + EXPECT_THROW(device.resize(device.capacity() + 1), std::bad_alloc); + EXPECT_EQ(device, before_resize); +} + +// 23.3.14.4 Data [inplace.vector.data] + +template class Data : public IVBasicTest {}; +TYPED_TEST_SUITE(Data, IVAllTypes); + +TYPED_TEST(Data, Test) { + // constexpr T* data() noexcept; + // constexpr const T* data() const noexcept; + // + // Returns: A pointer such that [data(), data() + size()) is a valid range. + // For a non-empty inplace_vector, data() == addressof(front()) is true. + // Complexity: Constant time. + + auto device = this->unique(); + device.data(); + if (device.capacity() == 0) + return; + + EXPECT_EQ(device.data(), std::addressof(device.front())); +} + +}; // namespace diff --git a/tests/beman/inplace_vector/triviality.test.cpp b/tests/beman/inplace_vector/triviality.test.cpp new file mode 100644 index 0000000..7806697 --- /dev/null +++ b/tests/beman/inplace_vector/triviality.test.cpp @@ -0,0 +1,112 @@ +#include + +#include "gtest_setup.hpp" + +namespace { + +// 6 Let IV denote a specialization of inplace_vector. +// If N is zero, then IV is trivially copyable and empty, and +// std::is_trivially_default_constructible_v is true. Otherwise: +// +// (6.1) — If is_trivially_copy_constructible_v is true, then IV has a +// trivial copy constructor +// +// (6.2) — If is_trivially_move_constructible_v is true, then +// IV has a trivial move constructor. +// +// (6.3) — If is_trivially_destructible_v is true, then: +// (6.3.1) — IV has a trivial destructor. +// (6.3.2) — If is_trivially_copy_constructible_v && +// is_trivially_copy_assignable_v is true, then IV has a trivial copy +// assignment operator. +// (6.3.3) — If is_trivially_move_constructible_v && +// is_trivially_move_assignable_v is true, then IV has a trivial move +// assignment operator. + +template class Triviality : public IVBasicTest {}; +TYPED_TEST_SUITE(Triviality, IVAllTypes); + +TYPED_TEST(Triviality, ZeroSized) { + // 6 Let IV denote a specialization of inplace_vector. + // If N is zero, then IV is trivially copyable and empty, and + // std::is_trivially_default_constructible_v is true. + + constexpr auto N = TestFixture::N; + using IV = TestFixture::IV; + + if constexpr (N == 0) { + EXPECT_TRUE(std::is_trivially_copyable_v); + EXPECT_TRUE(std::is_empty_v); + EXPECT_TRUE(std::is_trivially_default_constructible_v); + } +} + +TYPED_TEST(Triviality, TrivialCopyConstructible) { + // (6.1) — If is_trivially_copy_constructible_v is true, then IV has a + // trivial copy constructor + + using T = TestFixture::T; + using IV = TestFixture::IV; + + if constexpr (std::is_trivially_copy_constructible_v) { + EXPECT_TRUE(std::is_trivially_copy_constructible_v); + } +} + +TYPED_TEST(Triviality, TrivialMoveConstructible) { + // (6.2) — If is_trivially_move_constructible_v is true, then IV has a + // trivial move constructor. + + using T = TestFixture::T; + using IV = TestFixture::IV; + + if constexpr (std::is_trivially_move_constructible_v) { + EXPECT_TRUE(std::is_trivially_move_constructible_v); + } +} + +TYPED_TEST(Triviality, TrivialDestructor) { + // (6.3) — If is_trivially_destructible_v is true, then: + // (6.3.1) — IV has a trivial destructor. + + using T = TestFixture::T; + using IV = TestFixture::IV; + + if constexpr (std::is_trivially_destructible_v) { + EXPECT_TRUE(std::is_trivially_destructible_v); + } +} + +TYPED_TEST(Triviality, TrivialCopyAssignment) { + // (6.3) — If is_trivially_destructible_v is true, then: + // (6.3.2) — If is_trivially_copy_constructible_v && + // is_trivially_copy_assignable_v is true, then IV has a trivial copy + // assignment operator. + + using T = TestFixture::T; + using IV = TestFixture::IV; + + if constexpr (std::is_trivially_destructible_v && + std::is_trivially_copy_constructible_v && + std::is_trivially_copy_assignable_v) { + EXPECT_TRUE(std::is_trivially_copy_assignable_v); + } +} + +TYPED_TEST(Triviality, TrivialMoveAssignment) { + // (6.3) — If is_trivially_destructible_v is true, then: + // (6.3.3) — If is_trivially_move_constructible_v && + // is_trivially_move_assignable_v is true, then IV has a trivial move + // assignment operator. + + using T = TestFixture::T; + using IV = TestFixture::IV; + + if constexpr (std::is_trivially_destructible_v && + std::is_trivially_move_constructible_v && + std::is_trivially_move_assignable_v) { + EXPECT_TRUE(std::is_trivially_move_assignable_v); + } +} + +}; // namespace