Scarab  2.8.1
Project 8 C++ Utility Library
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_tests.h"
12 #include "constructor_stats.h"
13 #include <pybind11/stl.h>
14 
15 template <typename derived>
16 struct empty {
17  static const derived& get_one() { return instance_; }
18  static derived instance_;
19 };
20 
21 struct lacking_copy_ctor : public empty<lacking_copy_ctor> {
23  lacking_copy_ctor(const lacking_copy_ctor& other) = delete;
24 };
25 
27 
28 struct lacking_move_ctor : public empty<lacking_move_ctor> {
30  lacking_move_ctor(const lacking_move_ctor& other) = delete;
31  lacking_move_ctor(lacking_move_ctor&& other) = delete;
32 };
33 
35 
36 /* Custom type caster move/copy test classes */
37 class MoveOnlyInt {
38 public:
40  MoveOnlyInt(int v) : value{std::move(v)} { print_created(this, value); }
41  MoveOnlyInt(MoveOnlyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); }
42  MoveOnlyInt &operator=(MoveOnlyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; }
43  MoveOnlyInt(const MoveOnlyInt &) = delete;
44  MoveOnlyInt &operator=(const MoveOnlyInt &) = delete;
46 
47  int value;
48 };
50 public:
52  MoveOrCopyInt(int v) : value{std::move(v)} { print_created(this, value); }
53  MoveOrCopyInt(MoveOrCopyInt &&m) { print_move_created(this, m.value); std::swap(value, m.value); }
54  MoveOrCopyInt &operator=(MoveOrCopyInt &&m) { print_move_assigned(this, m.value); std::swap(value, m.value); return *this; }
56  MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
58 
59  int value;
60 };
61 class CopyOnlyInt {
62 public:
64  CopyOnlyInt(int v) : value{std::move(v)} { print_created(this, value); }
66  CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
68 
69  int value;
70 };
72 NAMESPACE_BEGIN(detail)
73 template <> struct type_caster<MoveOnlyInt> {
74  PYBIND11_TYPE_CASTER(MoveOnlyInt, _("MoveOnlyInt"));
75  bool load(handle src, bool) { value = MoveOnlyInt(src.cast<int>()); return true; }
76  static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); }
77 };
78 
79 template <> struct type_caster<MoveOrCopyInt> {
80  PYBIND11_TYPE_CASTER(MoveOrCopyInt, _("MoveOrCopyInt"));
81  bool load(handle src, bool) { value = MoveOrCopyInt(src.cast<int>()); return true; }
82  static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); }
83 };
84 
85 template <> struct type_caster<CopyOnlyInt> {
86 protected:
88 public:
89  static constexpr auto name = _("CopyOnlyInt");
90  bool load(handle src, bool) { value = CopyOnlyInt(src.cast<int>()); return true; }
91  static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); }
92  static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) {
93  if (!src) return none().release();
94  return cast(*src, policy, parent);
95  }
96  operator CopyOnlyInt*() { return &value; }
97  operator CopyOnlyInt&() { return value; }
98  template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>;
99 };
100 NAMESPACE_END(detail)
102 
104  // test_lacking_copy_ctor
105  py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
106  .def_static("get_one", &lacking_copy_ctor::get_one,
107  py::return_value_policy::copy);
108  // test_lacking_move_ctor
109  py::class_<lacking_move_ctor>(m, "lacking_move_ctor")
110  .def_static("get_one", &lacking_move_ctor::get_one,
112 
113  // test_move_and_copy_casts
114  m.def("move_and_copy_casts", [](py::object o) {
115  int r = 0;
116  r += py::cast<MoveOrCopyInt>(o).value; /* moves */
117  r += py::cast<MoveOnlyInt>(o).value; /* moves */
118  r += py::cast<CopyOnlyInt>(o).value; /* copies */
119  MoveOrCopyInt m1(py::cast<MoveOrCopyInt>(o)); /* moves */
120  MoveOnlyInt m2(py::cast<MoveOnlyInt>(o)); /* moves */
121  CopyOnlyInt m3(py::cast<CopyOnlyInt>(o)); /* copies */
122  r += m1.value + m2.value + m3.value;
123 
124  return r;
125  });
126 
127  // test_move_and_copy_loads
128  m.def("move_only", [](MoveOnlyInt m) { return m.value; });
129  m.def("move_or_copy", [](MoveOrCopyInt m) { return m.value; });
130  m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
131  m.def("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) {
132  return p.first.value + p.second.value;
133  });
134  m.def("move_tuple", [](std::tuple<MoveOnlyInt, MoveOrCopyInt, MoveOnlyInt> t) {
135  return std::get<0>(t).value + std::get<1>(t).value + std::get<2>(t).value;
136  });
137  m.def("copy_tuple", [](std::tuple<CopyOnlyInt, CopyOnlyInt> t) {
138  return std::get<0>(t).value + std::get<1>(t).value;
139  });
140  m.def("move_copy_nested", [](std::pair<MoveOnlyInt, std::pair<std::tuple<MoveOrCopyInt, CopyOnlyInt, std::tuple<MoveOnlyInt>>, MoveOrCopyInt>> x) {
141  return x.first.value + std::get<0>(x.second.first).value + std::get<1>(x.second.first).value +
142  std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value;
143  });
144  m.def("move_and_copy_cstats", []() {
146  // Reset counts to 0 so that previous tests don't affect later ones:
147  auto &mc = ConstructorStats::get<MoveOrCopyInt>();
148  mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions = 0;
149  auto &mo = ConstructorStats::get<MoveOnlyInt>();
150  mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions = 0;
151  auto &co = ConstructorStats::get<CopyOnlyInt>();
152  co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions = 0;
153  py::dict d;
154  d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference);
155  d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference);
156  d["CopyOnlyInt"] = py::cast(co, py::return_value_policy::reference);
157  return d;
158  });
159 #ifdef PYBIND11_HAS_OPTIONAL
160  // test_move_and_copy_load_optional
161  m.attr("has_optional") = true;
162  m.def("move_optional", [](std::optional<MoveOnlyInt> o) {
163  return o->value;
164  });
165  m.def("move_or_copy_optional", [](std::optional<MoveOrCopyInt> o) {
166  return o->value;
167  });
168  m.def("copy_optional", [](std::optional<CopyOnlyInt> o) {
169  return o->value;
170  });
171  m.def("move_optional_tuple", [](std::optional<std::tuple<MoveOrCopyInt, MoveOnlyInt, CopyOnlyInt>> x) {
172  return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value;
173  });
174 #else
175  m.attr("has_optional") = false;
176 #endif
177 
178  // #70 compilation issue if operator new is not public
179  struct PrivateOpNew {
180  int value = 1;
181  private:
182 #if defined(_MSC_VER)
183 # pragma warning(disable: 4822) // warning C4822: local class member function does not have a body
184 #endif
185  void *operator new(size_t bytes);
186  };
187  py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
188  m.def("private_op_new_value", []() { return PrivateOpNew(); });
189  m.def("private_op_new_reference", []() -> const PrivateOpNew & {
190  static PrivateOpNew x{};
191  return x;
193 
194  // test_move_fallback
195  // #389: rvp::move should fall-through to copy on non-movable objects
196  struct MoveIssue1 {
197  int v;
198  MoveIssue1(int v) : v{v} {}
199  MoveIssue1(const MoveIssue1 &c) = default;
200  MoveIssue1(MoveIssue1 &&) = delete;
201  };
202  py::class_<MoveIssue1>(m, "MoveIssue1").def(py::init<int>()).def_readwrite("value", &MoveIssue1::v);
203 
204  struct MoveIssue2 {
205  int v;
206  MoveIssue2(int v) : v{v} {}
207  MoveIssue2(MoveIssue2 &&) = default;
208  };
209  py::class_<MoveIssue2>(m, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v);
210 
211  m.def("get_moveissue1", [](int i) { return new MoveIssue1(i); }, py::return_value_policy::move);
212  m.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
213 }
static derived instance_
static const derived & get_one()
glibc defines I as a macro which breaks things, e.g., boost template names
Definition: attr.h:15
void print_destroyed(T *inst, Values &&...values)
static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p)
MoveOrCopyInt & operator=(const MoveOrCopyInt &c)
pybind11::detail::cast_op_type< T > cast_op_type
MoveOrCopyInt(const MoveOrCopyInt &c)
void print_copy_assigned(T *inst, Values &&...values)
void print_copy_created(T *inst, Values &&...values)
constexpr descr< N - 1 > _(char const(&text)[N])
Definition: descr.h:54
MoveOnlyInt & operator=(MoveOnlyInt &&m)
conditional_t< std::is_pointer< remove_reference_t< T > >::value, typename std::add_pointer< intrinsic_t< T > >::type, typename std::add_lvalue_reference< intrinsic_t< T > >::type > cast_op_type
Definition: cast.h:752
CopyOnlyInt(int v)
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p)
void print_default_created(T *inst, Values &&...values)
test_initializer copy_move_policies("copy_move_policies", test_submodule_copy_move_policies)
#define NAMESPACE_END(name)
Definition: detail/common.h:16
void print_move_assigned(T *inst, Values &&...values)
CopyOnlyInt & operator=(const CopyOnlyInt &c)
return_value_policy
Approach used to cast a previously unknown C++ instance into a Python object.
def d(s)
Definition: mkdoc.py:69
MoveOnlyInt(MoveOnlyInt &&m)
CopyOnlyInt(const CopyOnlyInt &c)
void print_created(T *inst, Values &&...values)
detail::enable_if_t<!detail::move_never< T >::value, T > move(object &&obj)
Definition: cast.h:1685
MoveOrCopyInt(MoveOrCopyInt &&m)
bool load(handle src, bool)
bool load(handle src, bool)
#define NAMESPACE_BEGIN(name)
Definition: detail/common.h:13
static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p)
T cast(const handle &handle)
Definition: cast.h:1659
void print_move_created(T *inst, Values &&...values)
MoveOrCopyInt & operator=(MoveOrCopyInt &&m)
#define TEST_SUBMODULE(name, variable)
bool typename Extra class_ & def(const char *name_, Func &&f, const Extra &... extra)
Definition: pybind11.h:1110
MoveOnlyInt(int v)
#define PYBIND11_TYPE_CASTER(type, py_name)
Definition: cast.h:942
bool load(handle src, bool)
static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent)