μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
init.h
Go to the documentation of this file.
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
15
17
19
20template <>
22public:
23 bool load(handle h, bool) {
24 value = reinterpret_cast<value_and_holder *>(h.ptr());
25 return true;
26 }
27
28 template <typename>
30 explicit operator value_and_holder &() { return *value; }
31 static constexpr auto name = const_name<value_and_holder>();
32
33private:
34 value_and_holder *value = nullptr;
35};
36
38
39inline void no_nullptr(void *ptr) {
40 if (!ptr) {
41 throw type_error("pybind11::init(): factory function returned nullptr");
42 }
43}
44
45// Implementing functions for all forms of py::init<...> and py::init(...)
46template <typename Class>
47using Cpp = typename Class::type;
48template <typename Class>
49using Alias = typename Class::type_alias;
50template <typename Class>
51using Holder = typename Class::holder_type;
52
53template <typename Class>
54using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>;
55
56// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance.
57template <typename Class, enable_if_t<Class::has_alias, int> = 0>
58bool is_alias(Cpp<Class> *ptr) {
59 return dynamic_cast<Alias<Class> *>(ptr) != nullptr;
60}
61// Failing fallback version of the above for a no-alias class (always returns false)
62template <typename /*Class*/>
63constexpr bool is_alias(void *) {
64 return false;
65}
66
67// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
68// back to brace aggregate initiailization so that for aggregate initialization can be used with
69// py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For
70// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
71// works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).
72template <typename Class,
73 typename... Args,
74 detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0>
75inline Class *construct_or_initialize(Args &&...args) {
76 return new Class(std::forward<Args>(args)...);
77}
78template <typename Class,
79 typename... Args,
80 detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0>
81inline Class *construct_or_initialize(Args &&...args) {
82 return new Class{std::forward<Args>(args)...};
83}
84
85// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with
86// an alias to provide only a single Cpp factory function as long as the Alias can be
87// constructed from an rvalue reference of the base Cpp type. This means that Alias classes
88// can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to
89// inherit all the base class constructors.
90template <typename Class>
91void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/,
93 Cpp<Class> &&base) {
94 v_h.value_ptr() = new Alias<Class>(std::move(base));
95}
96template <typename Class>
97[[noreturn]] void construct_alias_from_cpp(std::false_type ,
99 Cpp<Class> &&) {
100 throw type_error("pybind11::init(): unable to convert returned instance to required "
101 "alias class: no `Alias<Class>(Class &&)` constructor available");
102}
103
104// Error-generating fallback for factories that don't match one of the below construction
105// mechanisms.
106template <typename Class>
107void construct(...) {
108 static_assert(!std::is_same<Class, Class>::value /* always false */,
109 "pybind11::init(): init function must return a compatible pointer, "
110 "holder, or value");
111}
112
113// Pointer return v1: the factory function returns a class pointer for a registered class.
114// If we don't need an alias (because this class doesn't have one, or because the final type is
115// inherited on the Python side) we can simply take over ownership. Otherwise we need to try to
116// construct an Alias from the returned base instance.
117template <typename Class>
118void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
120 no_nullptr(ptr);
121 if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
122 // We're going to try to construct an alias by moving the cpp type. Whether or not
123 // that succeeds, we still need to destroy the original cpp pointer (either the
124 // moved away leftover, if the alias construction works, or the value itself if we
125 // throw an error), but we can't just call `delete ptr`: it might have a special
126 // deleter, or might be shared_from_this. So we construct a holder around it as if
127 // it was a normal instance, then steal the holder away into a local variable; thus
128 // the holder and destruction happens when we leave the C++ scope, and the holder
129 // class gets to handle the destruction however it likes.
130 v_h.value_ptr() = ptr;
131 v_h.set_instance_registered(true); // To prevent init_instance from registering it
132 v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder
133 Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder
134 v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null
135 v_h.set_instance_registered(false);
136
137 construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(*ptr));
138 } else {
139 // Otherwise the type isn't inherited, so we don't need an Alias
140 v_h.value_ptr() = ptr;
141 }
142}
143
144// Pointer return v2: a factory that always returns an alias instance ptr. We simply take over
145// ownership of the pointer.
146template <typename Class, enable_if_t<Class::has_alias, int> = 0>
147void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
148 no_nullptr(alias_ptr);
149 v_h.value_ptr() = static_cast<Cpp<Class> *>(alias_ptr);
150}
151
152// Holder return: copy its pointer, and move or copy the returned holder into the new instance's
153// holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a
154// derived type (through those holder's implicit conversion from derived class holder
155// constructors).
156template <typename Class>
157void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
159 auto *ptr = holder_helper<Holder<Class>>::get(holder);
160 no_nullptr(ptr);
161 // If we need an alias, check that the held pointer is actually an alias instance
162 if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
163 throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance "
164 "is not an alias instance");
165 }
166
167 v_h.value_ptr() = ptr;
168 v_h.type->init_instance(v_h.inst, &holder);
169}
170
171// return-by-value version 1: returning a cpp class by value. If the class has an alias and an
172// alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct
173// the alias from the base when needed (i.e. because of Python-side inheritance). When we don't
174// need it, we simply move-construct the cpp value into a new instance.
175template <typename Class>
176void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
178 static_assert(std::is_move_constructible<Cpp<Class>>::value,
179 "pybind11::init() return-by-value factory function requires a movable class");
180 if (Class::has_alias && need_alias) {
181 construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
182 } else {
183 v_h.value_ptr() = new Cpp<Class>(std::move(result));
184 }
185}
186
187// return-by-value version 2: returning a value of the alias type itself. We move-construct an
188// Alias instance (even if no the python-side inheritance is involved). The is intended for
189// cases where Alias initialization is always desired.
190template <typename Class>
191void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
192 static_assert(
193 std::is_move_constructible<Alias<Class>>::value,
194 "pybind11::init() return-by-alias-value factory function requires a movable alias class");
195 v_h.value_ptr() = new Alias<Class>(std::move(result));
196}
197
198// Implementing class for py::init<...>()
199template <typename... Args>
200struct constructor {
201 template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0>
202 static void execute(Class &cl, const Extra &...extra) {
203 cl.def(
204 "__init__",
205 [](value_and_holder &v_h, Args... args) {
206 v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
207 },
209 extra...);
210 }
211
212 template <
213 typename Class,
214 typename... Extra,
216 = 0>
217 static void execute(Class &cl, const Extra &...extra) {
218 cl.def(
219 "__init__",
220 [](value_and_holder &v_h, Args... args) {
221 if (Py_TYPE(v_h.inst) == v_h.type->type) {
222 v_h.value_ptr()
223 = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
224 } else {
225 v_h.value_ptr()
226 = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
227 }
228 },
230 extra...);
231 }
232
233 template <
234 typename Class,
235 typename... Extra,
237 = 0>
238 static void execute(Class &cl, const Extra &...extra) {
239 cl.def(
240 "__init__",
241 [](value_and_holder &v_h, Args... args) {
242 v_h.value_ptr()
243 = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
244 },
246 extra...);
247 }
248};
249
250// Implementing class for py::init_alias<...>()
251template <typename... Args>
252struct alias_constructor {
253 template <
254 typename Class,
255 typename... Extra,
257 = 0>
258 static void execute(Class &cl, const Extra &...extra) {
259 cl.def(
260 "__init__",
261 [](value_and_holder &v_h, Args... args) {
262 v_h.value_ptr()
263 = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
264 },
266 extra...);
267 }
268};
269
270// Implementation class for py::init(Func) and py::init(Func, AliasFunc)
271template <typename CFunc,
272 typename AFunc = void_type (*)(),
275struct factory;
276
277// Specialization for py::init(Func)
278template <typename Func, typename Return, typename... Args>
279struct factory<Func, void_type (*)(), Return(Args...)> {
280 remove_reference_t<Func> class_factory;
281
282 // NOLINTNEXTLINE(google-explicit-constructor)
283 factory(Func &&f) : class_factory(std::forward<Func>(f)) {}
284
285 // The given class either has no alias or has no separate alias factory;
286 // this always constructs the class itself. If the class is registered with an alias
287 // type and an alias instance is needed (i.e. because the final type is a Python class
288 // inheriting from the C++ type) the returned value needs to either already be an alias
289 // instance, or the alias needs to be constructible from a `Class &&` argument.
290 template <typename Class, typename... Extra>
291 void execute(Class &cl, const Extra &...extra) && {
292#if defined(PYBIND11_CPP14)
293 cl.def(
294 "__init__",
295 [func = std::move(class_factory)]
296#else
297 auto &func = class_factory;
298 cl.def(
299 "__init__",
300 [func]
301#endif
302 (value_and_holder &v_h, Args... args) {
303 construct<Class>(
304 v_h, func(std::forward<Args>(args)...), Py_TYPE(v_h.inst) != v_h.type->type);
305 },
307 extra...);
308 }
309};
310
311// Specialization for py::init(Func, AliasFunc)
312template <typename CFunc,
313 typename AFunc,
314 typename CReturn,
315 typename... CArgs,
316 typename AReturn,
317 typename... AArgs>
318struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
319 static_assert(sizeof...(CArgs) == sizeof...(AArgs),
320 "pybind11::init(class_factory, alias_factory): class and alias factories "
321 "must have identical argument signatures");
322 static_assert(all_of<std::is_same<CArgs, AArgs>...>::value,
323 "pybind11::init(class_factory, alias_factory): class and alias factories "
324 "must have identical argument signatures");
325
326 remove_reference_t<CFunc> class_factory;
327 remove_reference_t<AFunc> alias_factory;
328
329 factory(CFunc &&c, AFunc &&a)
330 : class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) {}
331
332 // The class factory is called when the `self` type passed to `__init__` is the direct
333 // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype.
334 template <typename Class, typename... Extra>
335 void execute(Class &cl, const Extra &...extra) && {
336 static_assert(Class::has_alias,
337 "The two-argument version of `py::init()` can "
338 "only be used if the class has an alias");
339#if defined(PYBIND11_CPP14)
340 cl.def(
341 "__init__",
342 [class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
343#else
344 auto &class_func = class_factory;
345 auto &alias_func = alias_factory;
346 cl.def(
347 "__init__",
348 [class_func, alias_func]
349#endif
350 (value_and_holder &v_h, CArgs... args) {
351 if (Py_TYPE(v_h.inst) == v_h.type->type) {
352 // If the instance type equals the registered type we don't have inheritance,
353 // so don't need the alias and can construct using the class function:
354 construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
355 } else {
356 construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
357 }
358 },
360 extra...);
361 }
362};
363
365template <typename Class, typename T>
366void setstate(value_and_holder &v_h, T &&result, bool need_alias) {
367 construct<Class>(v_h, std::forward<T>(result), need_alias);
368}
369
371template <typename Class,
372 typename T,
373 typename O,
375void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) {
376 construct<Class>(v_h, std::move(result.first), need_alias);
377 auto d = handle(result.second);
378 if (PyDict_Check(d.ptr()) && PyDict_Size(d.ptr()) == 0) {
379 // Skipping setattr below, to not force use of py::dynamic_attr() for Class unnecessarily.
380 // See PR #2972 for details.
381 return;
382 }
383 setattr((PyObject *) v_h.inst, "__dict__", d);
384}
385
387template <typename Get,
388 typename Set,
389 typename = function_signature_t<Get>,
390 typename = function_signature_t<Set>>
391struct pickle_factory;
392
393template <typename Get,
394 typename Set,
395 typename RetState,
396 typename Self,
397 typename NewInstance,
398 typename ArgState>
399struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
400 static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value,
401 "The type returned by `__getstate__` must be the same "
402 "as the argument accepted by `__setstate__`");
403
406
407 pickle_factory(Get get, Set set) : get(std::forward<Get>(get)), set(std::forward<Set>(set)) {}
408
409 template <typename Class, typename... Extra>
410 void execute(Class &cl, const Extra &...extra) && {
411 cl.def("__getstate__", std::move(get));
412
413#if defined(PYBIND11_CPP14)
414 cl.def(
415 "__setstate__",
416 [func = std::move(set)]
417#else
418 auto &func = set;
419 cl.def(
420 "__setstate__",
421 [func]
422#endif
423 (value_and_holder &v_h, ArgState state) {
424 setstate<Class>(
425 v_h, func(std::forward<ArgState>(state)), Py_TYPE(v_h.inst) != v_h.type->type);
426 },
428 extra...);
429 }
430};
431
Definition: pytypes.h:1776
\rst Holds a reference to a Python object (no reference counting)
Definition: pytypes.h:194
PyObject * ptr() const
Return the underlying PyObject * pointer.
Definition: pytypes.h:203
Definition: pytypes.h:1783
bool load(handle h, bool)
Definition: init.h:23
void setattr(handle obj, handle name, handle value)
Definition: pytypes.h:569
typename std::enable_if< B, T >::type enable_if_t
from cpp_future import (convenient aliases from C++14/17)
Definition: common.h:625
typename std::remove_reference< T >::type remove_reference_t
Definition: common.h:631
typename intrinsic_type< T >::type intrinsic_t
Definition: common.h:770
#define PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(...)
Definition: common.h:1187
#define PYBIND11_NAMESPACE_END(name)
Definition: common.h:21
conditional_t< std::is_function< F >::value, F, typename conditional_t< std::is_pointer< F >::value||std::is_member_pointer< F >::value, std::remove_pointer< F >, strip_function_object< F > >::type > function_signature_t
Definition: common.h:930
#define PYBIND11_NAMESPACE_BEGIN(name)
Definition: common.h:20
std::is_same< bools< Ts::value..., true >, bools< true, Ts::value... > > all_of
Definition: common.h:707
void setstate(value_and_holder &v_h, T &&result, bool need_alias)
Set just the C++ state. Same as __init__.
Definition: init.h:360
typename Class::holder_type Holder
Definition: init.h:48
typename Class::type_alias Alias
Definition: init.h:46
void construct(...)
Definition: init.h:104
void construct_alias_from_cpp(std::true_type, value_and_holder &v_h, Cpp< Class > &&base)
Definition: init.h:88
Class * construct_or_initialize(Args &&...args)
Definition: init.h:72
void no_nullptr(void *ptr)
Definition: init.h:36
bool is_alias(Cpp< Class > *ptr)
Definition: init.h:55
typename Class::type Cpp
Definition: init.h:44
std::is_constructible< Alias< Class >, Cpp< Class > && > is_alias_constructible
Definition: init.h:51
#define PYBIND11_WARNING_DISABLE_MSVC(name)
Definition: common.h:55
static void execute(Class &cl, const Extra &...extra)
Definition: init.h:258
Annotation indicating that a class derives from another given type.
Definition: attr.h:60
static void execute(Class &cl, const Extra &...extra)
Definition: init.h:202
void execute(Class &cl, const Extra &...extra) &&
Definition: init.h:335
void execute(Class &cl, const Extra &...extra) &&
Definition: init.h:291
Definition: init.h:269
Helper class which abstracts away certain actions.
Definition: cast.h:740
Tag for a new-style __init__ defined in detail/init.h
Definition: attr.h:364
Annotation for function names.
Definition: attr.h:47
void execute(Class &cl, const Extra &...extra) &&
Definition: init.h:410
Implementation for py::pickle(GetState, SetState)
Definition: init.h:385
const detail::type_info * type
V *& value_ptr() const
void set_instance_registered(bool v=true)
Helper type to replace 'void' in some expressions.
Definition: common.h:773