diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 5cb6c1e..e330c5b 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: [20, 23, 26] + cpp_version: [17, 20, 23, 26] cmake_args: - description: "Default" args: "" @@ -46,6 +46,8 @@ jobs: - description: "ASan" args: "-DCMAKE_CXX_FLAGS=-fsanitize=address -fsanitize=undefined" include: + # Needs C++ 20 as C++17 selectivly disables ranges and concepts + # related functionalities - platform: ubuntu-latest compiler: cpp: g++ diff --git a/README.md b/README.md index d69e1d0..a3011cc 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ which dynamic memory allocations are undesired. ### Compiler support -Building this repository requires **C++20** or later. +Building this repository requires **C++17** or later. ### Dependencies diff --git a/include/beman/inplace_vector/inplace_vector.hpp b/include/beman/inplace_vector/inplace_vector.hpp index 86e2aef..13c2ca2 100644 --- a/include/beman/inplace_vector/inplace_vector.hpp +++ b/include/beman/inplace_vector/inplace_vector.hpp @@ -2,18 +2,29 @@ #include #include -#include -#include #include #include #include #include #include -#include #include #include +#include + +#ifdef __cpp_concepts +#include +#endif + +#ifdef __cpp_lib_containers_ranges +#include +#endif + +#ifdef __cpp_lib_three_way_comparison +#include +#endif namespace beman::inplace_vector { +#ifdef __cpp_concepts namespace detail { // Exposition-only container-compatible-range template @@ -29,6 +40,7 @@ concept container_compatible_range_impl = requires(T &&t) { template constexpr bool container_compatible_range = detail::container_compatible_range_impl; +#endif template using If = typename std::conditional::type; @@ -99,12 +111,12 @@ struct inplace_vector_base : public inplace_vector_destruct_base { inplace_vector_base(const inplace_vector_base &other) noexcept( std::is_nothrow_copy_constructible_v) : inplace_vector_destruct_base(other.size) { - std::ranges::copy(other.begin(), other.end(), begin()); + std::copy(other.begin(), other.end(), begin()); } inplace_vector_base(inplace_vector_base &&other) noexcept( Capacity == 0 || std::is_nothrow_move_constructible_v) : inplace_vector_destruct_base(other.size) { - std::ranges::copy(other.begin(), other.end(), begin()); + std::copy(other.begin(), other.end(), begin()); std::destroy(other.begin(), other.end()); other.size = 0; } @@ -114,17 +126,16 @@ struct inplace_vector_base : public inplace_vector_destruct_base { const auto diff = static_cast(other.size() - size()); // other.size is less than size if (diff < 0) { - const auto new_end = - std::ranges::copy(other.begin(), other.end(), begin()); + const auto new_end = std::copy(other.begin(), other.end(), begin()); // destroy unnecessary memory std::destroy(new_end, end()); } // other.size is greater than size else { // copy other vector into the current vector until it runs ouf of size - std::ranges::copy(other.begin(), other.begin() + size(), begin()); + std::copy(other.begin(), other.begin() + size(), begin()); // copy the other half after the end of the current vector - std::ranges::copy(other.begin() + size(), other.end(), end()); + std::copy(other.begin() + size(), other.end(), end()); } this->size_ = other.size(); return *this; @@ -200,7 +211,7 @@ struct inplace_vector_base : public inplace_vector_destruct_base { template static constexpr void uninitialized_copy(Iter first, Iter last, iterator dest) noexcept { - std::ranges::copy(first, last, dest); + std::copy(first, last, dest); } template @@ -244,6 +255,7 @@ class inplace_vector : public inplace_vector_base { base::uninitialized_fill(this->begin(), this->end(), value); } +#ifdef __cpp_concepts template requires std::input_iterator constexpr inplace_vector(InputIterator first, InputIterator last) : base() { @@ -251,6 +263,7 @@ class inplace_vector : public inplace_vector_base { emplace_back(*first); } } + template requires std::forward_iterator constexpr inplace_vector(InputIterator first, InputIterator last) @@ -259,7 +272,15 @@ class inplace_vector : public inplace_vector_base { base::uninitialized_copy(first, last, this->begin()); } } -#ifdef __cpp_lib_containers_ranges +#else + template constexpr inplace_vector(Itr first, Itr last) : base() { + for (; first != last; ++first) { + emplace_back(*first); + } + } +#endif + +#if defined(__cpp_lib_containers_ranges) && defined(__cpp_concepts) template requires container_compatible_range constexpr inplace_vector(std::from_range_t, R &&rg) { @@ -267,7 +288,6 @@ class inplace_vector : public inplace_vector_base { emplace_back(std::forward(value)); } } -#else #endif constexpr inplace_vector(std::initializer_list il) : base(il.size()) { if (il.size() != 0) { @@ -282,14 +302,13 @@ class inplace_vector : public inplace_vector_base { // The current size is greater if (diff < 0) { // if other.size is less than just copy normally - const iterator new_end = - std::ranges::copy(il.begin(), il.end(), this->begin()); + const iterator new_end = std::copy(il.begin(), il.end(), this->begin()); // destroy the wasted memory std::destroy(new_end, this->end()); // The other size is greater than size } else { // copy other vector into the current vector until it runs ouf of size - std::ranges::copy(il.begin(), il.begin() + this->size(), this->begin()); + std::copy(il.begin(), il.begin() + this->size(), this->begin()); // copy the other half after the end of the current vector base::uninitialized_copy(il.begin() + this->size(), il.end(), this->end()); @@ -318,6 +337,7 @@ class inplace_vector : public inplace_vector_base { } }; // freestanding-deleted +#if defined(__cpp_lib_containers_ranges) and defined(__cpp_concepts) template requires container_compatible_range constexpr void assign_range(R &&rg) { @@ -340,6 +360,8 @@ class inplace_vector : public inplace_vector_base { emplace_back(*first); } }; // freestanding-deleted +#endif + constexpr void assign(size_type n, const T &u) { if (Capacity == 0) { assert(size() == 0 && @@ -366,13 +388,12 @@ class inplace_vector : public inplace_vector_base { // other size is less than size if (diff < 0) { // if other.size is less than just copy normally - const iterator new_end = - std::ranges::copy(il.begin(), il.end(), this->begin()); + const iterator new_end = std::copy(il.begin(), il.end(), this->begin()); std::destroy(new_end, this->end); // other.size is greater than size } else { // copy other vector into the current vector until it runs ouf of size - std::ranges::copy(il.begin(), il.begin() + this->size(), this->begin()); + std::copy(il.begin(), il.begin() + this->size(), this->begin()); // copy the other half after the end of the current vector base::uninitialized_copy(il.begin() + this->size(), il.end(), this->end()); @@ -491,6 +512,8 @@ class inplace_vector : public inplace_vector_base { } return this->unchecked_emplace_back(std::move(x)); }; // freestanding-deleted + +#if defined(__cpp_lib_containers_ranges) and defined(__cpp_concepts) template requires container_compatible_range constexpr void append_range(R &&rg) { @@ -500,6 +523,8 @@ class inplace_vector : public inplace_vector_base { emplace_back(*first); } }; // freestanding-deleted +#endif + constexpr void pop_back() { if (!empty()) { const auto end = this->end(); @@ -546,7 +571,13 @@ class inplace_vector : public inplace_vector_base { template constexpr reference unchecked_emplace_back(Args &&...args) { +#ifdef __cpp_constexpr_dynamic_alloc auto final = std::construct_at(end(), std::forward(args)...); +#else + // Note: placement-new may not be constexpr friendly + // Avoiding placement-new may allow inplace_vector to be constexpr friendly + auto final = ::new (end()) T(std::forward(args)...); +#endif this->change_size(1); return *final; }; @@ -644,13 +675,14 @@ class inplace_vector : public inplace_vector_base { InputIterator imiddle = std::next(first, to_copy); base::uninitialized_copy(imiddle, last, end); base::uninitialized_move(pos, end, middle); - std::ranges::copy(first, imiddle, pos); + std::copy(first, imiddle, pos); } else { base::uninitialized_move(end - count, end, end); std::move_backward(pos, end - count, end); - std::ranges::copy(first, last, pos); + std::copy(first, last, pos); } } // freestanding-deleted +#if defined(__cpp_lib_containers_ranges) and defined(__cpp_concepts) template requires container_compatible_range constexpr iterator insert_range(const_iterator position, R &&rg) { @@ -665,6 +697,7 @@ class inplace_vector : public inplace_vector_base { std::rotate(pos, old_end, this->end()); return pos; } // freestanding-deleted +#endif constexpr iterator insert(const_iterator position, std::initializer_list il) { const iterator pos = position; @@ -744,10 +777,13 @@ class inplace_vector : public inplace_vector_base { return std::equal(x.begin(), x.end(), y.begin(), y.end()); } +#ifdef __cpp_lib_three_way_comparison constexpr friend std::compare_three_way_result operator<=>(const inplace_vector &x, const inplace_vector &y) { return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); }; +#endif + constexpr friend void swap(inplace_vector &x, inplace_vector &y) noexcept( Capacity == 0 || (std::is_nothrow_swappable_v && std::is_nothrow_move_constructible_v)) {