-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Constexpr-ify inplace_vector #21
Changes from all commits
48d7243
ba31ac9
f8517de
cc75d2a
8799d0b
cfbc617
446426c
3ba7896
431b0f1
12fe05e
541ad68
6b75318
8dbc733
5ff463a
3ec2600
64fdedc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
||
#include <algorithm> | ||
#include <array> | ||
#include <cassert> | ||
#include <cstddef> | ||
#include <exception> | ||
#include <iterator> | ||
#include <limits> | ||
|
@@ -54,36 +56,68 @@ using inplace_vector_internal_size_type = | |
If<Capacity <= std::numeric_limits<uint32_t>::max(), uint32_t, | ||
uint64_t>>>; | ||
|
||
// array based storage is used so that we can satisfy constexpr requirement | ||
// | ||
// Selecting this storage type implies: std::is_trivial_v<T> or Capacity = 0 | ||
template <typename T, std::size_t Capacity> | ||
struct inplace_vector_type_based_storage { | ||
using array_type = If<!std::is_const_v<T>, std::array<T, Capacity>, | ||
std::array<std::remove_const_t<T>, Capacity>>; | ||
array_type elems; | ||
|
||
constexpr T *begin() { return elems.data(); } | ||
constexpr const T *begin() const { return elems.data(); } | ||
}; | ||
|
||
// byte based storage is used for non-constexpr environment. Objects of type T | ||
// are only constructed when needed and they don't even have to be default | ||
// constructible. | ||
// | ||
// Selecting this storage type implies: !std::is_trivial_v<T> and Capacity != 0 | ||
template <typename T, std::size_t Capacity> | ||
struct inplace_vector_bytes_based_storage { | ||
alignas(T) std::array<std::byte, Capacity * sizeof(T)> elems; | ||
|
||
T *begin() { return std::launder(reinterpret_cast<T *>(elems.data())); } | ||
const T *begin() const { | ||
return std::launder(reinterpret_cast<const T *>(elems.data())); | ||
} | ||
}; | ||
|
||
// Base class for inplace_vector | ||
template <typename T, std::size_t Capacity> | ||
struct inplace_vector_destruct_base { | ||
using size_type = std::size_t; | ||
using internal_size_type = inplace_vector_internal_size_type<Capacity>; | ||
using internal_storage_type = | ||
std::conditional_t<std::is_trivial_v<T> or Capacity == 0, | ||
inplace_vector_type_based_storage<T, Capacity>, | ||
inplace_vector_bytes_based_storage<T, Capacity>>; | ||
|
||
alignas(T) unsigned char elems[Capacity * sizeof(T)] = {}; | ||
internal_storage_type elems; | ||
internal_size_type size_{0}; | ||
|
||
// [containers.sequences.inplace.vector.cons], construct/copy/destroy | ||
constexpr inplace_vector_destruct_base() = default; | ||
|
||
inplace_vector_destruct_base( | ||
constexpr inplace_vector_destruct_base( | ||
const inplace_vector_destruct_base | ||
&other) noexcept(std::is_nothrow_copy_constructible_v<T>) | ||
: elems(), size_(other.size_) {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not your change, but I need to make some remark on all constructors in They all value-initialise Besides (superfluously) initialising There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Unfortunately intention in this repo is to support C++17, so we cannot use requires. This have to be done with template. |
||
|
||
inplace_vector_destruct_base( | ||
constexpr inplace_vector_destruct_base( | ||
const inplace_vector_destruct_base | ||
&&other) noexcept(std::is_nothrow_move_constructible_v<T>) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not your change, but to me this is unconditionally noexcept, since no T's are moved at all. |
||
: elems(), size_(other.size()) {} | ||
: elems(), size_(other.size_) {} | ||
|
||
inplace_vector_destruct_base & | ||
constexpr inplace_vector_destruct_base & | ||
operator=(const inplace_vector_destruct_base &other) noexcept( | ||
std::is_nothrow_copy_constructible_v<T> && | ||
std::is_nothrow_copy_assignable_v<T>) { | ||
Comment on lines
115
to
116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not your change, but to me this is unconditionally noexcept, since no T's are copied at all. |
||
size_ = other.size_; | ||
} | ||
|
||
inplace_vector_destruct_base & | ||
constexpr inplace_vector_destruct_base & | ||
operator=(const inplace_vector_destruct_base &&other) noexcept( | ||
std::is_nothrow_move_constructible_v<T> && | ||
std::is_nothrow_move_assignable_v<T>) { | ||
Comment on lines
122
to
123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not your change, but to me this is unconditionally noexcept, since no T's are copied at all. |
||
|
@@ -108,19 +142,18 @@ struct inplace_vector_base : public inplace_vector_destruct_base<T, Capacity> { | |
|
||
// [containers.sequences.inplace.vector.cons], construct/copy/destroy | ||
constexpr inplace_vector_base() = default; | ||
inplace_vector_base(const inplace_vector_base &other) noexcept( | ||
constexpr inplace_vector_base(const inplace_vector_base &other) noexcept( | ||
std::is_nothrow_copy_constructible_v<T>) | ||
: inplace_vector_destruct_base<T, Capacity>(other.size) { | ||
: inplace_vector_destruct_base<T, Capacity>(other.size_) { | ||
std::copy(other.begin(), other.end(), begin()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not your change, but needs to be an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is |
||
} | ||
inplace_vector_base(inplace_vector_base &&other) noexcept( | ||
constexpr inplace_vector_base(inplace_vector_base &&other) noexcept( | ||
Capacity == 0 || std::is_nothrow_move_constructible_v<T>) | ||
: inplace_vector_destruct_base<T, Capacity>(other.size_) { | ||
std::copy(other.begin(), other.end(), begin()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not your change, but must be |
||
std::destroy(other.begin(), other.end()); | ||
other.size_ = 0; | ||
} | ||
inplace_vector_base &operator=(const inplace_vector_base &other) noexcept( | ||
constexpr inplace_vector_base & | ||
operator=(const inplace_vector_base &other) noexcept( | ||
std::is_nothrow_copy_constructible_v<T> && | ||
std::is_nothrow_copy_assignable_v<T>) { | ||
const auto diff = static_cast<std::ptrdiff_t>(other.size() - size()); | ||
|
@@ -141,7 +174,8 @@ struct inplace_vector_base : public inplace_vector_destruct_base<T, Capacity> { | |
return *this; | ||
} | ||
|
||
inplace_vector_base &operator=(inplace_vector_base &&other) noexcept( | ||
constexpr inplace_vector_base & | ||
operator=(inplace_vector_base &&other) noexcept( | ||
Capacity == 0 || (std::is_nothrow_move_constructible_v<T> && | ||
std::is_nothrow_move_assignable_v<T>)) { | ||
const auto diff = static_cast<std::ptrdiff_t>(other.size() - size()); | ||
|
@@ -151,13 +185,10 @@ struct inplace_vector_base : public inplace_vector_destruct_base<T, Capacity> { | |
std::destroy(new_end, end()); | ||
// other size is grater than size | ||
} else { | ||
std::move(other, other.begin(), other.begin() + size(), begin()); | ||
std::move(other.begin(), other.begin() + size(), begin()); | ||
std::move(other.begin() + size(), other.end(), end()); | ||
} | ||
this->size_ = other.size(); | ||
std::destroy(other.begin(), other.end()); | ||
// reset size to zero | ||
other.change_size(-static_cast<std::ptrdiff_t>(other.size())); | ||
return *this; | ||
} | ||
constexpr inplace_vector_base(const size_type size) | ||
|
@@ -173,28 +204,27 @@ struct inplace_vector_base : public inplace_vector_destruct_base<T, Capacity> { | |
constexpr bool empty() const noexcept { return this->size_ == 0; } | ||
|
||
// [containers.sequences.inplace.vector.data], data access | ||
constexpr T *data() noexcept { | ||
return std::launder(reinterpret_cast<T *>(this->elems)); | ||
} | ||
constexpr const T *data() const noexcept { | ||
return std::launder(reinterpret_cast<const T *>(this->elems)); | ||
} | ||
constexpr T *data() noexcept { return this->elems.begin(); } | ||
constexpr const T *data() const noexcept { return this->elems.begin(); } | ||
|
||
// [containers.sequences.inplace.vector.iterators] iterators | ||
iterator begin() noexcept { | ||
return std::launder(reinterpret_cast<T *>(this->elems)); | ||
constexpr iterator begin() noexcept { | ||
if constexpr (Capacity == 0) | ||
return nullptr; | ||
else | ||
return this->elems.begin(); | ||
} | ||
const_iterator begin() const noexcept { | ||
return std::launder(reinterpret_cast<const T *>(this->elems)); | ||
constexpr const_iterator begin() const noexcept { | ||
if constexpr (Capacity == 0) | ||
return nullptr; | ||
else | ||
return this->elems.begin(); | ||
} | ||
|
||
iterator end() noexcept { | ||
return std::launder(reinterpret_cast<T *>(this->elems) + this->size()); | ||
} | ||
constexpr iterator end() noexcept { return begin() + this->size(); } | ||
|
||
const_iterator end() const noexcept { | ||
return std::launder(reinterpret_cast<const T *>(this->elems) + | ||
this->size()); | ||
constexpr const_iterator end() const noexcept { | ||
return begin() + this->size(); | ||
} | ||
|
||
// [containers.sequences.inplace.vector.modifiers], modifiers | ||
|
@@ -576,7 +606,8 @@ class inplace_vector : public inplace_vector_base<T, Capacity> { | |
#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>(args)...); | ||
// cast to void* here to adapt to a const T | ||
auto final = ::new ((void *)end()) T(std::forward<Args>(args)...); | ||
#endif | ||
this->change_size(1); | ||
return *final; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not your change, but to me this is unconditionally noexcept, since no T's are copied at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay I will mark a TODO here. There's a lot of non-conformity in this implementation.I think we should defer this to another PR, there's too much to be updated.