Files
factory-hole-core/include/Util/InplaceVector.h
2026-02-09 00:53:38 +09:00

732 lines
32 KiB
C++

/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
*/
// Original: https://github.com/TedLyngmo/inplace_vector
// NOLINTNEXTLINE(llvm-header-guard)
#ifndef LYNIPV_F4BA9AA8_99CD_11EF_8916_90B11C0C0FF8
#define LYNIPV_F4BA9AA8_99CD_11EF_8916_90B11C0C0FF8
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <memory>
#include <new>
#if __cplusplus >= 202002L
# include <ranges>
#endif
#include <stdexcept>
#include <type_traits>
#include <utility>
#if __cplusplus >= 201402L
# define LYNIPV_CXX14_CONSTEXPR constexpr
#else
# define LYNIPV_CXX14_CONSTEXPR
#endif
#if __cplusplus >= 202002L
# define LYNIPV_CXX20_CONSTEXPR constexpr
# define LYNIPV_CONSTRUCT_AT(p, ...) std::construct_at(p __VA_OPT__(, ) __VA_ARGS__)
#else
# define LYNIPV_CXX20_CONSTEXPR
# define LYNIPV_CONSTRUCT_AT(p, ...) ::new(static_cast<void*>(p)) T(__VA_ARGS__)
#endif
namespace lyn {
template<class, std::size_t>
class inplace_vector;
namespace lyn_inplace_vector_detail {
#if __cplusplus >= 201703L
using std::is_nothrow_swappable;
#else
template<typename U>
struct is_nothrow_swappable : std::integral_constant<bool, noexcept(swap(std::declval<U&>(), std::declval<U&>()))> {};
#endif
#if __cplusplus >= 202002L
template<class R, class T>
concept container_compatiblel_range = std::ranges::input_range<R> && std::convertible_to<std::ranges::range_reference_t<R>, T>;
#endif
template<class T, std::size_t N>
struct aligned_storage_non_trivial {
constexpr aligned_storage_non_trivial() noexcept {}
using value_type = typename std::remove_const<T>::type;
using size_type = std::size_t;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = value_type*;
using const_pointer = value_type const*;
// destructor
LYNIPV_CXX20_CONSTEXPR ~aligned_storage_non_trivial() noexcept { static_cast<inplace_vector<T, N>*>(this)->clear(); }
LYNIPV_CXX14_CONSTEXPR pointer ptr(size_type idx) noexcept { return std::addressof(m_data[idx].data); }
LYNIPV_CXX14_CONSTEXPR const_pointer ptr(size_type idx) const noexcept { return std::addressof(m_data[idx].data); }
LYNIPV_CXX14_CONSTEXPR reference ref(size_type idx) noexcept { return m_data[idx].data; }
LYNIPV_CXX14_CONSTEXPR const_reference ref(size_type idx) const noexcept { return m_data[idx].data; }
template<class... Args>
LYNIPV_CXX20_CONSTEXPR reference construct(size_type idx, Args&&... args) {
return *LYNIPV_CONSTRUCT_AT(ptr(idx), std::forward<Args>(args)...);
}
LYNIPV_CXX14_CONSTEXPR void destroy(size_type idx) noexcept { ref(idx).~T(); }
LYNIPV_CXX14_CONSTEXPR reference operator[](size_type idx) noexcept { return ref(idx); }
constexpr const_reference operator[](size_type idx) const noexcept { return ref(idx); }
constexpr size_type size() const noexcept { return m_size; }
LYNIPV_CXX14_CONSTEXPR size_type inc() noexcept { return ++m_size; }
LYNIPV_CXX14_CONSTEXPR size_type dec(size_type count = 1) noexcept { return m_size -= count; }
private:
union raw {
LYNIPV_CXX20_CONSTEXPR ~raw() {}
char dummy{};
value_type data;
};
std::array<raw, N> m_data;
static_assert(sizeof m_data == sizeof(T[N]), "erroneous size");
size_type m_size = 0;
};
template<class T, std::size_t N>
struct aligned_storage_trivial {
static_assert(std::is_trivially_destructible<T>::value, "T must be trivially destructible");
constexpr aligned_storage_trivial() noexcept {}
using value_type = typename std::remove_const<T>::type;
using size_type = std::size_t;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = value_type*;
using const_pointer = value_type const*;
LYNIPV_CXX14_CONSTEXPR pointer ptr(size_type idx) noexcept { return std::addressof(m_data[idx].data); }
LYNIPV_CXX14_CONSTEXPR const_pointer ptr(size_type idx) const noexcept { return std::addressof(m_data[idx].data); }
LYNIPV_CXX14_CONSTEXPR reference ref(size_type idx) noexcept { return m_data[idx].data; }
LYNIPV_CXX14_CONSTEXPR const_reference ref(size_type idx) const noexcept { return m_data[idx].data; }
template<class... Args>
LYNIPV_CXX20_CONSTEXPR reference construct(size_type idx, Args&&... args) {
return *LYNIPV_CONSTRUCT_AT(ptr(idx), std::forward<Args>(args)...);
}
LYNIPV_CXX14_CONSTEXPR void destroy(size_type idx) noexcept { ref(idx).~T(); }
LYNIPV_CXX14_CONSTEXPR reference operator[](size_type idx) noexcept { return ref(idx); }
constexpr const_reference operator[](size_type idx) const noexcept { return ref(idx); }
constexpr size_type size() const noexcept { return m_size; }
LYNIPV_CXX14_CONSTEXPR size_type inc() noexcept { return ++m_size; }
LYNIPV_CXX14_CONSTEXPR size_type dec(size_type count = 1) noexcept { return m_size -= count; }
private:
union raw {
constexpr raw() : dummy{} {}
char dummy;
value_type data;
};
std::array<raw, N> m_data;
static_assert(sizeof m_data == sizeof(T[N]), "erroneous size");
size_type m_size = 0;
};
template<class T>
struct aligned_storage_empty { // specialization for 0 elements
using value_type = typename std::remove_const<T>::type;
using size_type = std::size_t;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = value_type*;
using const_pointer = value_type const*;
LYNIPV_CXX14_CONSTEXPR pointer ptr(size_type) { return nullptr; }
LYNIPV_CXX14_CONSTEXPR const_pointer ptr(size_type) const { return nullptr; }
LYNIPV_CXX14_CONSTEXPR reference ref(size_type) { return *ptr(0); }
LYNIPV_CXX14_CONSTEXPR const_reference ref(size_type) const { return *ptr(0); }
template<class... Args>
LYNIPV_CXX20_CONSTEXPR reference construct(size_type, Args&&...) {
return *ptr(0);
}
LYNIPV_CXX14_CONSTEXPR void destroy(size_type) {}
LYNIPV_CXX14_CONSTEXPR reference operator[](size_type) { return *ptr(0); }
constexpr const_reference operator[](size_type) const { return *ptr(0); }
constexpr size_type size() const noexcept { return 0; }
LYNIPV_CXX14_CONSTEXPR size_type inc() { return 0; }
LYNIPV_CXX14_CONSTEXPR size_type dec(size_type = 1) { return 0; }
};
template<class T, std::size_t N>
struct base_selector {
using type =
typename std::conditional<N == 0, aligned_storage_empty<T>,
typename std::conditional<std::is_trivially_copyable<T>::value, aligned_storage_trivial<T, N>,
aligned_storage_non_trivial<T, N>>::type>::type;
};
} // namespace lyn_inplace_vector_detail
template<class T, std::size_t N>
class inplace_vector : public lyn_inplace_vector_detail::base_selector<T, N>::type {
static_assert(std::is_nothrow_destructible<T>::value,
"inplace_vector: classes with potentially throwing destructors are prohibited");
using base = typename lyn_inplace_vector_detail::base_selector<T, N>::type;
using base::construct;
using base::destroy;
using base::ptr;
using base::ref;
public:
using base::size;
using base::operator[];
using value_type = T;
using size_type = std::size_t;
using reference = T&;
using const_reference = T const&;
using pointer = T*;
using const_pointer = T const*;
using iterator = T*;
using const_iterator = T const*;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using difference_type = typename std::iterator_traits<iterator>::difference_type;
private:
LYNIPV_CXX14_CONSTEXPR void shrink_to(const size_type count) noexcept {
while (count != size()) {
pop_back();
}
}
public:
// constructors
constexpr inplace_vector() noexcept = default;
template<bool D = std::is_default_constructible<T>::value, typename std::enable_if<D, int>::type = 0>
LYNIPV_CXX14_CONSTEXPR explicit inplace_vector(size_type count) {
if (count > N) throw std::bad_alloc();
while (count != size()) unchecked_emplace_back();
}
template<bool C = std::is_copy_constructible<T>::value, typename std::enable_if<C, int>::type = 0>
LYNIPV_CXX14_CONSTEXPR inplace_vector(size_type count, const T& value) {
if (count > N) throw std::bad_alloc();
while (count != size()) unchecked_push_back(value);
}
template<class InputIt, typename std::enable_if<
std::is_constructible<typename std::iterator_traits<InputIt>::value_type>::value, int>::type = 0>
LYNIPV_CXX14_CONSTEXPR inplace_vector(InputIt first, InputIt last) {
std::copy(first, last, std::back_inserter(*this));
}
LYNIPV_CXX14_CONSTEXPR inplace_vector(const inplace_vector& other) = default; // for trivial types
template<class U = T,
typename std::enable_if<std::is_copy_constructible<U>::value &&
not std::is_trivially_copy_constructible<typename std::remove_reference<T>::type>::value,
int>::type = 0>
LYNIPV_CXX14_CONSTEXPR inplace_vector(const inplace_vector& other) {
for (size_type idx = 0; idx != other.size(); ++idx) {
unchecked_push_back(other[idx]);
}
}
LYNIPV_CXX14_CONSTEXPR inplace_vector(inplace_vector&& other) noexcept = default; // for trivial types
template<class U = T,
typename std::enable_if<std::is_move_constructible<U>::value &&
not std::is_trivially_move_constructible<typename std::remove_reference<T>::type>::value,
int>::type = 0>
LYNIPV_CXX14_CONSTEXPR inplace_vector(inplace_vector&& other) noexcept(N == 0 || std::is_nothrow_move_constructible<T>::value) {
for (size_type idx = 0; idx != other.size(); ++idx) {
unchecked_push_back(std::move(other[idx]));
}
other.clear();
}
template<bool C = std::is_copy_constructible<T>::value, typename std::enable_if<C, int>::type = 0>
constexpr inplace_vector(std::initializer_list<T> init) : inplace_vector(init.begin(), init.end()) {}
#if __cplusplus >= 202302L && defined(__cpp_lib_containers_ranges)
template<lyn_inplace_vector_detail::container_compatiblel_range<T> R>
constexpr inplace_vector(std::from_range_t, R&& rg) {
if constexpr (std::ranges::sized_range<R>) {
if (std::ranges::size(rg) > N) throw std::bad_alloc();
for (auto&& val : rg) unchecked_emplace_back(std::forward<decltype(val)>(val));
}
else {
for (auto&& val : rg) emplace_back(std::forward<decltype(val)>(val));
}
}
#endif
// assignment
LYNIPV_CXX14_CONSTEXPR inplace_vector& operator=(const inplace_vector& other) = default; // for trivial types
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto operator=(const inplace_vector& other) ->
typename std::enable_if<not(std::is_trivially_destructible<U>::value&& std::is_trivially_copy_constructible<U>::value&&
std::is_trivially_copy_assignable<U>::value),
inplace_vector&>::type {
assign(other.begin(), other.end());
return *this;
}
LYNIPV_CXX14_CONSTEXPR inplace_vector& operator=(inplace_vector&& other) noexcept(
N == 0 || (std::is_nothrow_move_assignable<T>::value)) = default; // for trivial types
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto operator=(inplace_vector&& other) noexcept(
N == 0 || (std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_constructible<T>::value &&
not std::is_trivially_copyable<typename std::remove_reference<T>::type>::value)) ->
typename std::enable_if<not(std::is_trivially_destructible<U>::value&& std::is_trivially_move_constructible<U>::value&&
std::is_trivially_move_assignable<U>::value),
inplace_vector&>::type {
clear();
std::move(other.begin(), other.end(), std::back_inserter(*this));
other.clear();
return *this;
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto operator=(std::initializer_list<T> init) ->
typename std::enable_if<std::is_copy_constructible<U>::value, inplace_vector&>::type {
if (init.size() > capacity()) throw std::bad_alloc();
assign(init.begin(), init.end());
return *this;
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto assign(size_type count, const T& value) ->
typename std::enable_if<std::is_copy_constructible<U>::value>::type {
if (count > capacity()) throw std::bad_alloc();
clear();
while (count != size()) push_back(value);
}
template<class InputIt>
LYNIPV_CXX14_CONSTEXPR auto assign(InputIt first, InputIt last) ->
typename std::enable_if<std::is_constructible<T, typename std::iterator_traits<InputIt>::value_type>::value>::type {
clear();
std::copy(first, last, std::back_inserter(*this));
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto assign(std::initializer_list<T> ilist) ->
typename std::enable_if<std::is_copy_constructible<U>::value>::type {
if (ilist.size() > capacity()) throw std::bad_alloc();
clear();
std::copy(ilist.begin(), ilist.end(), std::back_inserter(*this));
}
#if __cplusplus >= 202002L
template<lyn_inplace_vector_detail::container_compatiblel_range<T> R>
constexpr void assign_range(R&& rg)
requires std::constructible_from<T&, std::ranges::range_reference_t<R>>
{
clear();
append_range(std::forward<R>(rg));
}
template<lyn_inplace_vector_detail::container_compatiblel_range<T> R>
constexpr void append_range(R&& rg)
requires std::constructible_from<T&, std::ranges::range_reference_t<R>>
{
if constexpr (std::ranges::sized_range<R>) {
if (size() + std::ranges::size(rg) > capacity()) throw std::bad_alloc();
for (auto&& val : rg) {
unchecked_emplace_back(std::forward<decltype(val)>(val));
}
}
else {
for (auto&& val : rg) {
emplace_back(std::forward<decltype(val)>(val));
}
}
}
template<lyn_inplace_vector_detail::container_compatiblel_range<T> R>
constexpr std::ranges::borrowed_iterator_t<R> try_append_range(R&& rg)
requires std::constructible_from<T&, std::ranges::range_reference_t<R>>
{
auto it = std::ranges::begin(rg);
for (auto end = std::ranges::end(rg); it != end; std::ranges::advance(it, 1)) {
if (size() == capacity()) break;
unchecked_emplace_back(*it);
}
return it;
}
#endif
// element access
LYNIPV_CXX14_CONSTEXPR reference at(size_type idx) {
if (idx >= size()) throw std::out_of_range("");
return ref(idx);
}
LYNIPV_CXX14_CONSTEXPR const_reference at(size_type idx) const {
if (idx >= size()) throw std::out_of_range("");
return ref(idx);
}
LYNIPV_CXX14_CONSTEXPR reference front() noexcept { return ref(0); }
constexpr const_reference front() const noexcept { return ref(0); }
LYNIPV_CXX14_CONSTEXPR reference back() noexcept { return ref(size() - 1); }
constexpr const_reference back() const noexcept { return ref(size() - 1); }
LYNIPV_CXX14_CONSTEXPR pointer data() noexcept { return ptr(0); }
LYNIPV_CXX14_CONSTEXPR const_pointer data() const noexcept { return ptr(0); }
// iterators
constexpr const_iterator cbegin() const noexcept { return data(); }
constexpr const_iterator cend() const noexcept { return std::next(cbegin(), static_cast<difference_type>(size())); }
constexpr const_iterator begin() const noexcept { return cbegin(); }
constexpr const_iterator end() const noexcept { return cend(); }
LYNIPV_CXX14_CONSTEXPR iterator begin() noexcept { return data(); }
LYNIPV_CXX14_CONSTEXPR iterator end() noexcept { return std::next(begin(), static_cast<difference_type>(size())); }
constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); }
constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); }
constexpr const_reverse_iterator rbegin() const noexcept { return crbegin(); }
constexpr const_reverse_iterator rend() const noexcept { return crend(); }
LYNIPV_CXX14_CONSTEXPR reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
LYNIPV_CXX14_CONSTEXPR reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
// size and capacity
constexpr bool empty() const noexcept { return size() == 0; }
static constexpr size_type max_size() noexcept { return N; }
static constexpr size_type capacity() noexcept { return N; }
private:
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto unchecked_resize(size_type count) ->
typename std::enable_if<std::is_default_constructible<U>::value>::type {
if (count < size()) {
shrink_to(count);
}
else {
while (count != size()) {
unchecked_emplace_back();
}
}
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto unchecked_resize(size_type count, const value_type& value) ->
typename std::enable_if<std::is_copy_constructible<U>::value>::type {
if (count < size()) {
shrink_to(count);
}
else {
while (count != size()) {
unchecked_push_back(value);
}
}
}
public:
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto resize(size_type count) -> typename std::enable_if<std::is_default_constructible<U>::value>::type {
if (count > capacity()) throw std::bad_alloc();
unchecked_resize(count);
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto resize(size_type count, const value_type& value) ->
typename std::enable_if<std::is_copy_constructible<U>::value>::type {
if (count > capacity()) throw std::bad_alloc();
unchecked_resize(count, value);
}
static LYNIPV_CXX14_CONSTEXPR void reserve(size_type new_cap) {
if (new_cap > capacity()) throw std::bad_alloc();
}
static LYNIPV_CXX14_CONSTEXPR void shrink_to_fit() noexcept {}
// modifiers
private:
/*
// optimization idea for all insert() functions to get away from constructing and rotating:
LYNIPV_CXX14_CONSTEXPR size_type make_room_at(const_iterator pos, size_type count) {
// - move construct some T's at current end().
// - move assign some T's before current end().
// - destroy the old host for those "moved from" but not "moved to".
//
// This should leave a nice gap to construct the new range in without the need for move assigning via rotate afterwards.
//
// I don't know what to do about exception guarantees with that implementation though so I'll leave it to something to think
// about. Perhaps it can be used for T's with a non-throwing move assignment operator and move constructor.
// It will at least be ok for trivial types.
}
*/
public:
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto insert(const_iterator pos, const T& value) ->
typename std::enable_if<std::is_copy_constructible<U>::value, iterator>::type {
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
if (size() == capacity()) throw std::bad_alloc();
const auto ncpos = const_cast<iterator>(pos);
unchecked_push_back(value);
std::rotate(ncpos, std::prev(end()), end());
return ncpos;
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto insert(const_iterator pos, T&& value) ->
typename std::enable_if<std::is_move_constructible<U>::value, iterator>::type {
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
if (size() == capacity()) throw std::bad_alloc();
const auto ncpos = const_cast<iterator>(pos);
unchecked_push_back(std::move(value));
std::rotate(ncpos, std::prev(end()), end());
return ncpos;
}
template<class U = T>
LYNIPV_CXX20_CONSTEXPR auto insert(const_iterator pos, size_type count, const T& value) ->
typename std::enable_if<std::is_copy_constructible<U>::value, iterator>::type {
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
if (size() + count > capacity()) throw std::bad_alloc();
const auto ncpos = const_cast<iterator>(pos);
auto oldsize = size();
auto first_inserted = end();
try {
while (count--) {
unchecked_push_back(value);
}
}
catch (...) {
shrink_to(oldsize);
throw;
}
std::rotate(ncpos, first_inserted, end());
return ncpos;
}
template<class InputIt, class U = T>
LYNIPV_CXX20_CONSTEXPR auto insert(const_iterator pos, InputIt first, InputIt last) ->
typename std::enable_if<std::is_constructible<typename std::iterator_traits<InputIt>::value_type>::value &&
!std::is_const<U>::value,
iterator>::type {
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
const auto ncpos = const_cast<iterator>(pos);
auto oldsize = size();
auto first_inserted = end();
try {
for (; first != last; std::advance(first, 1)) {
push_back(*first);
}
}
catch (...) {
shrink_to(oldsize);
throw;
}
std::rotate(ncpos, first_inserted, end());
return ncpos;
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto insert(const_iterator pos, std::initializer_list<T> ilist) ->
typename std::enable_if<std::is_copy_constructible<U>::value && !std::is_const<U>::value, iterator>::type {
return insert(pos, ilist.begin(), ilist.end());
}
template<class... Args>
LYNIPV_CXX14_CONSTEXPR auto emplace(const_iterator pos, Args&&... args) ->
typename std::enable_if<std::is_constructible<T, Args...>::value, iterator>::type {
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
const auto ncpos = const_cast<iterator>(pos);
emplace_back(std::forward<Args>(args)...);
std::rotate(ncpos, std::prev(end()), end());
return ncpos;
}
template<class... Args>
LYNIPV_CXX14_CONSTEXPR auto unchecked_emplace_back(Args&&... args) ->
typename std::enable_if<std::is_constructible<T, Args...>::value, reference>::type {
auto& rv = construct(size(), std::forward<Args>(args)...);
this->inc();
return rv;
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto unchecked_push_back(T const& value) ->
typename std::enable_if<std::is_copy_constructible<U>::value, reference>::type {
auto& rv = construct(size(), value);
this->inc();
return rv;
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto unchecked_push_back(T&& value) ->
typename std::enable_if<std::is_move_constructible<U>::value, reference>::type {
auto& rv = construct(size(), std::move(value));
this->inc();
return rv;
}
template<class... Args>
LYNIPV_CXX14_CONSTEXPR auto emplace_back(Args&&... args) ->
typename std::enable_if<std::is_constructible<T, Args...>::value, reference>::type {
if (size() == N) throw std::bad_alloc();
return unchecked_emplace_back(std::forward<Args>(args)...);
}
template<class... Args>
LYNIPV_CXX14_CONSTEXPR auto try_emplace_back(Args&&... args) ->
typename std::enable_if<std::is_constructible<T, Args...>::value, pointer>::type {
if (size() == N) return nullptr;
return std::addressof(unchecked_emplace_back(std::forward<Args>(args)...));
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto push_back(T const& value) ->
typename std::enable_if<std::is_copy_constructible<U>::value, reference>::type {
if (size() == N) throw std::bad_alloc();
return unchecked_push_back(value);
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto push_back(T&& value) ->
typename std::enable_if<std::is_move_constructible<U>::value, reference>::type {
if (size() == N) throw std::bad_alloc();
return unchecked_push_back(std::move(value));
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto try_push_back(T const& value) ->
typename std::enable_if<std::is_copy_constructible<U>::value, pointer>::type {
if (size() == N) return nullptr;
return std::addressof(unchecked_push_back(value));
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto try_push_back(T&& value) ->
typename std::enable_if<std::is_move_constructible<U>::value, pointer>::type {
if (size() == N) return nullptr;
return std::addressof(unchecked_push_back(std::move(value)));
}
LYNIPV_CXX14_CONSTEXPR void pop_back() noexcept { destroy(this->dec()); }
LYNIPV_CXX14_CONSTEXPR void clear() noexcept { shrink_to(0); }
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto erase(const_iterator first, const_iterator last) ->
typename std::enable_if<!std::is_const<U>::value, iterator>::type {
auto ncfirst = const_cast<iterator>(first);
auto nclast = const_cast<iterator>(last);
auto removed = static_cast<std::size_t>(std::distance(ncfirst, nclast));
std::move(nclast, end(), ncfirst);
for (size_type idx = size() - removed; idx < size(); ++idx) {
destroy(idx);
}
this->dec(removed);
return ncfirst;
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto erase(const_iterator pos) -> typename std::enable_if<!std::is_const<U>::value, iterator>::type {
return erase(pos, std::next(pos));
}
template<class U = T>
LYNIPV_CXX14_CONSTEXPR auto swap(inplace_vector& other) noexcept(N == 0 ||
(lyn_inplace_vector_detail::is_nothrow_swappable<T>::value &&
std::is_nothrow_move_constructible<T>::value)) ->
typename std::enable_if<!std::is_const<U>::value>::type {
auto&& p = (size() < other.size()) ? std::pair<inplace_vector&, inplace_vector&>(*this, other)
: std::pair<inplace_vector&, inplace_vector&>(other, *this);
auto& small = p.first;
auto& large = p.second;
size_type idx = 0, small_size = small.size();
for (; idx < small_size; ++idx) {
using std::swap;
swap(small[idx], large[idx]);
}
for (; idx < large.size(); ++idx) {
small.push_back(std::move(large[idx]));
}
large.shrink_to(small_size);
}
LYNIPV_CXX14_CONSTEXPR void friend swap(inplace_vector& lhs, inplace_vector& rhs) noexcept(
N == 0 || (lyn_inplace_vector_detail::is_nothrow_swappable<T>::value && std::is_nothrow_move_constructible<T>::value)) {
lhs.swap(rhs);
}
#if __cplusplus >= 202002L
constexpr friend auto operator<=>(const inplace_vector& lhs, const inplace_vector& rhs) {
return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
#else
friend bool operator<(const inplace_vector& lhs, const inplace_vector& rhs) {
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
friend bool operator>(const inplace_vector& lhs, const inplace_vector& rhs) { return rhs < lhs; }
friend bool operator<=(const inplace_vector& lhs, const inplace_vector& rhs) { return !(rhs < lhs); }
friend bool operator>=(const inplace_vector& lhs, const inplace_vector& rhs) { return rhs <= lhs; }
friend bool operator!=(const inplace_vector& lhs, const inplace_vector& rhs) { return !(lhs == rhs); }
#endif
friend bool operator==(const inplace_vector& lhs, const inplace_vector& rhs) {
if (lhs.size() != rhs.size()) return false;
return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
}
};
template<class T, size_t N, class U = T>
LYNIPV_CXX14_CONSTEXPR typename inplace_vector<T, N>::size_type erase(inplace_vector<T, N>& c, const U& value) {
auto it = std::remove(c.begin(), c.end(), value);
auto r = static_cast<typename inplace_vector<T, N>::size_type>(std::distance(it, c.end()));
c.erase(it, it.end());
return r;
}
template<class T, size_t N, class Predicate>
LYNIPV_CXX14_CONSTEXPR typename inplace_vector<T, N>::size_type erase_if(inplace_vector<T, N>& c, Predicate pred) {
auto it = std::remove_if(c.begin(), c.end(), pred);
auto r = static_cast<typename inplace_vector<T, N>::size_type>(std::distance(it, c.end()));
c.erase(it, c.end());
return r;
}
} // namespace lyn
// clean up defines
#undef LYNIPV_CXX14_CONSTEXPR
#undef LYNIPV_CXX20_CONSTEXPR
#undef LYNIPV_CONSTRUCT_AT
#endif