mirror of
https://github.com/pybind/pybind11.git
synced 2026-03-14 20:27:47 +00:00
Make argument_vector re-usable for other types.
This commit is contained in:
@@ -63,53 +63,11 @@ union inline_array_or_vector {
|
||||
heap_vector(std::size_t count, VectorT value) : vec(count, value) {}
|
||||
};
|
||||
|
||||
static_assert(std::is_nothrow_move_assignable_v<ArrayT>, "this class is not exception safe");
|
||||
|
||||
inline_array iarray;
|
||||
heap_vector hvector;
|
||||
|
||||
static_assert(std::is_trivially_move_constructible<ArrayT>::value,
|
||||
"ArrayT must be trivially move constructible");
|
||||
static_assert(std::is_trivially_destructible<ArrayT>::value,
|
||||
"ArrayT must be trivially destructible");
|
||||
|
||||
inline_array_or_vector() : iarray() {}
|
||||
~inline_array_or_vector() {
|
||||
if (!is_inline()) {
|
||||
hvector.~heap_vector();
|
||||
}
|
||||
}
|
||||
// Disable copy ctor and assignment.
|
||||
inline_array_or_vector(const inline_array_or_vector &) = delete;
|
||||
inline_array_or_vector &operator=(const inline_array_or_vector &) = delete;
|
||||
|
||||
inline_array_or_vector(inline_array_or_vector &&rhs) noexcept {
|
||||
if (rhs.is_inline()) {
|
||||
std::memcpy(&iarray, &rhs.iarray, sizeof(iarray));
|
||||
} else {
|
||||
new (&hvector) heap_vector(std::move(rhs.hvector));
|
||||
}
|
||||
assert(is_inline() == rhs.is_inline());
|
||||
}
|
||||
|
||||
inline_array_or_vector &operator=(inline_array_or_vector &&rhs) noexcept {
|
||||
if (this == &rhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (rhs.is_inline()) {
|
||||
if (!is_inline()) {
|
||||
hvector.~heap_vector();
|
||||
}
|
||||
std::memcpy(&iarray, &rhs.iarray, sizeof(iarray));
|
||||
} else {
|
||||
if (is_inline()) {
|
||||
new (&hvector) heap_vector(std::move(rhs.hvector));
|
||||
} else {
|
||||
hvector = std::move(rhs.hvector);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_inline() const {
|
||||
// It is undefined behavior to access the inactive member of a
|
||||
// union directly. However, it is well-defined to reinterpret_cast any
|
||||
@@ -124,20 +82,64 @@ union inline_array_or_vector {
|
||||
std::memcpy(&result, reinterpret_cast<const char *>(this), sizeof(bool));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// small_vector-like container to avoid heap allocation for N or fewer
|
||||
// arguments.
|
||||
template <std::size_t N>
|
||||
struct argument_vector {
|
||||
public:
|
||||
argument_vector() = default;
|
||||
void destruct() {
|
||||
if (!is_inline()) {
|
||||
hvector.~heap_vector();
|
||||
}
|
||||
else if (!std::is_trivially_destructible<ArrayT>::value) {
|
||||
for (size_t i = 0; i < iarray.size; ++i) {
|
||||
iarray.arr[i].~ArrayT();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline_array_or_vector() : iarray() {}
|
||||
|
||||
~inline_array_or_vector() {
|
||||
destruct();
|
||||
}
|
||||
|
||||
// Disable copy ctor and assignment.
|
||||
argument_vector(const argument_vector &) = delete;
|
||||
argument_vector &operator=(const argument_vector &) = delete;
|
||||
argument_vector(argument_vector &&) noexcept = default;
|
||||
argument_vector &operator=(argument_vector &&) noexcept = default;
|
||||
inline_array_or_vector(const inline_array_or_vector &) = delete;
|
||||
inline_array_or_vector &operator=(const inline_array_or_vector &) = delete;
|
||||
|
||||
inline_array_or_vector(inline_array_or_vector &&rhs) noexcept {
|
||||
if (rhs.is_inline()) {
|
||||
new (&iarray) inline_array(std::move(rhs.iarray));
|
||||
} else {
|
||||
new (&hvector) heap_vector(std::move(rhs.hvector));
|
||||
}
|
||||
assert(is_inline() == rhs.is_inline());
|
||||
}
|
||||
|
||||
inline_array_or_vector &operator=(inline_array_or_vector &&rhs) noexcept {
|
||||
if (this == &rhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
destruct();
|
||||
|
||||
if (rhs.is_inline()) {
|
||||
new (&iarray) inline_array(std::move(rhs.iarray));
|
||||
} else {
|
||||
new (&hvector) heap_vector(std::move(rhs.hvector));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T, std::size_t InlineSize>
|
||||
struct small_vector {
|
||||
public:
|
||||
small_vector() = default;
|
||||
|
||||
// Disable copy ctor and assignment.
|
||||
small_vector(const small_vector &) = delete;
|
||||
small_vector &operator=(const small_vector &) = delete;
|
||||
small_vector(small_vector &&) noexcept = default;
|
||||
small_vector &operator=(small_vector &&) noexcept = default;
|
||||
|
||||
std::size_t size() const {
|
||||
if (is_inline()) {
|
||||
@@ -146,7 +148,14 @@ public:
|
||||
return m_repr.hvector.vec.size();
|
||||
}
|
||||
|
||||
handle &operator[](std::size_t idx) {
|
||||
T const *data() const {
|
||||
if (is_inline()) {
|
||||
return &m_repr.iarray.arr[0];
|
||||
}
|
||||
return m_repr.hvector.vec.data();
|
||||
}
|
||||
|
||||
T &operator[](std::size_t idx) {
|
||||
assert(idx < size());
|
||||
if (is_inline()) {
|
||||
return m_repr.iarray.arr[idx];
|
||||
@@ -154,7 +163,7 @@ public:
|
||||
return m_repr.hvector.vec[idx];
|
||||
}
|
||||
|
||||
handle operator[](std::size_t idx) const {
|
||||
T operator[](std::size_t idx) const {
|
||||
assert(idx < size());
|
||||
if (is_inline()) {
|
||||
return m_repr.iarray.arr[idx];
|
||||
@@ -162,28 +171,28 @@ public:
|
||||
return m_repr.hvector.vec[idx];
|
||||
}
|
||||
|
||||
void push_back(handle x) {
|
||||
void push_back(T x) {
|
||||
if (is_inline()) {
|
||||
auto &ha = m_repr.iarray;
|
||||
if (ha.size == N) {
|
||||
move_to_heap_vector_with_reserved_size(N + 1);
|
||||
push_back_slow_path(x);
|
||||
if (ha.size == InlineSize) {
|
||||
move_to_heap_vector_with_reserved_size(InlineSize + 1);
|
||||
push_back_slow_path(std::move(x));
|
||||
} else {
|
||||
ha.arr[ha.size++] = x;
|
||||
ha.arr[ha.size++] = std::move(x);
|
||||
}
|
||||
} else {
|
||||
push_back_slow_path(x);
|
||||
push_back_slow_path(std::move(x));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Arg>
|
||||
void emplace_back(Arg &&x) {
|
||||
push_back(handle(x));
|
||||
push_back(T(x));
|
||||
}
|
||||
|
||||
void reserve(std::size_t sz) {
|
||||
if (is_inline()) {
|
||||
if (sz > N) {
|
||||
if (sz > InlineSize) {
|
||||
move_to_heap_vector_with_reserved_size(sz);
|
||||
}
|
||||
} else {
|
||||
@@ -192,7 +201,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
using repr_type = inline_array_or_vector<handle, N>;
|
||||
using repr_type = inline_array_or_vector<T, InlineSize>;
|
||||
repr_type m_repr;
|
||||
|
||||
PYBIND11_NOINLINE void move_to_heap_vector_with_reserved_size(std::size_t reserved_size) {
|
||||
@@ -201,32 +210,32 @@ private:
|
||||
using heap_vector = typename repr_type::heap_vector;
|
||||
heap_vector hv;
|
||||
hv.vec.reserve(reserved_size);
|
||||
std::copy(ha.arr.begin(), ha.arr.begin() + ha.size, std::back_inserter(hv.vec));
|
||||
static_assert(std::is_nothrow_move_assignable_v<T>, "this class is not exception safe");
|
||||
std::move(ha.arr.begin(), ha.arr.begin() + ha.size, std::back_inserter(hv.vec));
|
||||
new (&m_repr.hvector) heap_vector(std::move(hv));
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE void push_back_slow_path(handle x) { m_repr.hvector.vec.push_back(x); }
|
||||
PYBIND11_NOINLINE void push_back_slow_path(T x) { m_repr.hvector.vec.push_back(std::move(x)); }
|
||||
|
||||
PYBIND11_NOINLINE void reserve_slow_path(std::size_t sz) { m_repr.hvector.vec.reserve(sz); }
|
||||
|
||||
bool is_inline() const { return m_repr.is_inline(); }
|
||||
};
|
||||
|
||||
// small_vector-like container to avoid heap allocation for N or fewer
|
||||
// arguments.
|
||||
// Container to avoid heap allocation for kRequestedInlineSize or fewer booleans.
|
||||
template <std::size_t kRequestedInlineSize>
|
||||
struct args_convert_vector {
|
||||
struct small_vector<bool, kRequestedInlineSize> {
|
||||
private:
|
||||
public:
|
||||
args_convert_vector() = default;
|
||||
small_vector() = default;
|
||||
|
||||
// Disable copy ctor and assignment.
|
||||
args_convert_vector(const args_convert_vector &) = delete;
|
||||
args_convert_vector &operator=(const args_convert_vector &) = delete;
|
||||
args_convert_vector(args_convert_vector &&) noexcept = default;
|
||||
args_convert_vector &operator=(args_convert_vector &&) noexcept = default;
|
||||
small_vector(const small_vector &) = delete;
|
||||
small_vector &operator=(const small_vector &) = delete;
|
||||
small_vector(small_vector &&) noexcept = default;
|
||||
small_vector &operator=(small_vector &&) noexcept = default;
|
||||
|
||||
args_convert_vector(std::size_t count, bool value) {
|
||||
small_vector(std::size_t count, bool value) {
|
||||
if (count > kInlineSize) {
|
||||
new (&m_repr.hvector) typename repr_type::heap_vector(count, value);
|
||||
} else {
|
||||
@@ -284,7 +293,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void swap(args_convert_vector &rhs) noexcept { std::swap(m_repr, rhs.m_repr); }
|
||||
void swap(small_vector &rhs) noexcept { std::swap(m_repr, rhs.m_repr); }
|
||||
|
||||
private:
|
||||
struct WordAndBitIndex {
|
||||
@@ -326,5 +335,13 @@ private:
|
||||
bool is_inline() const { return m_repr.is_inline(); }
|
||||
};
|
||||
|
||||
// Container to avoid heap allocation for N or fewer arguments.
|
||||
template <size_t N>
|
||||
using argument_vector = small_vector<handle, N>;
|
||||
|
||||
// Container to avoid heap allocation for N or fewer booleans.
|
||||
template <size_t N>
|
||||
using args_convert_vector = small_vector<bool, N>;
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
Reference in New Issue
Block a user