Skip to content

Commit

Permalink
Introduce pybind11::detail::is_move_constructible (#4631)
Browse files Browse the repository at this point in the history
To support the use case captured in the new test_vector_unique_ptr_member.cpp
  • Loading branch information
Ralf W. Grosse-Kunstleve authored Apr 24, 2023
1 parent 071f35a commit 07725c2
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 5 deletions.
4 changes: 2 additions & 2 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ struct move_always<
enable_if_t<
all_of<move_is_plain_type<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::true_type {};
template <typename T, typename SFINAE = void>
Expand All @@ -975,7 +975,7 @@ struct move_if_unreferenced<
enable_if_t<
all_of<move_is_plain_type<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::true_type {};
template <typename T>
Expand Down
4 changes: 2 additions & 2 deletions include/pybind11/detail/init.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
template <typename Class>
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool 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");
if (Class::has_alias && need_alias) {
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
Expand All @@ -190,7 +190,7 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
template <typename Class>
void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
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");
v_h.value_ptr() = new Alias<Class>(std::move(result));
}
Expand Down
5 changes: 4 additions & 1 deletion include/pybind11/detail/type_caster_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,9 @@ using movable_cast_op_type
template <typename T, typename SFINAE = void>
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
// (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.
Expand Down Expand Up @@ -994,7 +997,7 @@ class type_caster_base : public type_caster_generic {
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 *)
-> decltype(new T(std::declval<T &&>()), Constructor{}) {
return [](const void *arg) -> void * {
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ set(PYBIND11_TEST_FILES
test_tagbased_polymorphic
test_thread
test_union
test_vector_unique_ptr_member
test_virtual_functions)

# Invoking cmake with something like:
Expand Down
56 changes: 56 additions & 0 deletions tests/test_vector_unique_ptr_member.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
14 changes: 14 additions & 0 deletions tests/test_vector_unique_ptr_member.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 07725c2

Please sign in to comment.