test_classh_wip.cpp 17.2 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
59
60
61
62
63
64
65
66
67
68
69
70
inline std::pair<bool, handle> find_existing_python_instance(void *src_void_ptr,
                                                             const detail::type_info *tinfo) {
    // Loop copied from type_caster_generic::cast.
    // IMPROVEABLE: Factor out of type_caster_generic::cast.
    auto it_instances = get_internals().registered_instances.equal_range(src_void_ptr);
    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 std::make_pair(true, handle((PyObject *) it_i->second).inc_ref());
        }
    }
    return std::make_pair(false, handle());
}

71
72
template <typename T>
struct smart_holder_type_caster_load {
73
74
    using holder_type = pybindit::memory::smart_holder;

75
    bool load(handle src, bool /*convert*/) {
76
77
78
        if (!isinstance<T>(src))
            return false;
        auto inst  = reinterpret_cast<instance *>(src.ptr());
79
80
        loaded_v_h = inst->get_value_and_holder(get_type_info(typeid(T)));
        if (!loaded_v_h.holder_constructed()) {
81
82
            // IMPROVEABLE: Error message. A change to the existing internals is
            // needed to cleanly distinguish between uninitialized or disowned.
83
84
85
86
            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>();
87
88
89
        return true;
    }

90
91
92
93
94
95
96
97
98
99
    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;
    }

100
protected:
101
102
    value_and_holder loaded_v_h;
    holder_type *loaded_smhldr_ptr = nullptr;
103
104
};

105
template <>
106
struct type_caster<mpty> : smart_holder_type_caster_load<mpty> {
107
108
109
110
111
    static constexpr auto name = _<mpty>();

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

112
113
114
115
116
117
    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
118
119
    }

120
121
122
123
124
125
126
127
    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
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
    static handle cast(mpty const *src, return_value_policy policy, handle parent) {
135
136
137
138
139
140
        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,
141
142
            make_constructor::make_copy_constructor(src),
            make_constructor::make_move_constructor(src));
143
144
    }

145
    static handle cast(mpty *src, return_value_policy policy, handle parent) {
146
        return cast(const_cast<mpty const *>(src), policy, parent); // Mutbl2Const
147
148
149
150
    }

    template <typename T_>
    using cast_op_type = conditional_t<
151
152
        std::is_same<remove_reference_t<T_>, mpty const *>::value,
        mpty const *,
153
        conditional_t<
154
155
            std::is_same<remove_reference_t<T_>, mpty *>::value,
            mpty *,
156
            conditional_t<
157
158
159
160
161
162
163
                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
164

165
166
167
168
169
170
    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>(); }
171
172

    // clang-format on
173

174
    // Originally type_caster_generic::cast.
175
176
177
178
179
180
181
    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) {
182
183
184
185
186
187
188
        if (!tinfo) // no type info: error will be set already
            return handle();

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

189
190
191
        auto existing_inst = find_existing_python_instance(src, tinfo);
        if (existing_inst.first)
            return existing_inst.second;
192

193
194
195
        auto inst       = reinterpret_steal<object>(make_new_instance(tinfo->type));
        auto wrapper    = reinterpret_cast<instance *>(inst.ptr());
        wrapper->owned  = false;
196
197
198
199
200
        void *&valueptr = values_and_holders(wrapper).begin()->value_ptr();

        switch (policy) {
            case return_value_policy::automatic:
            case return_value_policy::take_ownership:
201
                valueptr       = src;
202
203
204
205
206
                wrapper->owned = true;
                break;

            case return_value_policy::automatic_reference:
            case return_value_policy::reference:
207
                valueptr       = src;
208
209
210
211
212
213
214
215
216
217
218
219
220
                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);
221
222
                    throw cast_error("return_value_policy = copy, but type " + type_name
                                     + " is non-copyable!");
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#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);
241
242
                    throw cast_error("return_value_policy = move, but type " + type_name
                                     + " is neither movable nor copyable!");
243
244
245
246
247
248
#endif
                }
                wrapper->owned = true;
                break;

            case return_value_policy::reference_internal:
249
                valueptr       = src;
250
251
252
253
254
255
256
257
258
259
260
261
                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();
    }
262
263
264
};

template <>
265
struct type_caster<std::shared_ptr<mpty>> : smart_holder_type_caster_load<mpty> {
266
267
    static constexpr auto name = _<std::shared_ptr<mpty>>();

268
269
270
271
272
273
274
275
276
    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();
277
        auto st          = type_caster_base<mpty>::src_and_type(src_raw_ptr);
278
279
280
281
282
        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;
283
284
285
286
287
        auto existing_inst             = find_existing_python_instance(src_raw_void_ptr, tinfo);
        if (existing_inst.first)
            // MISSING: Enforcement of consistency with existing smart_holder.
            // MISSING: keep_alive.
            return existing_inst.second;
288
289
290

        object inst            = reinterpret_steal<object>(make_new_instance(tinfo->type));
        instance *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
291
292
293
        inst_raw_ptr->owned    = true;
        void *&valueptr        = values_and_holders(inst_raw_ptr).begin()->value_ptr();
        valueptr               = src_raw_void_ptr;
294
295
296
297
298
299
300
301

        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();
302
303
    }

304
305
    template <typename>
    using cast_op_type = std::shared_ptr<mpty>;
306

307
    operator std::shared_ptr<mpty>() { return loaded_smhldr_ptr->as_shared_ptr<mpty>(); }
308
309
310
};

template <>
311
struct type_caster<std::shared_ptr<mpty const>> : smart_holder_type_caster_load<mpty> {
312
313
    static constexpr auto name = _<std::shared_ptr<mpty const>>();

314
315
316
317
318
319
    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);
320
321
    }

322
323
    template <typename>
    using cast_op_type = std::shared_ptr<mpty const>;
324

325
    operator std::shared_ptr<mpty const>() { return loaded_smhldr_ptr->as_shared_ptr<mpty>(); }
326
327
328
};

template <>
329
struct type_caster<std::unique_ptr<mpty>> : smart_holder_type_caster_load<mpty> {
330
331
    static constexpr auto name = _<std::unique_ptr<mpty>>();

332
333
334
335
336
337
338
339
    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();
340
        auto st          = type_caster_base<mpty>::src_and_type(src_raw_ptr);
341
342
343
344
345
        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;
346
347
348
        auto existing_inst             = find_existing_python_instance(src_raw_void_ptr, tinfo);
        if (existing_inst.first)
            throw cast_error("Invalid unique_ptr: another instance owns this pointer already.");
349
350
351

        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