μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
test_copy_move.cpp
Go to the documentation of this file.
1/*
2 tests/test_copy_move_policies.cpp -- 'copy' and 'move' return value policies
3 and related tests
4
5 Copyright (c) 2016 Ben North <ben@redfrontdoor.org>
6
7 All rights reserved. Use of this source code is governed by a
8 BSD-style license that can be found in the LICENSE file.
9*/
10
11#include <pybind11/stl.h>
12
13#include "constructor_stats.h"
14#include "pybind11_tests.h"
15
16template <typename derived>
17struct empty {
18 static const derived &get_one() { return instance_; }
19 static derived instance_;
20};
21
22struct lacking_copy_ctor : public empty<lacking_copy_ctor> {
23 lacking_copy_ctor() = default;
24 lacking_copy_ctor(const lacking_copy_ctor &other) = delete;
25};
26
27template <>
29
30struct lacking_move_ctor : public empty<lacking_move_ctor> {
31 lacking_move_ctor() = default;
32 lacking_move_ctor(const lacking_move_ctor &other) = delete;
34};
35
36template <>
38
39/* Custom type caster move/copy test classes */
40class MoveOnlyInt {
41public:
43 explicit MoveOnlyInt(int v) : value{v} { print_created(this, value); }
44 MoveOnlyInt(MoveOnlyInt &&m) noexcept {
45 print_move_created(this, m.value);
46 std::swap(value, m.value);
47 }
49 print_move_assigned(this, m.value);
50 std::swap(value, m.value);
51 return *this;
52 }
53 MoveOnlyInt(const MoveOnlyInt &) = delete;
54 MoveOnlyInt &operator=(const MoveOnlyInt &) = delete;
56
57 int value;
58};
59class MoveOrCopyInt {
60public:
62 explicit MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
64 print_move_created(this, m.value);
65 std::swap(value, m.value);
66 }
68 print_move_assigned(this, m.value);
69 std::swap(value, m.value);
70 return *this;
71 }
73 print_copy_created(this, c.value);
74 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
75 value = c.value;
76 }
78 print_copy_assigned(this, c.value);
79 value = c.value;
80 return *this;
81 }
83
84 int value;
85};
86class CopyOnlyInt {
87public:
89 explicit CopyOnlyInt(int v) : value{v} { print_created(this, value); }
91 print_copy_created(this, c.value);
92 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
93 value = c.value;
94 }
96 print_copy_assigned(this, c.value);
97 value = c.value;
98 return *this;
99 }
101
102 int value;
103};
106template <>
107struct type_caster<MoveOnlyInt> {
109 bool load(handle src, bool) {
110 value = MoveOnlyInt(src.cast<int>());
111 return true;
112 }
114 return pybind11::cast(m.value, r, p);
115 }
116};
117
118template <>
121 bool load(handle src, bool) {
122 value = MoveOrCopyInt(src.cast<int>());
123 return true;
124 }
126 return pybind11::cast(m.value, r, p);
127 }
128};
129
130template <>
131struct type_caster<CopyOnlyInt> {
132protected:
134
135public:
136 static constexpr auto name = const_name("CopyOnlyInt");
137 bool load(handle src, bool) {
138 value = CopyOnlyInt(src.cast<int>());
139 return true;
140 }
142 return pybind11::cast(m.value, r, p);
143 }
144 static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) {
145 if (!src) {
146 return none().release();
147 }
148 return cast(*src, policy, parent);
149 }
150 explicit operator CopyOnlyInt *() { return &value; }
151 explicit operator CopyOnlyInt &() { return value; }
152 template <typename T>
153 using cast_op_type = pybind11::detail::cast_op_type<T>;
154};
157
158TEST_SUBMODULE(copy_move_policies, m) {
159 // test_lacking_copy_ctor
160 py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
161 .def_static("get_one", &lacking_copy_ctor::get_one, py::return_value_policy::copy);
162 // test_lacking_move_ctor
163 py::class_<lacking_move_ctor>(m, "lacking_move_ctor")
164 .def_static("get_one", &lacking_move_ctor::get_one, py::return_value_policy::move);
165
166 // test_move_and_copy_casts
167 // NOLINTNEXTLINE(performance-unnecessary-value-param)
168 m.def("move_and_copy_casts", [](const py::object &o) {
169 int r = 0;
170 r += py::cast<MoveOrCopyInt>(o).value; /* moves */
171 r += py::cast<MoveOnlyInt>(o).value; /* moves */
172 r += py::cast<CopyOnlyInt>(o).value; /* copies */
173 auto m1(py::cast<MoveOrCopyInt>(o)); /* moves */
174 auto m2(py::cast<MoveOnlyInt>(o)); /* moves */
175 auto m3(py::cast<CopyOnlyInt>(o)); /* copies */
176 r += m1.value + m2.value + m3.value;
177
178 return r;
179 });
180
181 // test_move_and_copy_loads
182 m.def("move_only", [](MoveOnlyInt m) { return m.value; });
183 // Changing this breaks the existing test: needs careful review.
184 // NOLINTNEXTLINE(performance-unnecessary-value-param)
185 m.def("move_or_copy", [](MoveOrCopyInt m) { return m.value; });
186 // Changing this breaks the existing test: needs careful review.
187 // NOLINTNEXTLINE(performance-unnecessary-value-param)
188 m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
189 m.def("move_pair",
190 [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) { return p.first.value + p.second.value; });
191 m.def("move_tuple", [](std::tuple<MoveOnlyInt, MoveOrCopyInt, MoveOnlyInt> t) {
192 return std::get<0>(t).value + std::get<1>(t).value + std::get<2>(t).value;
193 });
194 m.def("copy_tuple", [](std::tuple<CopyOnlyInt, CopyOnlyInt> t) {
195 return std::get<0>(t).value + std::get<1>(t).value;
196 });
197 m.def("move_copy_nested",
198 [](std::pair<MoveOnlyInt,
199 std::pair<std::tuple<MoveOrCopyInt, CopyOnlyInt, std::tuple<MoveOnlyInt>>,
200 MoveOrCopyInt>> x) {
201 return x.first.value + std::get<0>(x.second.first).value
202 + std::get<1>(x.second.first).value
203 + std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value;
204 });
205 m.def("move_and_copy_cstats", []() {
207 // Reset counts to 0 so that previous tests don't affect later ones:
208 auto &mc = ConstructorStats::get<MoveOrCopyInt>();
209 mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions
210 = 0;
211 auto &mo = ConstructorStats::get<MoveOnlyInt>();
212 mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions
213 = 0;
214 auto &co = ConstructorStats::get<CopyOnlyInt>();
215 co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions
216 = 0;
217 py::dict d;
218 d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference);
219 d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference);
220 d["CopyOnlyInt"] = py::cast(co, py::return_value_policy::reference);
221 return d;
222 });
223#ifdef PYBIND11_HAS_OPTIONAL
224 // test_move_and_copy_load_optional
225 m.attr("has_optional") = true;
226 m.def("move_optional", [](std::optional<MoveOnlyInt> o) { return o->value; });
227 m.def("move_or_copy_optional", [](std::optional<MoveOrCopyInt> o) { return o->value; });
228 m.def("copy_optional", [](std::optional<CopyOnlyInt> o) { return o->value; });
229 m.def("move_optional_tuple",
230 [](std::optional<std::tuple<MoveOrCopyInt, MoveOnlyInt, CopyOnlyInt>> x) {
231 return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value;
232 });
233#else
234 m.attr("has_optional") = false;
235#endif
236
237 // #70 compilation issue if operator new is not public - simple body added
238 // but not needed on most compilers; MSVC and nvcc don't like a local
239 // struct not having a method defined when declared, since it can not be
240 // added later.
241 struct PrivateOpNew {
242 int value = 1;
243
244 private:
245 void *operator new(size_t bytes) {
246 void *ptr = std::malloc(bytes);
247 if (ptr) {
248 return ptr;
249 }
250 throw std::bad_alloc{};
251 }
252 };
253 py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
254 m.def("private_op_new_value", []() { return PrivateOpNew(); });
255 m.def(
256 "private_op_new_reference",
257 []() -> const PrivateOpNew & {
258 static PrivateOpNew x{};
259 return x;
260 },
261 py::return_value_policy::reference);
262
263 // test_move_fallback
264 // #389: rvp::move should fall-through to copy on non-movable objects
265 struct MoveIssue1 {
266 int v;
267 explicit MoveIssue1(int v) : v{v} {}
268 MoveIssue1(const MoveIssue1 &c) = default;
269 MoveIssue1(MoveIssue1 &&) = delete;
270 };
271 py::class_<MoveIssue1>(m, "MoveIssue1")
272 .def(py::init<int>())
273 .def_readwrite("value", &MoveIssue1::v);
274
275 struct MoveIssue2 {
276 int v;
277 explicit MoveIssue2(int v) : v{v} {}
278 MoveIssue2(MoveIssue2 &&) = default;
279 };
280 py::class_<MoveIssue2>(m, "MoveIssue2")
281 .def(py::init<int>())
282 .def_readwrite("value", &MoveIssue2::v);
283
284 // #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with
285 // `py::return_value_policy::move`
286 m.def(
287 "get_moveissue1",
288 [](int i) { return std::unique_ptr<MoveIssue1>(new MoveIssue1(i)); },
289 py::return_value_policy::move);
290 m.def(
291 "get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
292
293 // Make sure that cast from pytype rvalue to other pytype works
294 m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); });
295}
CopyOnlyInt(const CopyOnlyInt &c)
CopyOnlyInt(int v)
CopyOnlyInt & operator=(const CopyOnlyInt &c)
MoveOnlyInt & operator=(MoveOnlyInt &&m) noexcept
MoveOnlyInt & operator=(const MoveOnlyInt &)=delete
MoveOnlyInt(const MoveOnlyInt &)=delete
MoveOnlyInt(MoveOnlyInt &&m) noexcept
MoveOnlyInt(int v)
MoveOrCopyInt & operator=(const MoveOrCopyInt &c)
MoveOrCopyInt(MoveOrCopyInt &&m) noexcept
MoveOrCopyInt(const MoveOrCopyInt &c)
MoveOrCopyInt & operator=(MoveOrCopyInt &&m) noexcept
\rst Holds a reference to a Python object (no reference counting)
Definition: pytypes.h:194
T cast() const
\rst Attempt to cast the Python object into the given C++ type.
Definition: cast.h:1083
Definition: pytypes.h:1422
handle release()
\rst Resets the internal pointer to nullptr without decreasing the object's reference count.
Definition: pytypes.h:283
static handle cast(const itype &src, return_value_policy policy, handle parent)
#define PYBIND11_NAMESPACE_END(name)
Definition: common.h:21
#define PYBIND11_NAMESPACE_BEGIN(name)
Definition: common.h:20
return_value_policy
Approach used to cast a previously unknown C++ instance into a Python object.
Definition: common.h:470
constexpr descr< N - 1 > const_name(char const (&text)[N])
Definition: descr.h:60
void print_default_created(T *inst, Values &&...values)
void print_copy_created(T *inst, Values &&...values)
void print_copy_assigned(T *inst, Values &&...values)
void print_created(T *inst, Values &&...values)
void print_destroyed(T *inst, Values &&...values)
void print_move_assigned(T *inst, Values &&...values)
void print_move_created(T *inst, Values &&...values)
#define TEST_SUBMODULE(name, variable)
static const derived & get_one()
static derived instance_
lacking_copy_ctor(const lacking_copy_ctor &other)=delete
lacking_copy_ctor()=default
lacking_move_ctor(const lacking_move_ctor &other)=delete
lacking_move_ctor()=default
lacking_move_ctor(lacking_move_ctor &&other)=delete
Annotation for function names.
Definition: attr.h:47
pybind11::detail::cast_op_type< T > cast_op_type
bool load(handle src, bool)
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p)
static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent)
PYBIND11_TYPE_CASTER(MoveOnlyInt, const_name("MoveOnlyInt"))
static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p)
bool load(handle src, bool)
bool load(handle src, bool)
PYBIND11_TYPE_CASTER(MoveOrCopyInt, const_name("MoveOrCopyInt"))
static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p)