test_classh_wip.cpp 17.4 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
        loaded_v_h = inst->get_value_and_holder(get_type_info(typeid(T)));
        if (!loaded_v_h.holder_constructed()) {
67
68
            // IMPROVEABLE: Error message. A change to the existing internals is
            // needed to cleanly distinguish between uninitialized or disowned.
69
70
71
72
            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>();
73
74
75
        return true;
    }

76
77
78
79
80
81
82
83
84
85
    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;
    }

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

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

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

98
99
100
101
102
103
    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
104
105
    }

106
107
108
109
110
111
112
113
    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
114
115
    }

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

120
    static handle cast(mpty const *src, return_value_policy policy, handle parent) {
121
122
123
124
125
126
        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,
127
128
            make_constructor::make_copy_constructor(src),
            make_constructor::make_move_constructor(src));
129
130
    }

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

    template <typename T_>
    using cast_op_type = conditional_t<
137
138
        std::is_same<remove_reference_t<T_>, mpty const *>::value,
        mpty const *,
139
        conditional_t<
140
141
            std::is_same<remove_reference_t<T_>, mpty *>::value,
            mpty *,
142
            conditional_t<
143
144
145
146
147
148
149
                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
150

151
152
153
154
155
156
    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>(); }
157
158

    // clang-format on
159

160
    // Originally type_caster_generic::cast.
161
162
163
164
165
166
167
    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) {
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
        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();
            }
        }

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

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

            case return_value_policy::automatic_reference:
            case return_value_policy::reference:
197
                valueptr       = src;
198
199
200
201
202
203
204
205
206
207
208
209
210
                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);
211
212
                    throw cast_error("return_value_policy = copy, but type " + type_name
                                     + " is non-copyable!");
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#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);
231
232
                    throw cast_error("return_value_policy = move, but type " + type_name
                                     + " is neither movable nor copyable!");
233
234
235
236
237
238
#endif
                }
                wrapper->owned = true;
                break;

            case return_value_policy::reference_internal:
239
                valueptr       = src;
240
241
242
243
244
245
246
247
248
249
250
251
                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();
    }
252
253
254
};

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

258
259
260
261
262
263
264
265
266
    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();
267
        auto st          = type_caster_base<mpty>::src_and_type(src_raw_ptr);
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
        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());
286
287
288
        inst_raw_ptr->owned    = true;
        void *&valueptr        = values_and_holders(inst_raw_ptr).begin()->value_ptr();
        valueptr               = src_raw_void_ptr;
289
290
291
292
293
294
295
296

        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();
297
298
    }

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

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

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

309
310
311
312
313
314
    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);
315
316
    }

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

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

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

327
328
329
330
331
332
333
334
    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();
335
        auto st          = type_caster_base<mpty>::src_and_type(src_raw_ptr);
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
        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());
353
354
355
        inst_raw_ptr->owned    = true;
        void *&valueptr        = values_and_holders(inst_raw_ptr).begin()->value_ptr();
        valueptr               = src_raw_void_ptr;
356
357
358
359
360
361
362
363

        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();
364
365
    }

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

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

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

376
377
378
379
380
381
    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);
382
383
    }

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

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

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

namespace pybind11_tests {
namespace classh_wip {

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

399
400
401
402
403
    py::classh<mpty>(m, "mpty").def(py::init<>()).def(py::init([](const std::string &mtxt) {
        mpty obj;
        obj.mtxt = mtxt;
        return obj;
    }));
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
429

    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);
430

431
432
433
434
    // 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
435
436
}

437
438
} // namespace classh_wip
} // namespace pybind11_tests