|
15 | 15 | # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
16 | 16 | #endif
|
17 | 17 |
|
| 18 | +struct ConstRefCasted { |
| 19 | + int tag; |
| 20 | +}; |
| 21 | + |
| 22 | +PYBIND11_NAMESPACE_BEGIN(pybind11) |
| 23 | +PYBIND11_NAMESPACE_BEGIN(detail) |
| 24 | +template <> |
| 25 | +class type_caster<ConstRefCasted> { |
| 26 | + public: |
| 27 | + static constexpr auto name = _<ConstRefCasted>(); |
| 28 | + |
| 29 | + // Input is unimportant, a new value will always be constructed based on the |
| 30 | + // cast operator. |
| 31 | + bool load(handle, bool) { return true; } |
| 32 | + |
| 33 | + operator ConstRefCasted&&() { value = {1}; return std::move(value); } |
| 34 | + operator ConstRefCasted&() { value = {2}; return value; } |
| 35 | + operator ConstRefCasted*() { value = {3}; return &value; } |
| 36 | + |
| 37 | + operator const ConstRefCasted&() { value = {4}; return value; } |
| 38 | + operator const ConstRefCasted*() { value = {5}; return &value; } |
| 39 | + |
| 40 | + // custom cast_op to explicitly propagate types to the conversion operators. |
| 41 | + template <typename T_> |
| 42 | + using cast_op_type = |
| 43 | + /// const |
| 44 | + conditional_t< |
| 45 | + std::is_same<remove_reference_t<T_>, const ConstRefCasted*>::value, const ConstRefCasted*, |
| 46 | + conditional_t< |
| 47 | + std::is_same<T_, const ConstRefCasted&>::value, const ConstRefCasted&, |
| 48 | + /// non-const |
| 49 | + conditional_t< |
| 50 | + std::is_same<remove_reference_t<T_>, ConstRefCasted*>::value, ConstRefCasted*, |
| 51 | + conditional_t< |
| 52 | + std::is_same<T_, ConstRefCasted&>::value, ConstRefCasted&, |
| 53 | + /* else */ConstRefCasted&&>>>>; |
| 54 | + |
| 55 | + private: |
| 56 | + ConstRefCasted value = {0}; |
| 57 | +}; |
| 58 | +PYBIND11_NAMESPACE_END(detail) |
| 59 | +PYBIND11_NAMESPACE_END(pybind11) |
| 60 | + |
18 | 61 | TEST_SUBMODULE(builtin_casters, m) {
|
19 | 62 | // test_simple_string
|
20 | 63 | m.def("string_roundtrip", [](const char *s) { return s; });
|
@@ -147,6 +190,17 @@ TEST_SUBMODULE(builtin_casters, m) {
|
147 | 190 | // test_reference_wrapper
|
148 | 191 | m.def("refwrap_builtin", [](std::reference_wrapper<int> p) { return 10 * p.get(); });
|
149 | 192 | m.def("refwrap_usertype", [](std::reference_wrapper<UserType> p) { return p.get().value(); });
|
| 193 | + m.def("refwrap_usertype_const", [](std::reference_wrapper<const UserType> p) { return p.get().value(); }); |
| 194 | + |
| 195 | + m.def("refwrap_lvalue", []() -> std::reference_wrapper<UserType> { |
| 196 | + static UserType x(1); |
| 197 | + return std::ref(x); |
| 198 | + }); |
| 199 | + m.def("refwrap_lvalue_const", []() -> std::reference_wrapper<const UserType> { |
| 200 | + static UserType x(1); |
| 201 | + return std::cref(x); |
| 202 | + }); |
| 203 | + |
150 | 204 | // Not currently supported (std::pair caster has return-by-value cast operator);
|
151 | 205 | // triggers static_assert failure.
|
152 | 206 | //m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
|
@@ -189,4 +243,14 @@ TEST_SUBMODULE(builtin_casters, m) {
|
189 | 243 | py::object o = py::cast(v);
|
190 | 244 | return py::cast<void *>(o) == v;
|
191 | 245 | });
|
| 246 | + |
| 247 | + // Tests const/non-const propagation in cast_op. |
| 248 | + m.def("takes", [](ConstRefCasted x) { return x.tag; }); |
| 249 | + m.def("takes_move", [](ConstRefCasted&& x) { return x.tag; }); |
| 250 | + m.def("takes_ptr", [](ConstRefCasted* x) { return x->tag; }); |
| 251 | + m.def("takes_ref", [](ConstRefCasted& x) { return x.tag; }); |
| 252 | + m.def("takes_ref_wrap", [](std::reference_wrapper<ConstRefCasted> x) { return x.get().tag; }); |
| 253 | + m.def("takes_const_ptr", [](const ConstRefCasted* x) { return x->tag; }); |
| 254 | + m.def("takes_const_ref", [](const ConstRefCasted& x) { return x.tag; }); |
| 255 | + m.def("takes_const_ref_wrap", [](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; }); |
192 | 256 | }
|
0 commit comments