test_classh_wip.cpp 17.3 KB
Newer Older
1
2
3
4
5
#include "pybind11_tests.h"

#include <pybind11/classh.h>

#include <memory>
6
#include <string>
7
8
9
10

namespace pybind11_tests {
namespace classh_wip {

11
12
13
14
15
struct mpty {
    std::string mtxt;
};

// clang-format off
16

17
mpty        rtrn_mpty_valu() { mpty obj{"rtrn_valu"}; return obj; }
18
mpty&&      rtrn_mpty_rref() { static mpty obj; obj.mtxt = "rtrn_rref"; return std::move(obj); }
19
20
mpty const& rtrn_mpty_cref() { static mpty obj; obj.mtxt = "rtrn_cref"; return obj; }
mpty&       rtrn_mpty_mref() { static mpty obj; obj.mtxt = "rtrn_mref"; return obj; }
21
22
mpty const* rtrn_mpty_cptr() { return new mpty{"rtrn_cptr"}; }
mpty*       rtrn_mpty_mptr() { return new mpty{"rtrn_mptr"}; }
23

24
25
26
27
28
29
std::string pass_mpty_valu(mpty obj)        { return "pass_valu:" + obj.mtxt; }
std::string pass_mpty_rref(mpty&& obj)      { return "pass_rref:" + obj.mtxt; }
std::string pass_mpty_cref(mpty const& obj) { return "pass_cref:" + obj.mtxt; }
std::string pass_mpty_mref(mpty& obj)       { return "pass_mref:" + obj.mtxt; }
std::string pass_mpty_cptr(mpty const* obj) { return "pass_cptr:" + obj->mtxt; }
std::string pass_mpty_mptr(mpty* obj)       { return "pass_mptr:" + obj->mtxt; }
30

31
32
std::shared_ptr<mpty>       rtrn_mpty_shmp() { return std::shared_ptr<mpty      >(new mpty{"rtrn_shmp"}); }
std::shared_ptr<mpty const> rtrn_mpty_shcp() { return std::shared_ptr<mpty const>(new mpty{"rtrn_shcp"}); }
33

34
std::string pass_mpty_shmp(std::shared_ptr<mpty>       obj) { return "pass_shmp:" + obj->mtxt; }
35
std::string pass_mpty_shcp(std::shared_ptr<mpty const> obj) { return "pass_shcp:" + obj->mtxt; }
36

37
std::unique_ptr<mpty>       rtrn_mpty_uqmp() { return std::unique_ptr<mpty      >(new mpty{"rtrn_uqmp"}); }
38
std::unique_ptr<mpty const> rtrn_mpty_uqcp() { return std::unique_ptr<mpty const>(new mpty{"rtrn_uqcp"}); }
39

40
std::string pass_mpty_uqmp(std::unique_ptr<mpty      > obj) { return "pass_uqmp:" + obj->mtxt; }
41
std::string pass_mpty_uqcp(std::unique_ptr<mpty const> obj) { return "pass_uqcp:" + obj->mtxt; }
42

43
44
// clang-format on

45
// Helpers for testing.
46
std::string get_mtxt(mpty const &obj) { return obj.mtxt; }
47
std::unique_ptr<mpty> unique_ptr_roundtrip(std::unique_ptr<mpty> obj) { return obj; }
48

49
50
} // namespace classh_wip
} // namespace pybind11_tests
51
52
53
54
55
56

namespace pybind11 {
namespace detail {

using namespace pybind11_tests::classh_wip;

57
58
template <typename T>
struct smart_holder_type_caster_load {
59
60
    using holder_type = pybindit::memory::smart_holder;

61
    bool load(handle src, bool /*convert*/) {
62
63
64
        if (!isinstance<T>(src))
            return false;
        auto inst  = reinterpret_cast<instance *>(src.ptr());
65
66
67
68
69
70
71
        loaded_v_h = inst->get_value_and_holder(get_type_info(typeid(T)));
        if (!loaded_v_h.holder_constructed()) {
            // IMPROVEABLE: Error message.
            throw std::runtime_error("Missing value for wrapped C++ type:"
                                     " Python instance is uninitialized or was disowned.");
        }
        loaded_smhldr_ptr = &loaded_v_h.holder<holder_type>();
72
73
74
        return true;
    }

75
76
77
78
79
80
81
82
83
84
    std::unique_ptr<T> loaded_as_unique_ptr() {
        void *value_void_ptr = loaded_v_h.value_ptr();
        auto unq_ptr         = loaded_smhldr_ptr->as_unique_ptr<mpty>();
        loaded_v_h.holder<holder_type>().~holder_type();
        loaded_v_h.set_holder_constructed(false);
        loaded_v_h.value_ptr() = nullptr;
        deregister_instance(loaded_v_h.inst, value_void_ptr, loaded_v_h.type);
        return unq_ptr;
    }

85
protected:
86
87
    value_and_holder loaded_v_h;
    holder_type *loaded_smhldr_ptr = nullptr;
88
89
};

90
template <>
91
struct type_caster<mpty> : smart_holder_type_caster_load<mpty> {
92
93
94
95
96
    static constexpr auto name = _<mpty>();

