1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
|
/*
tests/test_custom_type_casters.cpp -- tests type_caster<T>
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "constructor_stats.h"
#include "pybind11_tests.h"
// py::arg/py::arg_v testing: these arguments just record their argument when invoked
class ArgInspector1 {
public:
std::string arg = "(default arg inspector 1)";
};
class ArgInspector2 {
public:
std::string arg = "(default arg inspector 2)";
};
class ArgAlwaysConverts {};
namespace PYBIND11_NAMESPACE {
namespace detail {
template <>
struct type_caster<ArgInspector1> {
public:
// Classic
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1"));
#else
PYBIND11_TYPE_CASTER(ArgInspector1, const_name("ArgInspector1"));
#endif
bool load(handle src, bool convert) {
value.arg = "loading ArgInspector1 argument " + std::string(convert ? "WITH" : "WITHOUT")
+ " conversion allowed. "
"Argument value = "
+ (std::string) str(src);
return true;
}
static handle cast(const ArgInspector1 &src, return_value_policy, handle) {
return str(src.arg).release();
}
};
template <>
struct type_caster<ArgInspector2> {
public:
PYBIND11_TYPE_CASTER(ArgInspector2, const_name("ArgInspector2"));
bool load(handle src, bool convert) {
value.arg = "loading ArgInspector2 argument " + std::string(convert ? "WITH" : "WITHOUT")
+ " conversion allowed. "
"Argument value = "
+ (std::string) str(src);
return true;
}
static handle cast(const ArgInspector2 &src, return_value_policy, handle) {
return str(src.arg).release();
}
};
template <>
struct type_caster<ArgAlwaysConverts> {
public:
PYBIND11_TYPE_CASTER(ArgAlwaysConverts, const_name("ArgAlwaysConverts"));
bool load(handle, bool convert) { return convert; }
static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) {
return py::none().release();
}
};
} // namespace detail
} // namespace PYBIND11_NAMESPACE
// test_custom_caster_destruction
class DestructionTester {
public:
DestructionTester() { print_default_created(this); }
~DestructionTester() { print_destroyed(this); }
DestructionTester(const DestructionTester &) { print_copy_created(this); }
DestructionTester(DestructionTester &&) noexcept { print_move_created(this); }
DestructionTester &operator=(const DestructionTester &) {
print_copy_assigned(this);
return *this;
}
DestructionTester &operator=(DestructionTester &&) noexcept {
print_move_assigned(this);
return *this;
}
};
namespace PYBIND11_NAMESPACE {
namespace detail {
template <>
struct type_caster<DestructionTester> {
PYBIND11_TYPE_CASTER(DestructionTester, const_name("DestructionTester"));
bool load(handle, bool) { return true; }
static handle cast(const DestructionTester &, return_value_policy, handle) {
return py::bool_(true).release();
}
};
} // namespace detail
} // namespace PYBIND11_NAMESPACE
// Define type caster outside of `pybind11::detail` and then alias it.
namespace other_lib {
struct MyType {};
// Corrupt `py` shorthand alias for surrounding context.
namespace py {}
// Corrupt unqualified relative `pybind11` namespace.
namespace PYBIND11_NAMESPACE {}
// Correct alias.
namespace py_ = ::pybind11;
// Define caster. This is effectively no-op, we only ensure it compiles and we
// don't have any symbol collision when using macro mixin.
struct my_caster {
PYBIND11_TYPE_CASTER(MyType, py_::detail::const_name("MyType"));
bool load(py_::handle, bool) { return true; }
static py_::handle cast(const MyType &, py_::return_value_policy, py_::handle) {
return py_::bool_(true).release();
}
};
} // namespace other_lib
// Effectively "alias" it into correct namespace (via inheritance).
namespace PYBIND11_NAMESPACE {
namespace detail {
template <>
struct type_caster<other_lib::MyType> : public other_lib::my_caster {};
} // namespace detail
} // namespace PYBIND11_NAMESPACE
// This simply is required to compile
namespace ADL_issue {
template <typename OutStringType = std::string, typename... Args>
OutStringType concat(Args &&...) {
return OutStringType();
}
struct test {};
} // namespace ADL_issue
TEST_SUBMODULE(custom_type_casters, m) {
// test_custom_type_casters
// test_noconvert_args
//
// Test converting. The ArgAlwaysConverts is just there to make the first no-conversion pass
// fail so that our call always ends up happening via the second dispatch (the one that allows
// some conversion).
class ArgInspector {
public:
ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; }
std::string g(const ArgInspector1 &a,
const ArgInspector1 &b,
int c,
ArgInspector2 *d,
ArgAlwaysConverts) {
return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg;
}
static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; }
};
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
py::class_<ArgInspector>(m, "ArgInspector")
.def(py::init<>())
.def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts())
.def("g",
&ArgInspector::g,
"a"_a.noconvert(),
"b"_a,
"c"_a.noconvert() = 13,
"d"_a = ArgInspector2(),
py::arg() = ArgAlwaysConverts())
.def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts());
m.def(
"arg_inspect_func",
[](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) {
return a.arg + "\n" + b.arg;
},
py::arg{}.noconvert(false),
py::arg_v(nullptr, ArgInspector1()).noconvert(true),
py::arg() = ArgAlwaysConverts());
m.def("floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
m.def("floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());
m.def("ints_preferred", [](int i) { return i / 2; }, "i"_a);
m.def("ints_only", [](int i) { return i / 2; }, "i"_a.noconvert());
// test_custom_caster_destruction
// Test that `take_ownership` works on types with a custom type caster when given a pointer
// default policy: don't take ownership:
m.def("custom_caster_no_destroy", []() {
static auto *dt = new DestructionTester();
return dt;
});
m.def(
"custom_caster_destroy",
[]() { return new DestructionTester(); },
py::return_value_policy::take_ownership); // Takes ownership: destroy when finished
m.def(
"custom_caster_destroy_const",
[]() -> const DestructionTester * { return new DestructionTester(); },
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
m.def("destruction_tester_cstats",
&ConstructorStats::get<DestructionTester>,
py::return_value_policy::reference);
m.def("other_lib_type", [](other_lib::MyType x) { return x; });
m.def("_adl_issue", [](const ADL_issue::test &) {});
}
|