Skip to content
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

Introduce pybind11::detail::is_move_constructible #4631

Merged
merged 1 commit into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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