classh.h 14.9 KB
Newer Older
1
2
#pragma once

3
#include "smart_holder_poc.h"
4
5
6
7
#include "pybind11.h"

PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)

8
template <typename type_, typename... options>
9
class classh : public detail::generic_type {
10
11
12
13
    template <typename T> using is_subtype = detail::is_strict_base_of<type_, T>;
    template <typename T> using is_base = detail::is_strict_base_of<T, type_>;
    // struct instead of using here to help MSVC:
    template <typename T> struct is_valid_class_option :
14
        detail::any_of<is_subtype<T>, is_base<T>> {};
15
16
17
18
19

public:
    using type = type_;
    using type_alias = detail::exactly_one_t<is_subtype, void, options...>;
    constexpr static bool has_alias = !std::is_void<type_alias>::value;
20
    using holder_type = pybindit::memory::smart_holder;
21
22

    static_assert(detail::all_of<is_valid_class_option<options>...>::value,
23
            "Unknown/invalid classh template parameters provided");
24
25
26
27

    static_assert(!has_alias || std::is_polymorphic<type>::value,
            "Cannot use an alias class with a non-polymorphic type");

28
    PYBIND11_OBJECT(classh, generic_type, PyType_Check)
29
30

    template <typename... Extra>
31
    classh(handle scope, const char *name, const Extra &... extra) {
32
33
        using namespace detail;

34
        // MI can only be specified via classh template options, not constructor parameters
35
36
37
38
39
        static_assert(
            none_of<is_pyobject<Extra>...>::value || // no base class arguments, or:
            (   constexpr_sum(is_pyobject<Extra>::value...) == 1 && // Exactly one base
                constexpr_sum(is_base<options>::value...)   == 0 && // no template option bases
                none_of<std::is_same<multiple_inheritance, Extra>...>::value), // no multiple_inheritance attr
40
            "Error: multiple inheritance bases must be specified via classh template options");
41
42
43
44
45
46
47
48
49
50

        type_record record;
        record.scope = scope;
        record.name = name;
        record.type = &typeid(type);
        record.type_size = sizeof(conditional_t<has_alias, type_alias, type>);
        record.type_align = alignof(conditional_t<has_alias, type_alias, type>&);
        record.holder_size = sizeof(holder_type);
        record.init_instance = init_instance;
        record.dealloc = dealloc;
51
        record.default_holder = false;
52
53
54

        set_operator_new<type>(&record);

55
        /* Register base classes specified via template arguments to classh, if any */
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
        PYBIND11_EXPAND_SIDE_EFFECTS(add_base<options>(record));

        /* Process optional arguments, if any */
        process_attributes<Extra...>::init(extra..., &record);

        generic_type::initialize(record);

        if (has_alias) {
            auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp;
            instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))];
        }
    }

    template <typename Base, detail::enable_if_t<is_base<Base>::value, int> = 0>
    static void add_base(detail::type_record &rec) {
        rec.add_base(typeid(Base), [](void *src) -> void * {
            return static_cast<Base *>(reinterpret_cast<type *>(src));
        });
    }

    template <typename Base, detail::enable_if_t<!is_base<Base>::value, int> = 0>
    static void add_base(detail::type_record &) { }

    template <typename Func, typename... Extra>
80
    classh &def(const char *name_, Func&& f, const Extra&... extra) {
81
82
83
84
85
86
        cpp_function cf(method_adaptor<type>(std::forward<Func>(f)), name(name_), is_method(*this),
                        sibling(getattr(*this, name_, none())), extra...);
        add_class_method(*this, name_, cf);
        return *this;
    }

87
    template <typename Func, typename... Extra> classh &
88
89
90
91
92
93
94
95
96
97
    def_static(const char *name_, Func &&f, const Extra&... extra) {
        static_assert(!std::is_member_function_pointer<Func>::value,
                "def_static(...) called with a non-static member function pointer");
        cpp_function cf(std::forward<Func>(f), name(name_), scope(*this),
                        sibling(getattr(*this, name_, none())), extra...);
        attr(cf.name()) = staticmethod(cf);
        return *this;
    }

    template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
98
    classh &def(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
99
100
101
102
103
        op.execute(*this, extra...);
        return *this;
    }

    template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
104
    classh & def_cast(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
105
106
107
108
109
        op.execute_cast(*this, extra...);
        return *this;
    }

    template <typename... Args, typename... Extra>
