Introduce pybind11::detail::is_move_constructible (#4631)

To support the use case captured in the new test_vector_unique_ptr_member.cpp
This commit is contained in:
Ralf W. Grosse-Kunstleve 2023-04-24 00:19:21 -07:00 committed by GitHub
parent 071f35ab85
commit 07725c28c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 79 additions and 5 deletions

View File

@ -964,7 +964,7 @@ struct move_always<
enable_if_t< enable_if_t<
all_of<move_is_plain_type<T>, all_of<move_is_plain_type<T>,
negation<is_copy_constructible<T>>, negation<is_copy_constructible<T>>,
std::is_move_constructible<T>, is_move_constructible<T>,
std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>> std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>>
: std::true_type {}; : std::true_type {};
template <typename T, typename SFINAE = void> template <typename T, typename SFINAE = void>
@ -975,7 +975,7 @@ struct move_if_unreferenced<
enable_if_t< enable_if_t<
all_of<move_is_plain_type<T>, all_of<move_is_plain_type<T>,
negation<move_always<T>>, negation<move_always<T>>,
std::is_move_constructible<T>, is_move_constructible<T>,
std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>> std::is_same<decltype(std::declval<make_caster<T>>().operator T &()), T &>>::value>>
: std::true_type {}; : std::true_type {};
template <typename T> template <typename T>

View File

@ -175,7 +175,7 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
template <typename Class> template <typename Class>
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) { void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
static_assert(std::is_move_constructible<Cpp<Class>>::value, static_assert(is_move_constructible<Cpp<Class>>::value,
"pybind11::init() return-by-value factory function requires a movable class"); "pybind11::init() return-by-value factory function requires a movable class");
if (Class::has_alias && need_alias) { if (Class::has_alias && need_alias) {
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result)); construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
@ -190,7 +190,7 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
template <typename Class> template <typename Class>
void construct(value_and_holder &v_h, Alias<Class> &&result, bool) { void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
static_assert( static_assert(
std::is_move_constructible<Alias<Class>>::value, is_move_constructible<Alias<Class>>::value,
"pybind11::init() return-by-alias-value factory function requires a movable alias class"); "pybind11::init() return-by-alias-value factory function requires a movable alias class");
v_h.value_ptr() = new Alias<Class>(std::move(result)); v_h.value_ptr() = new Alias<Class>(std::move(result));
} }

View File

@ -827,6 +827,9 @@ using movable_cast_op_type
template <typename T, typename SFINAE = void> template <typename T, typename SFINAE = void>
struct is_copy_constructible : std::is_copy_constructible<T> {}; struct is_copy_constructible : std::is_copy_constructible<T> {};
template <typename T, typename SFINAE = void>
struct is_move_constructible : std::is_move_constructible<T> {};
// Specialization for types that appear to be copy constructible but also look like stl containers // Specialization for types that appear to be copy constructible but also look like stl containers
// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if // (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
// so, copy constructability depends on whether the value_type is copy constructible. // so, copy constructability depends on whether the value_type is copy constructible.
@ -994,7 +997,7 @@ protected:
return [](const void *arg) -> void * { return new T(*reinterpret_cast<const T *>(arg)); }; return [](const void *arg) -> void * { return new T(*reinterpret_cast<const T *>(arg)); };
} }
template <typename T, typename = enable_if_t<std::is_move_constructible<T>::value>> template <typename T, typename = enable_if_t<is_move_constructible<T>::value>>
static auto make_move_constructor(const T *) static auto make_move_constructor(const T *)
-> decltype(new T(std::declval<T &&>()), Constructor{}) { -> decltype(new T(std::declval<T &&>()), Constructor{}) {
return [](const void *arg) -> void * { return [](const void *arg) -> void * {

View File

@ -155,6 +155,7 @@ set(PYBIND11_TEST_FILES
test_tagbased_polymorphic test_tagbased_polymorphic
test_thread test_thread
test_union test_union
test_vector_unique_ptr_member
test_virtual_functions) test_virtual_functions)
# Invoking cmake with something like: # Invoking cmake with something like:

View File

@ -0,0 +1,56 @@
#include "pybind11_tests.h"
#include <cstddef>
#include <memory>
#include <vector>
namespace pybind11_tests {
namespace vector_unique_ptr_member {
struct DataType {};
// Reduced from a use case in the wild.
struct VectorOwner {
static std::unique_ptr<VectorOwner> Create(std::size_t num_elems) {
return std::unique_ptr<VectorOwner>(
new VectorOwner(std::vector<std::unique_ptr<DataType>>(num_elems)));
}
std::size_t data_size() const { return data_.size(); }
private:
explicit VectorOwner(std::vector<std::unique_ptr<DataType>> data) : data_(std::move(data)) {}
const std::vector<std::unique_ptr<DataType>> data_;
};
} // namespace vector_unique_ptr_member
} // namespace pybind11_tests
namespace pybind11 {
namespace detail {
template <>
struct is_copy_constructible<pybind11_tests::vector_unique_ptr_member::VectorOwner>
: std::false_type {};
template <>
struct is_move_constructible<pybind11_tests::vector_unique_ptr_member::VectorOwner>
: std::false_type {};
} // namespace detail
} // namespace pybind11
using namespace pybind11_tests::vector_unique_ptr_member;
py::object py_cast_VectorOwner_ptr(VectorOwner *ptr) { return py::cast(ptr); }
// PYBIND11_SMART_HOLDER_TYPE_CASTERS(VectorOwner)
TEST_SUBMODULE(vector_unique_ptr_member, m) {
py::class_<VectorOwner>(m, "VectorOwner")
.def_static("Create", &VectorOwner::Create)
.def("data_size", &VectorOwner::data_size);
m.def("py_cast_VectorOwner_ptr", py_cast_VectorOwner_ptr);
}

View File

@ -0,0 +1,14 @@
import pytest
from pybind11_tests import vector_unique_ptr_member as m
@pytest.mark.parametrize("num_elems", range(3))
def test_create(num_elems):
vo = m.VectorOwner.Create(num_elems)
assert vo.data_size() == num_elems
def test_cast():
vo = m.VectorOwner.Create(0)
assert m.py_cast_VectorOwner_ptr(vo) is vo