/src/pybind11/include/pybind11/detail/init.h
Line | Count | Source |
1 | | /* |
2 | | pybind11/detail/init.h: init factory function implementation and support code. |
3 | | |
4 | | Copyright (c) 2017 Jason Rhinelander <jason@imaginary.ca> |
5 | | |
6 | | All rights reserved. Use of this source code is governed by a |
7 | | BSD-style license that can be found in the LICENSE file. |
8 | | */ |
9 | | |
10 | | #pragma once |
11 | | |
12 | | #include "class.h" |
13 | | #include "using_smart_holder.h" |
14 | | |
15 | | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) |
16 | | |
17 | | PYBIND11_WARNING_DISABLE_MSVC(4127) |
18 | | |
19 | | PYBIND11_NAMESPACE_BEGIN(detail) |
20 | | |
21 | | template <> |
22 | | class type_caster<value_and_holder> { |
23 | | public: |
24 | 0 | bool load(handle h, bool) { |
25 | 0 | value = reinterpret_cast<value_and_holder *>(h.ptr()); |
26 | 0 | return true; |
27 | 0 | } |
28 | | |
29 | | template <typename> |
30 | | using cast_op_type = value_and_holder &; |
31 | 0 | explicit operator value_and_holder &() { return *value; } |
32 | | static constexpr auto name = const_name<value_and_holder>(); |
33 | | |
34 | | private: |
35 | | value_and_holder *value = nullptr; |
36 | | }; |
37 | | |
38 | | PYBIND11_NAMESPACE_BEGIN(initimpl) |
39 | | |
40 | 0 | inline void no_nullptr(const void *ptr) { |
41 | 0 | if (!ptr) { |
42 | 0 | throw type_error("pybind11::init(): factory function returned nullptr"); |
43 | 0 | } |
44 | 0 | } |
45 | | |
46 | | // Implementing functions for all forms of py::init<...> and py::init(...) |
47 | | template <typename Class> |
48 | | using Cpp = typename Class::type; |
49 | | template <typename Class> |
50 | | using Alias = typename Class::type_alias; |
51 | | template <typename Class> |
52 | | using Holder = typename Class::holder_type; |
53 | | |
54 | | template <typename Class> |
55 | | using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>; |
56 | | |
57 | | // Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. |
58 | | template <typename Class, enable_if_t<Class::has_alias, int> = 0> |
59 | | bool is_alias(Cpp<Class> *ptr) { |
60 | | return dynamic_cast<Alias<Class> *>(ptr) != nullptr; |
61 | | } |
62 | | // Failing fallback version of the above for a no-alias class (always returns false) |
63 | | template <typename /*Class*/> |
64 | | constexpr bool is_alias(const void *) { |
65 | | return false; |
66 | | } |
67 | | |
68 | | // Constructs and returns a new object; if the given arguments don't map to a constructor, we fall |
69 | | // back to brace aggregate initialization so that for aggregate initialization can be used with |
70 | | // py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For |
71 | | // non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually |
72 | | // works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor). |
73 | | template <typename Class, |
74 | | typename... Args, |
75 | | detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0> |
76 | | inline Class *construct_or_initialize(Args &&...args) { |
77 | | return new Class(std::forward<Args>(args)...); |
78 | | } |
79 | | template <typename Class, |
80 | | typename... Args, |
81 | | detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0> |
82 | | inline Class *construct_or_initialize(Args &&...args) { |
83 | | return new Class{std::forward<Args>(args)...}; |
84 | | } |
85 | | |
86 | | // Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with |
87 | | // an alias to provide only a single Cpp factory function as long as the Alias can be |
88 | | // constructed from an rvalue reference of the base Cpp type. This means that Alias classes |
89 | | // can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to |
90 | | // inherit all the base class constructors. |
91 | | template <typename Class> |
92 | | void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, |
93 | | value_and_holder &v_h, |
94 | | Cpp<Class> &&base) { |
95 | | v_h.value_ptr() = new Alias<Class>(std::move(base)); |
96 | | } |
97 | | template <typename Class> |
98 | | [[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, |
99 | | value_and_holder &, |
100 | | Cpp<Class> &&) { |
101 | | throw type_error("pybind11::init(): unable to convert returned instance to required " |
102 | | "alias class: no `Alias<Class>(Class &&)` constructor available"); |
103 | | } |
104 | | |
105 | | // Error-generating fallback for factories that don't match one of the below construction |
106 | | // mechanisms. |
107 | | template <typename Class> |
108 | | void construct(...) { |
109 | | static_assert(!std::is_same<Class, Class>::value /* always false */, |
110 | | "pybind11::init(): init function must return a compatible pointer, " |
111 | | "holder, or value"); |
112 | | } |
113 | | |
114 | | // Pointer return v1: the factory function returns a class pointer for a registered class. |
115 | | // If we don't need an alias (because this class doesn't have one, or because the final type is |
116 | | // inherited on the Python side) we can simply take over ownership. Otherwise we need to try to |
117 | | // construct an Alias from the returned base instance. |
118 | | template <typename Class> |
119 | | void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) { |
120 | | PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); |
121 | | no_nullptr(ptr); |
122 | | if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) { |
123 | | // We're going to try to construct an alias by moving the cpp type. Whether or not |
124 | | // that succeeds, we still need to destroy the original cpp pointer (either the |
125 | | // moved away leftover, if the alias construction works, or the value itself if we |
126 | | // throw an error), but we can't just call `delete ptr`: it might have a special |
127 | | // deleter, or might be shared_from_this. So we construct a holder around it as if |
128 | | // it was a normal instance, then steal the holder away into a local variable; thus |
129 | | // the holder and destruction happens when we leave the C++ scope, and the holder |
130 | | // class gets to handle the destruction however it likes. |
131 | | v_h.value_ptr() = ptr; |
132 | | v_h.set_instance_registered(true); // Trick to prevent init_instance from registering it |
133 | | // DANGER ZONE BEGIN: exceptions will leave v_h in an invalid state. |
134 | | v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder |
135 | | Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder |
136 | | v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null |
137 | | v_h.set_instance_registered(false); |
138 | | // DANGER ZONE END. |
139 | | |
140 | | construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(*ptr)); |
141 | | } else { |
142 | | // Otherwise the type isn't inherited, so we don't need an Alias |
143 | | v_h.value_ptr() = ptr; |
144 | | } |
145 | | } |
146 | | |
147 | | // Pointer return v2: a factory that always returns an alias instance ptr. We simply take over |
148 | | // ownership of the pointer. |
149 | | template <typename Class, enable_if_t<Class::has_alias, int> = 0> |
150 | | void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) { |
151 | | no_nullptr(alias_ptr); |
152 | | v_h.value_ptr() = static_cast<Cpp<Class> *>(alias_ptr); |
153 | | } |
154 | | |
155 | | // Holder return: copy its pointer, and move or copy the returned holder into the new instance's |
156 | | // holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a |
157 | | // derived type (through those holder's implicit conversion from derived class holder |
158 | | // constructors). |
159 | | template <typename Class, detail::enable_if_t<!is_smart_holder<Holder<Class>>::value, int> = 0> |
160 | | void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { |
161 | | PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); |
162 | | auto *ptr = holder_helper<Holder<Class>>::get(holder); |
163 | | no_nullptr(ptr); |
164 | | // If we need an alias, check that the held pointer is actually an alias instance |
165 | | if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) { |
166 | | throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " |
167 | | "is not an alias instance"); |
168 | | } |
169 | | |
170 | | // Cast away constness to store in void* storage. |
171 | | // The value_and_holder storage is fundamentally untyped (void**), so we lose |
172 | | // const-correctness here by design. The const qualifier will be restored |
173 | | // when the pointer is later retrieved and cast back to the original type. |
174 | | // This explicit const_cast makes the const-removal clearly visible. |
175 | | v_h.value_ptr() = const_cast<void *>(static_cast<const void *>(ptr)); |
176 | | v_h.type->init_instance(v_h.inst, &holder); |
177 | | } |
178 | | |
179 | | // return-by-value version 1: returning a cpp class by value. If the class has an alias and an |
180 | | // alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct |
181 | | // the alias from the base when needed (i.e. because of Python-side inheritance). When we don't |
182 | | // need it, we simply move-construct the cpp value into a new instance. |
183 | | template <typename Class> |
184 | | void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) { |
185 | | PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); |
186 | | static_assert(is_move_constructible<Cpp<Class>>::value, |
187 | | "pybind11::init() return-by-value factory function requires a movable class"); |
188 | | if (Class::has_alias && need_alias) { |
189 | | construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result)); |
190 | | } else { |
191 | | v_h.value_ptr() = new Cpp<Class>(std::move(result)); |
192 | | } |
193 | | } |
194 | | |
195 | | // return-by-value version 2: returning a value of the alias type itself. We move-construct an |
196 | | // Alias instance (even if no the python-side inheritance is involved). The is intended for |
197 | | // cases where Alias initialization is always desired. |
198 | | template <typename Class> |
199 | | void construct(value_and_holder &v_h, Alias<Class> &&result, bool) { |
200 | | static_assert( |
201 | | is_move_constructible<Alias<Class>>::value, |
202 | | "pybind11::init() return-by-alias-value factory function requires a movable alias class"); |
203 | | v_h.value_ptr() = new Alias<Class>(std::move(result)); |
204 | | } |
205 | | |
206 | | template <typename T, typename D> |
207 | | smart_holder init_smart_holder_from_unique_ptr(std::unique_ptr<T, D> &&unq_ptr, |
208 | | bool void_cast_raw_ptr) { |
209 | | return smart_holder::from_unique_ptr( |
210 | | std::move(unq_ptr), /*mi_subobject_ptr*/ nullptr, void_cast_raw_ptr); |
211 | | } |
212 | | |
213 | | template <typename Class, |
214 | | typename D = std::default_delete<Cpp<Class>>, |
215 | | detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0> |
216 | | void construct(value_and_holder &v_h, std::unique_ptr<Cpp<Class>, D> &&unq_ptr, bool need_alias) { |
217 | | PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); |
218 | | auto *ptr = unq_ptr.get(); |
219 | | no_nullptr(ptr); |
220 | | if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) { |
221 | | throw type_error("pybind11::init(): construction failed: returned std::unique_ptr pointee " |
222 | | "is not an alias instance"); |
223 | | } |
224 | | // Here and below: if the new object is a trampoline, the shared_from_this mechanism needs |
225 | | // to be prevented from accessing the smart_holder vptr, because it does not keep the |
226 | | // trampoline Python object alive. For types that don't inherit from enable_shared_from_this |
227 | | // it does not matter if void_cast_raw_ptr is true or false, therefore it's not necessary |
228 | | // to also inspect the type. |
229 | | auto smhldr = init_smart_holder_from_unique_ptr( |
230 | | std::move(unq_ptr), /*void_cast_raw_ptr*/ Class::has_alias && is_alias<Class>(ptr)); |
231 | | v_h.value_ptr() = ptr; |
232 | | v_h.type->init_instance(v_h.inst, &smhldr); |
233 | | } |
234 | | |
235 | | template <typename Class, |
236 | | typename D = std::default_delete<Alias<Class>>, |
237 | | detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0> |
238 | | void construct(value_and_holder &v_h, |
239 | | std::unique_ptr<Alias<Class>, D> &&unq_ptr, |
240 | | bool /*need_alias*/) { |
241 | | auto *ptr = unq_ptr.get(); |
242 | | no_nullptr(ptr); |
243 | | auto smhldr |
244 | | = init_smart_holder_from_unique_ptr(std::move(unq_ptr), /*void_cast_raw_ptr*/ true); |
245 | | v_h.value_ptr() = ptr; |
246 | | v_h.type->init_instance(v_h.inst, &smhldr); |
247 | | } |
248 | | |
249 | | template <typename PtrType, typename Class> |
250 | | void construct_from_shared_ptr(value_and_holder &v_h, |
251 | | std::shared_ptr<PtrType> &&shd_ptr, |
252 | | bool need_alias) { |
253 | | static_assert(std::is_same<PtrType, Cpp<Class>>::value |
254 | | || std::is_same<PtrType, const Cpp<Class>>::value, |
255 | | "Expected (const) Cpp<Class> as shared_ptr pointee"); |
256 | | auto *ptr = shd_ptr.get(); |
257 | | no_nullptr(ptr); |
258 | | if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) { |
259 | | throw type_error("pybind11::init(): construction failed: returned std::shared_ptr pointee " |
260 | | "is not an alias instance"); |
261 | | } |
262 | | // Cast to non-const if needed, consistent with internal design |
263 | | auto smhldr |
264 | | = smart_holder::from_shared_ptr(std::const_pointer_cast<Cpp<Class>>(std::move(shd_ptr))); |
265 | | v_h.value_ptr() = const_cast<Cpp<Class> *>(ptr); |
266 | | v_h.type->init_instance(v_h.inst, &smhldr); |
267 | | } |
268 | | |
269 | | template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0> |
270 | | void construct(value_and_holder &v_h, std::shared_ptr<Cpp<Class>> &&shd_ptr, bool need_alias) { |
271 | | construct_from_shared_ptr<Cpp<Class>, Class>(v_h, std::move(shd_ptr), need_alias); |
272 | | } |
273 | | |
274 | | template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0> |
275 | | void construct(value_and_holder &v_h, |
276 | | std::shared_ptr<const Cpp<Class>> &&shd_ptr, |
277 | | bool need_alias) { |
278 | | construct_from_shared_ptr<const Cpp<Class>, Class>(v_h, std::move(shd_ptr), need_alias); |
279 | | } |
280 | | |
281 | | template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0> |
282 | | void construct(value_and_holder &v_h, |
283 | | std::shared_ptr<Alias<Class>> &&shd_ptr, |
284 | | bool /*need_alias*/) { |
285 | | auto *ptr = shd_ptr.get(); |
286 | | no_nullptr(ptr); |
287 | | auto smhldr = smart_holder::from_shared_ptr(shd_ptr); |
288 | | v_h.value_ptr() = ptr; |
289 | | v_h.type->init_instance(v_h.inst, &smhldr); |
290 | | } |
291 | | |
292 | | // Implementing class for py::init<...>() |
293 | | template <typename... Args> |
294 | | struct constructor { |
295 | | template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0> |
296 | | static void execute(Class &cl, const Extra &...extra) { |
297 | | cl.def( |
298 | | "__init__", |
299 | | [](value_and_holder &v_h, |
300 | | Args... args) { // NOLINT(performance-unnecessary-value-param) |
301 | | v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); |
302 | | }, |
303 | | is_new_style_constructor(), |
304 | | extra...); |
305 | | } |
306 | | |
307 | | template <typename Class, |
308 | | typename... Extra, |
309 | | enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value, |
310 | | int> = 0> |
311 | | static void execute(Class &cl, const Extra &...extra) { |
312 | | cl.def( |
313 | | "__init__", |
314 | | [](value_and_holder &v_h, Args... args) { |
315 | | if (Py_TYPE(v_h.inst) == v_h.type->type) { |
316 | | v_h.value_ptr() |
317 | | = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); |
318 | | } else { |
319 | | v_h.value_ptr() |
320 | | = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); |
321 | | } |
322 | | }, |
323 | | is_new_style_constructor(), |
324 | | extra...); |
325 | | } |
326 | | |
327 | | template <typename Class, |
328 | | typename... Extra, |
329 | | enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value, |
330 | | int> = 0> |
331 | | static void execute(Class &cl, const Extra &...extra) { |
332 | | cl.def( |
333 | | "__init__", |
334 | | [](value_and_holder &v_h, Args... args) { |
335 | | v_h.value_ptr() |
336 | | = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); |
337 | | }, |
338 | | is_new_style_constructor(), |
339 | | extra...); |
340 | | } |
341 | | }; |
342 | | |
343 | | // Implementing class for py::init_alias<...>() |
344 | | template <typename... Args> |
345 | | struct alias_constructor { |
346 | | template <typename Class, |
347 | | typename... Extra, |
348 | | enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, |
349 | | int> = 0> |
350 | | static void execute(Class &cl, const Extra &...extra) { |
351 | | cl.def( |
352 | | "__init__", |
353 | | [](value_and_holder &v_h, Args... args) { |
354 | | v_h.value_ptr() |
355 | | = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); |
356 | | }, |
357 | | is_new_style_constructor(), |
358 | | extra...); |
359 | | } |
360 | | }; |
361 | | |
362 | | // Implementation class for py::init(Func) and py::init(Func, AliasFunc) |
363 | | template <typename CFunc, |
364 | | typename AFunc = void_type (*)(), |
365 | | typename = function_signature_t<CFunc>, |
366 | | typename = function_signature_t<AFunc>> |
367 | | struct factory; |
368 | | |
369 | | // Specialization for py::init(Func) |
370 | | // Note: The 4th template parameter `void_type()` is explicitly specified to resolve a |
371 | | // template ambiguity with the dual-factory specialization below when compiled with |
372 | | // nvcc + GCC (see #5565). Without it, both specializations match equally well for the |
373 | | // single-factory case, since the 4th parameter defaults to |
374 | | // `function_signature_t<void_type(*)()>` = `void_type()`, which the dual-factory |
375 | | // specialization can also decompose as `AReturn(AArgs...)` with `AReturn=void_type` |
376 | | // and `AArgs={}`. |
377 | | template <typename Func, typename Return, typename... Args> |
378 | | struct factory<Func, void_type (*)(), Return(Args...), void_type()> { |
379 | | remove_reference_t<Func> class_factory; |
380 | | |
381 | | // NOLINTNEXTLINE(google-explicit-constructor) |
382 | | factory(Func &&f) : class_factory(std::forward<Func>(f)) {} |
383 | | |
384 | | // The given class either has no alias or has no separate alias factory; |
385 | | // this always constructs the class itself. If the class is registered with an alias |
386 | | // type and an alias instance is needed (i.e. because the final type is a Python class |
387 | | // inheriting from the C++ type) the returned value needs to either already be an alias |
388 | | // instance, or the alias needs to be constructible from a `Class &&` argument. |
389 | | template <typename Class, typename... Extra> |
390 | | void execute(Class &cl, const Extra &...extra) && { |
391 | | #if defined(__cpp_init_captures) && __cpp_init_captures >= 201304L |
392 | | cl.def( |
393 | | "__init__", |
394 | | [func = std::move(class_factory)] |
395 | | #else |
396 | | auto &func = class_factory; |
397 | | cl.def( |
398 | | "__init__", |
399 | | [func] |
400 | | #endif |
401 | | (value_and_holder &v_h, Args... args) { |
402 | | construct<Class>( |
403 | | v_h, func(std::forward<Args>(args)...), Py_TYPE(v_h.inst) != v_h.type->type); |
404 | | }, |
405 | | is_new_style_constructor(), |
406 | | extra...); |
407 | | } |
408 | | }; |
409 | | |
410 | | // Specialization for py::init(Func, AliasFunc) |
411 | | template <typename CFunc, |
412 | | typename AFunc, |
413 | | typename CReturn, |
414 | | typename... CArgs, |
415 | | typename AReturn, |
416 | | typename... AArgs> |
417 | | struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> { |
418 | | static_assert(sizeof...(CArgs) == sizeof...(AArgs), |
419 | | "pybind11::init(class_factory, alias_factory): class and alias factories " |
420 | | "must have identical argument signatures"); |
421 | | static_assert(all_of<std::is_same<CArgs, AArgs>...>::value, |
422 | | "pybind11::init(class_factory, alias_factory): class and alias factories " |
423 | | "must have identical argument signatures"); |
424 | | |
425 | | remove_reference_t<CFunc> class_factory; |
426 | | remove_reference_t<AFunc> alias_factory; |
427 | | |
428 | | factory(CFunc &&c, AFunc &&a) |
429 | | : class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) {} |
430 | | |
431 | | // The class factory is called when the `self` type passed to `__init__` is the direct |
432 | | // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. |
433 | | template <typename Class, typename... Extra> |
434 | | void execute(Class &cl, const Extra &...extra) && { |
435 | | static_assert(Class::has_alias, |
436 | | "The two-argument version of `py::init()` can " |
437 | | "only be used if the class has an alias"); |
438 | | #if defined(__cpp_init_captures) && __cpp_init_captures >= 201304L |
439 | | cl.def( |
440 | | "__init__", |
441 | | [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] |
442 | | #else |
443 | | auto &class_func = class_factory; |
444 | | auto &alias_func = alias_factory; |
445 | | cl.def( |
446 | | "__init__", |
447 | | [class_func, alias_func] |
448 | | #endif |
449 | | (value_and_holder &v_h, CArgs... args) { |
450 | | if (Py_TYPE(v_h.inst) == v_h.type->type) { |
451 | | // If the instance type equals the registered type we don't have inheritance, |
452 | | // so don't need the alias and can construct using the class function: |
453 | | construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false); |
454 | | } else { |
455 | | construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true); |
456 | | } |
457 | | }, |
458 | | is_new_style_constructor(), |
459 | | extra...); |
460 | | } |
461 | | }; |
462 | | |
463 | | /// Set just the C++ state. Same as `__init__`. |
464 | | template <typename Class, typename T> |
465 | | void setstate(value_and_holder &v_h, T &&result, bool need_alias) { |
466 | | construct<Class>(v_h, std::forward<T>(result), need_alias); |
467 | | } |
468 | | |
469 | | /// Set both the C++ and Python states |
470 | | template <typename Class, |
471 | | typename T, |
472 | | typename O, |
473 | | enable_if_t<std::is_convertible<O, handle>::value, int> = 0> |
474 | | void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) { |
475 | | construct<Class>(v_h, std::move(result.first), need_alias); |
476 | | auto d = handle(result.second); |
477 | | if (PyDict_Check(d.ptr()) && PyDict_Size(d.ptr()) == 0) { |
478 | | // Skipping setattr below, to not force use of py::dynamic_attr() for Class unnecessarily. |
479 | | // See PR #2972 for details. |
480 | | return; |
481 | | } |
482 | | // Our tests never run into an unset dict, but being careful here for now (see #5658) |
483 | | auto dict = getattr(reinterpret_cast<PyObject *>(v_h.inst), "__dict__", none()); |
484 | | if (dict.is_none()) { |
485 | | setattr(reinterpret_cast<PyObject *>(v_h.inst), "__dict__", d); |
486 | | } else { |
487 | | // Keep the original object dict and just update it |
488 | | if (PyDict_Update(dict.ptr(), d.ptr()) < 0) { |
489 | | throw error_already_set(); |
490 | | } |
491 | | } |
492 | | } |
493 | | |
494 | | /// Implementation for py::pickle(GetState, SetState) |
495 | | template <typename Get, |
496 | | typename Set, |
497 | | typename = function_signature_t<Get>, |
498 | | typename = function_signature_t<Set>> |
499 | | struct pickle_factory; |
500 | | |
501 | | template <typename Get, |
502 | | typename Set, |
503 | | typename RetState, |
504 | | typename Self, |
505 | | typename NewInstance, |
506 | | typename ArgState> |
507 | | struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> { |
508 | | using Ret = intrinsic_t<RetState>; |
509 | | using Arg = intrinsic_t<ArgState>; |
510 | | |
511 | | // Subclasses are now allowed for support between type hint and generic versions of types |
512 | | // (e.g.) typing::List <--> list |
513 | | static_assert(std::is_same<Ret, Arg>::value || std::is_base_of<Ret, Arg>::value |
514 | | || std::is_base_of<Arg, Ret>::value, |
515 | | "The type returned by `__getstate__` must be the same or subclass of the " |
516 | | "argument accepted by `__setstate__`"); |
517 | | |
518 | | remove_reference_t<Get> get; |
519 | | remove_reference_t<Set> set; |
520 | | |
521 | | pickle_factory(Get get, Set set) : get(std::forward<Get>(get)), set(std::forward<Set>(set)) {} |
522 | | |
523 | | template <typename Class, typename... Extra> |
524 | | void execute(Class &cl, const Extra &...extra) && { |
525 | | cl.def("__getstate__", std::move(get), pos_only()); |
526 | | |
527 | | #if defined(__cpp_init_captures) && __cpp_init_captures >= 201304L |
528 | | cl.def( |
529 | | "__setstate__", |
530 | | [func = std::move(set)] |
531 | | #else |
532 | | auto &func = set; |
533 | | cl.def( |
534 | | "__setstate__", |
535 | | [func] |
536 | | #endif |
537 | | (value_and_holder &v_h, ArgState state) { |
538 | | setstate<Class>( |
539 | | v_h, func(std::forward<ArgState>(state)), Py_TYPE(v_h.inst) != v_h.type->type); |
540 | | }, |
541 | | is_new_style_constructor(), |
542 | | extra...); |
543 | | } |
544 | | }; |
545 | | |
546 | | PYBIND11_NAMESPACE_END(initimpl) |
547 | | PYBIND11_NAMESPACE_END(detail) |
548 | | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) |