110
    classh &def(const detail::initimpl::constructor<Args...> &init, const Extra&... extra) {
111
112
113
114
115
        init.execute(*this, extra...);
        return *this;
    }

    template <typename... Args, typename... Extra>
116
    classh &def(const detail::initimpl::alias_constructor<Args...> &init, const Extra&... extra) {
117
118
119
120
121
        init.execute(*this, extra...);
        return *this;
    }

    template <typename... Args, typename... Extra>
122
    classh &def(detail::initimpl::factory<Args...> &&init, const Extra&... extra) {
123
124
125
126
127
        std::move(init).execute(*this, extra...);
        return *this;
    }

    template <typename... Args, typename... Extra>
128
    classh &def(detail::initimpl::pickle_factory<Args...> &&pf, const Extra &...extra) {
129
130
131
132
133
        std::move(pf).execute(*this, extra...);
        return *this;
    }

    template <typename Func>
134
    classh& def_buffer(Func &&func) {
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
        struct capture { Func func; };
        auto *ptr = new capture { std::forward<Func>(func) };
        install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* {
            detail::make_caster<type> caster;
            if (!caster.load(obj, false))
                return nullptr;
            return new buffer_info(((capture *) ptr)->func(caster));
        }, ptr);
        weakref(m_ptr, cpp_function([ptr](handle wr) {
            delete ptr;
            wr.dec_ref();
        })).release();
        return *this;
    }

    template <typename Return, typename Class, typename... Args>
151
    classh &def_buffer(Return (Class::*func)(Args...)) {
152
153
154
155
        return def_buffer([func] (type &obj) { return (obj.*func)(); });
    }

    template <typename Return, typename Class, typename... Args>
156
    classh &def_buffer(Return (Class::*func)(Args...) const) {
157
158
159
160
        return def_buffer([func] (const type &obj) { return (obj.*func)(); });
    }

    template <typename C, typename D, typename... Extra>
161
    classh &def_readwrite(const char *name, D C::*pm, const Extra&... extra) {
162
163
164
165
166
167
168
169
        static_assert(std::is_same<C, type>::value || std::is_base_of<C, type>::value, "def_readwrite() requires a class member (or base class member)");
        cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)),
                     fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this));
        def_property(name, fget, fset, return_value_policy::reference_internal, extra...);
        return *this;
    }

    template <typename C, typename D, typename... Extra>
170
    classh &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) {
171
172
173
174
175
176
177
        static_assert(std::is_same<C, type>::value || std::is_base_of<C, type>::value, "def_readonly() requires a class member (or base class member)");
        cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this));
        def_property_readonly(name, fget, return_value_policy::reference_internal, extra...);
        return *this;
    }

    template <typename D, typename... Extra>
178
    classh &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) {
179
180
181
182
183
184
185
        cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)),
                     fset([pm](object, const D &value) { *pm = value; }, scope(*this));
        def_property_static(name, fget, fset, return_value_policy::reference, extra...);
        return *this;
    }

    template <typename D, typename... Extra>
186
    classh &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) {
187
188
189
190
191
192
193
        cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this));
        def_property_readonly_static(name, fget, return_value_policy::reference, extra...);
        return *this;
    }

    /// Uses return_value_policy::reference_internal by default
    template <typename Getter, typename... Extra>
194
    classh &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) {
195
196
197
198
199
200
        return def_property_readonly(name, cpp_function(method_adaptor<type>(fget)),
                                     return_value_policy::reference_internal, extra...);
    }

    /// Uses cpp_function's return_value_policy by default
    template <typename... Extra>
201
    classh &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) {
202
203
204
205
206
        return def_property(name, fget, nullptr, extra...);
    }

    /// Uses return_value_policy::reference by default
    template <typename Getter, typename... Extra>
207
    classh &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) {
208
209
210
211
212
        return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...);
    }

    /// Uses cpp_function's return_value_policy by default
    template <typename... Extra>
213
    classh &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) {
214
215
216
217
218
        return def_property_static(name, fget, nullptr, extra...);
    }

    /// Uses return_value_policy::reference_internal by default
    template <typename Getter, typename Setter, typename... Extra>
219
    classh &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) {
220
221
222
        return def_property(name, fget, cpp_function(method_adaptor<type>(fset)), extra...);
    }
    template <typename Getter, typename... Extra>