    // static handle cast(mpty, ...)
    // is redundant (leads to ambiguous overloads).

97
98
99
100
101
102
    static handle cast(mpty &&src, return_value_policy /*policy*/, handle parent) {
        // type_caster_base BEGIN
        // clang-format off
        return cast(&src, return_value_policy::move, parent);
        // clang-format on
        // type_caster_base END
103
104
    }

105
106
107
108
109
110
111
112
    static handle cast(mpty const &src, return_value_policy policy, handle parent) {
        // type_caster_base BEGIN
        // clang-format off
        if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference)
            policy = return_value_policy::copy;
        return cast(&src, policy, parent);
        // clang-format on
        // type_caster_base END
113
114
    }

115
    static handle cast(mpty &src, return_value_policy policy, handle parent) {
116
        return cast(const_cast<mpty const &>(src), policy, parent); // Mutbl2Const
117
118
    }

119
    static handle cast(mpty const *src, return_value_policy policy, handle parent) {
120
121
122
123
124
125
        auto st = type_caster_base<mpty>::src_and_type(src);
        return cast_const_raw_ptr( // Originally type_caster_generic::cast.
            st.first,
            policy,
            parent,
            st.second,
126
127
            make_constructor::make_copy_constructor(src),
            make_constructor::make_move_constructor(src));
128
129
    }

130
    static handle cast(mpty *src, return_value_policy policy, handle parent) {
131
        return cast(const_cast<mpty const *>(src), policy, parent); // Mutbl2Const
132
133
134
135
    }

    template <typename T_>
    using cast_op_type = conditional_t<
136
137
        std::is_same<remove_reference_t<T_>, mpty const *>::value,
        mpty const *,
138
        conditional_t<
139
140
            std::is_same<remove_reference_t<T_>, mpty *>::value,
            mpty *,
141
            conditional_t<
142
143
144
145
146
147
148
                std::is_same<T_, mpty const &>::value,
                mpty const &,
                conditional_t<std::is_same<T_, mpty &>::value,
                              mpty &,
                              conditional_t<std::is_same<T_, mpty &&>::value, mpty &&, mpty>>>>>;

    // clang-format off
149

150
151
152
153
154
155
    operator mpty()        { return loaded_smhldr_ptr->lvalue_ref<mpty>(); }
    operator mpty&&() &&   { return loaded_smhldr_ptr->rvalue_ref<mpty>(); }
    operator mpty const&() { return loaded_smhldr_ptr->lvalue_ref<mpty>(); }
    operator mpty&()       { return loaded_smhldr_ptr->lvalue_ref<mpty>(); }
    operator mpty const*() { return loaded_smhldr_ptr->as_raw_ptr_unowned<mpty>(); }
    operator mpty*()       { return loaded_smhldr_ptr->as_raw_ptr_unowned<mpty>(); }
156
157

    // clang-format on
158

159
    // Originally type_caster_generic::cast.
160
161
162
163
164
165
166
    PYBIND11_NOINLINE static handle cast_const_raw_ptr(const void *_src,
                                                       return_value_policy policy,
                                                       handle parent,
                                                       const detail::type_info *tinfo,
                                                       void *(*copy_constructor)(const void *),
                                                       void *(*move_constructor)(const void *),
                                                       const void *existing_holder = nullptr) {
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
        if (!tinfo) // no type info: error will be set already
            return handle();

        void *src = const_cast<void *>(_src);
        if (src == nullptr)
            return none().release();

        auto it_instances = get_internals().registered_instances.equal_range(src);
        for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
            for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
                if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype))
                    return handle((PyObject *) it_i->second).inc_ref();
            }
        }

182
183
184
        auto inst       = reinterpret_steal<object>(make_new_instance(tinfo->type));
        auto wrapper    = reinterpret_cast<instance *>(inst.ptr());
        wrapper->owned  = false;
