diff --git a/include/pybind11/detail/argument_vector.h b/include/pybind11/detail/argument_vector.h index e15a3cfab..c170b5848 100644 --- a/include/pybind11/detail/argument_vector.h +++ b/include/pybind11/detail/argument_vector.h @@ -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, "this class is not exception safe"); + inline_array iarray; heap_vector hvector; - static_assert(std::is_trivially_move_constructible::value, - "ArrayT must be trivially move constructible"); - static_assert(std::is_trivially_destructible::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(this), sizeof(bool)); return result; } -}; -// small_vector-like container to avoid heap allocation for N or fewer -// arguments. -template -struct argument_vector { -public: - argument_vector() = default; + void destruct() { + if (!is_inline()) { + hvector.~heap_vector(); + } + else if (!std::is_trivially_destructible::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 +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 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; + using repr_type = inline_array_or_vector; 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, "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 -struct args_convert_vector { +struct small_vector { 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 +using argument_vector = small_vector; + +// Container to avoid heap allocation for N or fewer booleans. +template +using args_convert_vector = small_vector; + PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)