223
    classh &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) {
224
225
226
227
228
229
        return def_property(name, cpp_function(method_adaptor<type>(fget)), fset,
                            return_value_policy::reference_internal, extra...);
    }

    /// Uses cpp_function's return_value_policy by default
    template <typename... Extra>
230
    classh &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) {
231
232
233
234
235
        return def_property_static(name, fget, fset, is_method(*this), extra...);
    }

    /// Uses return_value_policy::reference by default
    template <typename Getter, typename... Extra>
236
    classh &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) {
237
238
239
240
241
        return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...);
    }

    /// Uses cpp_function's return_value_policy by default
    template <typename... Extra>
242
    classh &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) {
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
        static_assert( 0 == detail::constexpr_sum(std::is_base_of<arg, Extra>::value...),
                      "Argument annotations are not allowed for properties");
        auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset);
        auto *rec_active = rec_fget;
        if (rec_fget) {
           char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
           detail::process_attributes<Extra...>::init(extra..., rec_fget);
           if (rec_fget->doc && rec_fget->doc != doc_prev) {
              free(doc_prev);
              rec_fget->doc = strdup(rec_fget->doc);
           }
        }
        if (rec_fset) {
            char *doc_prev = rec_fset->doc;
            detail::process_attributes<Extra...>::init(extra..., rec_fset);
            if (rec_fset->doc && rec_fset->doc != doc_prev) {
                free(doc_prev);
                rec_fset->doc = strdup(rec_fset->doc);
            }
            if (! rec_active) rec_active = rec_fset;
        }
        def_property_static_impl(name, fget, fset, rec_active);
        return *this;
    }

private:
    template <typename T>
270
271
    static void init_holder(bool /*owned*/, detail::value_and_holder &/*v_h*/,
            holder_type * /* unused */, const std::enable_shared_from_this<T> * /* dummy */) {
272
        throw std::runtime_error("Not implemented: classh::init_holder enable_shared_from_this.");
273
274
    }

275
276
    static void init_holder(bool owned, detail::value_and_holder &v_h,
            holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this<T>) */) {
277
        if (holder_ptr) {
278
            new (std::addressof(v_h.holder<holder_type>())) holder_type(
279
280
281
282
283
284
285
286
                std::move(*holder_ptr));
        } else if (owned) {
            new (std::addressof(v_h.holder<holder_type>())) holder_type(
                holder_type::from_raw_ptr_take_ownership(v_h.value_ptr<type>()));
        }
        else {
            new (std::addressof(v_h.holder<holder_type>())) holder_type(
                holder_type::from_raw_ptr_unowned(v_h.value_ptr<type>()));
287
        }
288
        v_h.set_holder_constructed();
289
290
291
292
293
294
295
296
    }

    static void init_instance(detail::instance *inst, const void *holder_ptr) {
        auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type)));
        if (!v_h.instance_registered()) {
            register_instance(inst, v_h.value_ptr(), v_h.type);
            v_h.set_instance_registered();
        }
297
298
299
        // Need for const_cast is a consequence of the type_info::init_instance type:
        // void (*init_instance)(instance *, const void *);
        init_holder(inst->owned, v_h, static_cast<holder_type *>(const_cast<void *>(holder_ptr)), v_h.value_ptr<type>());
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
    }

    /// Deallocates an instance; via holder, if constructed; otherwise via operator delete.
    static void dealloc(detail::value_and_holder &v_h) {
        // We could be deallocating because we are cleaning up after a Python exception.
        // If so, the Python error indicator will be set. We need to clear that before
        // running the destructor, in case the destructor code calls more Python.
        // If we don't, the Python API will exit with an exception, and pybind11 will
        // throw error_already_set from the C++ destructor which is forbidden and triggers
        // std::terminate().
        error_scope scope;
        if (v_h.holder_constructed()) {
            v_h.holder<holder_type>().~holder_type();
            v_h.set_holder_constructed(false);
        }
        else {
            detail::call_operator_delete(v_h.value_ptr<type>(),
                v_h.type->type_size,
                v_h.type->type_align
            );
        }
        v_h.value_ptr() = nullptr;
    }

    static detail::function_record *get_function_record(handle h) {
        h = detail::get_function(h);
        return h ? (detail::function_record *) reinterpret_borrow<capsule>(PyCFunction_GET_SELF(h.ptr()))
                 : nullptr;
    }
};
330
331

PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)