185
186
187
188
189
        void *&valueptr = values_and_holders(wrapper).begin()->value_ptr();

        switch (policy) {
            case return_value_policy::automatic:
            case return_value_policy::take_ownership:
190
                valueptr       = src;
191
192
193
194
195
                wrapper->owned = true;
                break;

            case return_value_policy::automatic_reference:
            case return_value_policy::reference:
196
                valueptr       = src;
197
198
199
200
201
202
203
204
205
206
207
208
209
                wrapper->owned = false;
                break;

            case return_value_policy::copy:
                if (copy_constructor)
                    valueptr = copy_constructor(src);
                else {
#if defined(NDEBUG)
                    throw cast_error("return_value_policy = copy, but type is "
                                     "non-copyable! (compile in debug mode for details)");
#else
                    std::string type_name(tinfo->cpptype->name());
                    detail::clean_type_id(type_name);
210
211
                    throw cast_error("return_value_policy = copy, but type " + type_name
                                     + " is non-copyable!");
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#endif
                }
                wrapper->owned = true;
                break;

            case return_value_policy::move:
                if (move_constructor)
                    valueptr = move_constructor(src);
                else if (copy_constructor)
                    valueptr = copy_constructor(src);
                else {
#if defined(NDEBUG)
                    throw cast_error("return_value_policy = move, but type is neither "
                                     "movable nor copyable! "
                                     "(compile in debug mode for details)");
#else
                    std::string type_name(tinfo->cpptype->name());
                    detail::clean_type_id(type_name);
230
231
                    throw cast_error("return_value_policy = move, but type " + type_name
                                     + " is neither movable nor copyable!");
232
233
234
235
236
237
#endif
                }
                wrapper->owned = true;
                break;

            case return_value_policy::reference_internal:
238
                valueptr       = src;
239
240
241
242
243
244
245
246
247
248
249
250
                wrapper->owned = false;
                keep_alive_impl(inst, parent);
                break;

            default:
                throw cast_error("unhandled return_value_policy: should not happen!");
        }

        tinfo->init_instance(wrapper, existing_holder);

        return inst.release();
    }
251
252
253
};

template <>
254
struct type_caster<std::shared_ptr<mpty>> : smart_holder_type_caster_load<mpty> {
255
256
    static constexpr auto name = _<std::shared_ptr<mpty>>();

257
258
259
260
261
262
263
264
265
    static handle
    cast(const std::shared_ptr<mpty> &src, return_value_policy policy, handle parent) {
        if (policy != return_value_policy::automatic
            && policy != return_value_policy::reference_internal) {
            // IMPROVEABLE: Error message.
            throw cast_error("Invalid return_value_policy for shared_ptr.");
        }

        auto src_raw_ptr = src.get();
266
        auto st          = type_caster_base<mpty>::src_and_type(src_raw_ptr);
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
        if (st.first == nullptr)
            return none().release(); // PyErr was set already.

        void *src_raw_void_ptr         = static_cast<void *>(src_raw_ptr);
        const detail::type_info *tinfo = st.second;
        auto it_instances = get_internals().registered_instances.equal_range(src_raw_void_ptr);
        // Loop copied from type_caster_generic::cast.
        for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
            for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
                if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype))
                    // MISSING: Enforcement of consistency with existing smart_holder.
                    // MISSING: keep_alive.
                    return handle((PyObject *) it_i->second).inc_ref();
            }
        }

        object inst            = reinterpret_steal<object>(make_new_instance(tinfo->type));
        instance *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
285
286
287
        inst_raw_ptr->owned    = true;
        void *&valueptr        = values_and_holders(inst_raw_ptr).begin()->value_ptr();
        valueptr               = src_raw_void_ptr;
288
289
290
291
292
293
294
295

        auto smhldr = pybindit::memory::smart_holder::from_shared_ptr(src);
        tinfo->init_instance(inst_raw_ptr, static_cast<const void *>(&smhldr));

        if (policy == return_value_policy::reference_internal)
            keep_alive_impl(inst, parent);

        return inst.release();
296
297
    }

298
299
    template <typename>
    using cast_op_type = std::shared_ptr<mpty>;
300

301
    operator std::shared_ptr<mpty>() { return loaded_smhldr_ptr->as_shared_ptr<mpty>(); }
302
303
304
};

template <>
305
struct type_caster<std::shared_ptr<mpty const>> : smart_holder_type_caster_load<mpty> {
306
307
    static constexpr auto name = _<std::shared_ptr<mpty const>>();

308
309
310
311
312
313
    static handle
    cast(const std::shared_ptr<mpty const> &src, return_value_policy policy, handle parent) {
        return type_caster<std::shared_ptr<mpty>>::cast(
            std::const_pointer_cast<mpty>(src), // Const2Mutbl
            policy,
            parent);
314
315
    }

316
317
    template <typename>
    using cast_op_type = std::shared_ptr<mpty const>;
318

319
    operator std::shared_ptr<mpty const>() { return loaded_smhldr_ptr->as_shared_ptr<mpty>(); }
320
321
322
};

