cppyabm  1.0.17
An agent-based library to integrate C++ and Python
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 
16 
17 template <>
19 public:
20  bool load(handle h, bool) {
21  value = reinterpret_cast<value_and_holder *>(h.ptr());
22  return true;
23  }
24 
25  template <typename> using cast_op_type = value_and_holder &;
26  operator value_and_holder &() { return *value; }
27  static constexpr auto name = _<value_and_holder>();
28 
29 private:
30  value_and_holder *value = nullptr;
31 };
32 
34 
35 inline void no_nullptr(void *ptr) {
36  if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr");
37 }
38 
39 // Implementing functions for all forms of py::init<...> and py::init(...)
40 template <typename Class> using Cpp = typename Class::type;
41 template <typename Class> using Alias = typename Class::type_alias;
42 template <typename Class> using Holder = typename Class::holder_type;
43 
44 template <typename Class> using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>;
45 
46 // Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance.
47 template <typename Class, enable_if_t<Class::has_alias, int> = 0>
48 bool is_alias(Cpp<Class> *ptr) {
49  return dynamic_cast<Alias<Class> *>(ptr) != nullptr;
50 }
51 // Failing fallback version of the above for a no-alias class (always returns false)
52 template <typename /*Class*/>
53 constexpr bool is_alias(void *) { return false; }
54 
55 // Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
56 // back to brace aggregate initiailization so that for aggregate initialization can be used with
57 // py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For
58 // non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
59 // works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).
60 template <typename Class, typename... Args, detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0>
61 inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward<Args>(args)...); }
62 template <typename Class, typename... Args, detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0>
63 inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward<Args>(args)...}; }
64 
65 // Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with
66 // an alias to provide only a single Cpp factory function as long as the Alias can be
67 // constructed from an rvalue reference of the base Cpp type. This means that Alias classes
68 // can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to
69 // inherit all the base class constructors.
70 template <typename Class>
71 void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/,
73  v_h.value_ptr() = new Alias<Class>(std::move(base));
74 }
75 template <typename Class>
76 [[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/,
78  throw type_error("pybind11::init(): unable to convert returned instance to required "
79  "alias class: no `Alias<Class>(Class &&)` constructor available");
80 }
81 
82 // Error-generating fallback for factories that don't match one of the below construction
83 // mechanisms.
84 template <typename Class>
85 void construct(...) {
86  static_assert(!std::is_same<Class, Class>::value /* always false */,
87  "pybind11::init(): init function must return a compatible pointer, "
88  "holder, or value");
89 }
90 
91 // Pointer return v1: the factory function returns a class pointer for a registered class.
92 // If we don't need an alias (because this class doesn't have one, or because the final type is
93 // inherited on the Python side) we can simply take over ownership. Otherwise we need to try to
94 // construct an Alias from the returned base instance.
95 template <typename Class>
96 void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
97  no_nullptr(ptr);
98  if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
99  // We're going to try to construct an alias by moving the cpp type. Whether or not
100  // that succeeds, we still need to destroy the original cpp pointer (either the
101  // moved away leftover, if the alias construction works, or the value itself if we
102  // throw an error), but we can't just call `delete ptr`: it might have a special
103  // deleter, or might be shared_from_this. So we construct a holder around it as if
104  // it was a normal instance, then steal the holder away into a local variable; thus
105  // the holder and destruction happens when we leave the C++ scope, and the holder
106  // class gets to handle the destruction however it likes.
107  v_h.value_ptr() = ptr;
108  v_h.set_instance_registered(true); // To prevent init_instance from registering it
109  v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder
110  Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder
111  v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null
112  v_h.set_instance_registered(false);
113 
114  construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(*ptr));
115  } else {
116  // Otherwise the type isn't inherited, so we don't need an Alias
117  v_h.value_ptr() = ptr;
118  }
119 }
120 
121 // Pointer return v2: a factory that always returns an alias instance ptr. We simply take over
122 // ownership of the pointer.
123 template <typename Class, enable_if_t<Class::has_alias, int> = 0>
124 void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
125  no_nullptr(alias_ptr);
126  v_h.value_ptr() = static_cast<Cpp<Class> *>(alias_ptr);
127 }
128 
129 // Holder return: copy its pointer, and move or copy the returned holder into the new instance's
130 // holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a
131 // derived type (through those holder's implicit conversion from derived class holder constructors).
132 template <typename Class>
133 void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
134  auto *ptr = holder_helper<Holder<Class>>::get(holder);
135  no_nullptr(ptr);
136  // If we need an alias, check that the held pointer is actually an alias instance
137  if (Class::has_alias && need_alias && !is_alias<Class>(ptr))
138  throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance "
139  "is not an alias instance");
140 
141  v_h.value_ptr() = ptr;
142  v_h.type->init_instance(v_h.inst, &holder);
143 }
144 
145 // return-by-value version 1: returning a cpp class by value. If the class has an alias and an
146 // alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct
147 // the alias from the base when needed (i.e. because of Python-side inheritance). When we don't
148 // need it, we simply move-construct the cpp value into a new instance.
149 template <typename Class>
150 void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
151  static_assert(std::is_move_constructible<Cpp<Class>>::value,
152  "pybind11::init() return-by-value factory function requires a movable class");
153  if (Class::has_alias && need_alias)
154  construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
155  else
156  v_h.value_ptr() = new Cpp<Class>(std::move(result));
157 }
158 
159 // return-by-value version 2: returning a value of the alias type itself. We move-construct an
160 // Alias instance (even if no the python-side inheritance is involved). The is intended for
161 // cases where Alias initialization is always desired.
162 template <typename Class>
163 void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
164  static_assert(std::is_move_constructible<Alias<Class>>::value,
165  "pybind11::init() return-by-alias-value factory function requires a movable alias class");
166  v_h.value_ptr() = new Alias<Class>(std::move(result));
167 }
168 
169 // Implementing class for py::init<...>()
170 template <typename... Args>
171 struct constructor {
172  template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0>
173  static void execute(Class &cl, const Extra&... extra) {
174  cl.def("__init__", [](value_and_holder &v_h, Args... args) {
175  v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
176  }, is_new_style_constructor(), extra...);
177  }
178 
179  template <typename Class, typename... Extra,
180  enable_if_t<Class::has_alias &&
181  std::is_constructible<Cpp<Class>, Args...>::value, int> = 0>
182  static void execute(Class &cl, const Extra&... extra) {
183  cl.def("__init__", [](value_and_holder &v_h, Args... args) {
184  if (Py_TYPE(v_h.inst) == v_h.type->type)
185  v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
186  else
187  v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
188  }, is_new_style_constructor(), extra...);
189  }
190 
191  template <typename Class, typename... Extra,
192  enable_if_t<Class::has_alias &&
193  !std::is_constructible<Cpp<Class>, Args...>::value, int> = 0>
194  static void execute(Class &cl, const Extra&... extra) {
195  cl.def("__init__", [](value_and_holder &v_h, Args... args) {
196  v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
197  }, is_new_style_constructor(), extra...);
198  }
199 };
200 
201 // Implementing class for py::init_alias<...>()
202 template <typename... Args> struct alias_constructor {
203  template <typename Class, typename... Extra,
205  static void execute(Class &cl, const Extra&... extra) {
206  cl.def("__init__", [](value_and_holder &v_h, Args... args) {
207  v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
208  }, is_new_style_constructor(), extra...);
209  }
210 };
211 
212 // Implementation class for py::init(Func) and py::init(Func, AliasFunc)
213 template <typename CFunc, typename AFunc = void_type (*)(),
215 struct factory;
216 
217 // Specialization for py::init(Func)
218 template <typename Func, typename Return, typename... Args>
219 struct factory<Func, void_type (*)(), Return(Args...)> {
221 
222  factory(Func &&f) : class_factory(std::forward<Func>(f)) { }
223 
224  // The given class either has no alias or has no separate alias factory;
225  // this always constructs the class itself. If the class is registered with an alias
226  // type and an alias instance is needed (i.e. because the final type is a Python class
227  // inheriting from the C++ type) the returned value needs to either already be an alias
228  // instance, or the alias needs to be constructible from a `Class &&` argument.
229  template <typename Class, typename... Extra>
230  void execute(Class &cl, const Extra &...extra) && {
231  #if defined(PYBIND11_CPP14)
232  cl.def("__init__", [func = std::move(class_factory)]
233  #else
234  auto &func = class_factory;
235  cl.def("__init__", [func]
236  #endif
237  (value_and_holder &v_h, Args... args) {
238  construct<Class>(v_h, func(std::forward<Args>(args)...),
239  Py_TYPE(v_h.inst) != v_h.type->type);
240  }, is_new_style_constructor(), extra...);
241  }
242 };
243 
244 // Specialization for py::init(Func, AliasFunc)
245 template <typename CFunc, typename AFunc,
246  typename CReturn, typename... CArgs, typename AReturn, typename... AArgs>
247 struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
248  static_assert(sizeof...(CArgs) == sizeof...(AArgs),
249  "pybind11::init(class_factory, alias_factory): class and alias factories "
250  "must have identical argument signatures");
251  static_assert(all_of<std::is_same<CArgs, AArgs>...>::value,
252  "pybind11::init(class_factory, alias_factory): class and alias factories "
253  "must have identical argument signatures");
254 
257 
258  factory(CFunc &&c, AFunc &&a)
259  : class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) { }
260 
261  // The class factory is called when the `self` type passed to `__init__` is the direct
262  // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype.
263  template <typename Class, typename... Extra>
264  void execute(Class &cl, const Extra&... extra) && {
265  static_assert(Class::has_alias, "The two-argument version of `py::init()` can "
266  "only be used if the class has an alias");
267  #if defined(PYBIND11_CPP14)
268  cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
269  #else
270  auto &class_func = class_factory;
271  auto &alias_func = alias_factory;
272  cl.def("__init__", [class_func, alias_func]
273  #endif
274  (value_and_holder &v_h, CArgs... args) {
275  if (Py_TYPE(v_h.inst) == v_h.type->type)
276  // If the instance type equals the registered type we don't have inheritance, so
277  // don't need the alias and can construct using the class function:
278  construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
279  else
280  construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
281  }, is_new_style_constructor(), extra...);
282  }
283 };
284 
285 /// Set just the C++ state. Same as `__init__`.
286 template <typename Class, typename T>
287 void setstate(value_and_holder &v_h, T &&result, bool need_alias) {
288  construct<Class>(v_h, std::forward<T>(result), need_alias);
289 }
290 
291 /// Set both the C++ and Python states
292 template <typename Class, typename T, typename O,
294 void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) {
295  construct<Class>(v_h, std::move(result.first), need_alias);
296  setattr((PyObject *) v_h.inst, "__dict__", result.second);
297 }
298 
299 /// Implementation for py::pickle(GetState, SetState)
300 template <typename Get, typename Set,
303 
304 template <typename Get, typename Set,
305  typename RetState, typename Self, typename NewInstance, typename ArgState>
306 struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
307  static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value,
308  "The type returned by `__getstate__` must be the same "
309  "as the argument accepted by `__setstate__`");
310 
313 
314  pickle_factory(Get get, Set set)
315  : get(std::forward<Get>(get)), set(std::forward<Set>(set)) { }
316 
317  template <typename Class, typename... Extra>
318  void execute(Class &cl, const Extra &...extra) && {
319  cl.def("__getstate__", std::move(get));
320 
321 #if defined(PYBIND11_CPP14)
322  cl.def("__setstate__", [func = std::move(set)]
323 #else
324  auto &func = set;
325  cl.def("__setstate__", [func]
326 #endif
327  (value_and_holder &v_h, ArgState state) {
328  setstate<Class>(v_h, func(std::forward<ArgState>(state)),
329  Py_TYPE(v_h.inst) != v_h.type->type);
330  }, is_new_style_constructor(), extra...);
331  }
332 };
333 
334 PYBIND11_NAMESPACE_END(initimpl)
enable_if_t
typename std::enable_if< B, T >::type enable_if_t
from cpp_future import (convenient aliases from C++14/17)
Definition: common.h:504
Alias
typename Class::type_alias Alias
Definition: init.h:41
name
Annotation for function names.
Definition: attr.h:36
setattr
void setattr(handle obj, handle name, handle value)
Definition: pytypes.h:451
PYBIND11_NAMESPACE_BEGIN
#define PYBIND11_NAMESPACE_BEGIN(name)
Definition: common.h:16
base
Annotation indicating that a class derives from another given type.
Definition: attr.h:42
pickle_factory< Get, Set, RetState(Self), NewInstance(ArgState)>::set
remove_reference_t< Set > set
Definition: init.h:312
PYBIND11_NAMESPACE_END
#define PYBIND11_NAMESPACE_END(name)
Definition: common.h:17
set
Definition: pytypes.h:1369
class.h
PYBIND11_NAMESPACE
#define PYBIND11_NAMESPACE
Definition: common.h:26
setstate
void setstate(value_and_holder &v_h, T &&result, bool need_alias)
Set just the C++ state. Same as __init__.
Definition: init.h:287
type_caster_generic::value
void * value
Definition: cast.h:767
pickle_factory< Get, Set, RetState(Self), NewInstance(ArgState)>::get
remove_reference_t< Get > get
Definition: init.h:309
factory
Definition: init.h:215
factory< CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)>::execute
void execute(Class &cl, const Extra &... extra) &&
Definition: init.h:264
all_of
std::is_same< bools< Ts::value..., true >, bools< true, Ts::value... > > all_of
Definition: common.h:550
value_and_holder
Definition: cast.h:223
constructor::execute
static void execute(Class &cl, const Extra &... extra)
Definition: init.h:173
factory< CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)>::class_factory
remove_reference_t< CFunc > class_factory
Definition: init.h:250
pickle_factory
Implementation for py::pickle(GetState, SetState)
Definition: init.h:302
remove_reference_t
typename std::remove_reference< T >::type remove_reference_t
Definition: common.h:507
construct_alias_from_cpp
void construct_alias_from_cpp(std::true_type, value_and_holder &v_h, Cpp< Class > &&base)
Definition: init.h:71
pickle_factory< Get, Set, RetState(Self), NewInstance(ArgState)>::execute
void execute(Class &cl, const Extra &...extra) &&
Definition: init.h:318
value_and_holder::set_instance_registered
void set_instance_registered(bool v=true)
Definition: cast.h:268
alias_constructor
Definition: init.h:202
handle
Definition: pytypes.h:176
type_caster
Definition: cast.h:954
value_and_holder::inst
instance * inst
Definition: cast.h:224
is_new_style_constructor
Tag for a new-style __init__ defined in detail/init.h
Definition: attr.h:315
Cpp
typename Class::type Cpp
Definition: init.h:40
is_alias_constructible
std::is_constructible< Alias< Class >, Cpp< Class > && > is_alias_constructible
Definition: init.h:44
intrinsic_t
typename intrinsic_type< T >::type intrinsic_t
Definition: common.h:577
is_alias
bool is_alias(Cpp< Class > *ptr)
Definition: init.h:48
factory< CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)>::factory
factory(CFunc &&c, AFunc &&a)
Definition: init.h:258
holder_helper
Definition: cast.h:1523
pickle_factory< Get, Set, RetState(Self), NewInstance(ArgState)>::pickle_factory
pickle_factory(Get get, Set set)
Definition: init.h:314
value_and_holder::value_ptr
V *& value_ptr() const
Definition: cast.h:241
move
detail::enable_if_t<!detail::move_never< T >::value, T > move(object &&obj)
Definition: cast.h:1798
handle::ptr
PyObject * ptr() const
Return the underlying PyObject * pointer.
Definition: pytypes.h:184
Holder
typename Class::holder_type Holder
Definition: init.h:42
constructor
Definition: init.h:171
construct
void construct(...)
Definition: init.h:85
void_type
Helper type to replace 'void' in some expressions.
Definition: common.h:580
value_and_holder::holder
H & holder() const
Definition: cast.h:247
args
Definition: pytypes.h:1366
factory< Func, void_type(*)(), Return(Args...)>::execute
void execute(Class &cl, const Extra &...extra) &&
Definition: init.h:230
pybind11
Definition: __init__.py:1
factory< CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)>::alias_factory
remove_reference_t< AFunc > alias_factory
Definition: init.h:256
no_nullptr
void no_nullptr(void *ptr)
Definition: init.h:35
factory< Func, void_type(*)(), Return(Args...)>::factory
factory(Func &&f)
Definition: init.h:222
construct_or_initialize
Class * construct_or_initialize(Args &&...args)
Definition: init.h:61
factory< Func, void_type(*)(), Return(Args...)>::class_factory
remove_reference_t< Func > class_factory
Definition: init.h:220
value_and_holder::type
const detail::type_info * type
Definition: cast.h:226
test_callbacks.value
value
Definition: test_callbacks.py:126
type_caster< value_and_holder >::load
bool load(handle h, bool)
Definition: init.h:20
function_signature_t
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:703
alias_constructor::execute
static void execute(Class &cl, const Extra &... extra)
Definition: init.h:205
make_changelog.state
state
Definition: make_changelog.py:30