template <>
323
struct type_caster<std::unique_ptr<mpty>> : smart_holder_type_caster_load<mpty> {
324
325
    static constexpr auto name = _<std::unique_ptr<mpty>>();

326
327
328
329
330
331
332
333
    static handle cast(std::unique_ptr<mpty> &&src, return_value_policy policy, handle parent) {
        if (policy != return_value_policy::automatic
            && policy != return_value_policy::reference_internal) {
            // IMPROVEABLE: Error message.
            throw cast_error("Invalid return_value_policy for unique_ptr.");
        }

        auto src_raw_ptr = src.get();
334
        auto st          = type_caster_base<mpty>::src_and_type(src_raw_ptr);
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
        if (st.first == nullptr)
            return none().release(); // PyErr was set already.

        void *src_raw_void_ptr         = static_cast<void *>(src_raw_ptr);
        const detail::type_info *tinfo = st.second;
        auto it_instances = get_internals().registered_instances.equal_range(src_raw_void_ptr);
        // Loop copied from type_caster_generic::cast.
        for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
            for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
                if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype))
                    throw cast_error(
                        "Invalid unique_ptr: another instance owns this pointer already.");
            }
        }

        object inst            = reinterpret_steal<object>(make_new_instance(tinfo->type));
        instance *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
352
353
354
        inst_raw_ptr->owned    = true;
        void *&valueptr        = values_and_holders(inst_raw_ptr).begin()->value_ptr();
        valueptr               = src_raw_void_ptr;
355
356
357
358
359
360
361
362

        auto smhldr = pybindit::memory::smart_holder::from_unique_ptr(std::move(src));
        tinfo->init_instance(inst_raw_ptr, static_cast<const void *>(&smhldr));

        if (policy == return_value_policy::reference_internal)
            keep_alive_impl(inst, parent);

        return inst.release();
363
364
    }

365
366
    template <typename>
    using cast_op_type = std::unique_ptr<mpty>;
367

368
    operator std::unique_ptr<mpty>() { return loaded_as_unique_ptr(); }
369
370
371
};

template <>
372
struct type_caster<std::unique_ptr<mpty const>> : smart_holder_type_caster_load<mpty> {
373
374
    static constexpr auto name = _<std::unique_ptr<mpty const>>();

375
376
377
378
379
380
    static handle
    cast(std::unique_ptr<mpty const> &&src, return_value_policy policy, handle parent) {
        return type_caster<std::unique_ptr<mpty>>::cast(
            std::unique_ptr<mpty>(const_cast<mpty *>(src.release())), // Const2Mutbl
            policy,
            parent);
381
382
    }

383
384
    template <typename>
    using cast_op_type = std::unique_ptr<mpty const>;
385

386
    operator std::unique_ptr<mpty const>() { return loaded_as_unique_ptr(); }
387
388
};

389
390
} // namespace detail
} // namespace pybind11
391
392
393
394
395
396
397

namespace pybind11_tests {
namespace classh_wip {

TEST_SUBMODULE(classh_wip, m) {
    namespace py = pybind11;

398
399
400
401
402
    py::classh<mpty>(m, "mpty").def(py::init<>()).def(py::init([](const std::string &mtxt) {
        mpty obj;
        obj.mtxt = mtxt;
        return obj;
    }));
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428

    m.def("rtrn_mpty_valu", rtrn_mpty_valu);
    m.def("rtrn_mpty_rref", rtrn_mpty_rref);
    m.def("rtrn_mpty_cref", rtrn_mpty_cref);
    m.def("rtrn_mpty_mref", rtrn_mpty_mref);
    m.def("rtrn_mpty_cptr", rtrn_mpty_cptr);
    m.def("rtrn_mpty_mptr", rtrn_mpty_mptr);

    m.def("pass_mpty_valu", pass_mpty_valu);
    m.def("pass_mpty_rref", pass_mpty_rref);
    m.def("pass_mpty_cref", pass_mpty_cref);
    m.def("pass_mpty_mref", pass_mpty_mref);
    m.def("pass_mpty_cptr", pass_mpty_cptr);
    m.def("pass_mpty_mptr", pass_mpty_mptr);

    m.def("rtrn_mpty_shmp", rtrn_mpty_shmp);
    m.def("rtrn_mpty_shcp", rtrn_mpty_shcp);

    m.def("pass_mpty_shmp", pass_mpty_shmp);
    m.def("pass_mpty_shcp", pass_mpty_shcp);

    m.def("rtrn_mpty_uqmp", rtrn_mpty_uqmp);
    m.def("rtrn_mpty_uqcp", rtrn_mpty_uqcp);

    m.def("pass_mpty_uqmp", pass_mpty_uqmp);
    m.def("pass_mpty_uqcp", pass_mpty_uqcp);
429

430
431
432
433
    // Helpers for testing.
    // These require selected functions above to work first, as indicated:
    m.def("get_mtxt", get_mtxt);                         // pass_mpty_cref
    m.def("unique_ptr_roundtrip", unique_ptr_roundtrip); // pass_mpty_uqmp, rtrn_mpty_uqmp
434
435
}

436
437
} // namespace classh_wip
} // namespace pybind11_tests