|
| 1 | +/* |
| 2 | + pybind11/factory.h: Helper class for binding C++ factory functions |
| 3 | + as Python constructors. |
| 4 | +
|
| 5 | + Copyright (c) 2017 Jason Rhinelander <[email protected]> |
| 6 | +
|
| 7 | + All rights reserved. Use of this source code is governed by a |
| 8 | + BSD-style license that can be found in the LICENSE file. |
| 9 | +*/ |
| 10 | + |
| 11 | +#pragma once |
| 12 | +#include "pybind11.h" |
| 13 | + |
| 14 | +#if defined(_MSC_VER) |
| 15 | +# pragma warning(push) |
| 16 | +# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter |
| 17 | +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant |
| 18 | +#endif |
| 19 | + |
| 20 | +NAMESPACE_BEGIN(pybind11) |
| 21 | + |
| 22 | +template <typename type, typename... options> |
| 23 | +template <typename... Args, typename... Extra> |
| 24 | +class_<type, options...> &class_<type, options...>::def(detail::init_factory<Args...> &&init, const Extra&... extra) { |
| 25 | + std::move(init).execute(*this, extra...); |
| 26 | + return *this; |
| 27 | +} |
| 28 | + |
| 29 | +NAMESPACE_BEGIN(detail) |
| 30 | + |
| 31 | +inline void init_factory_no_nullptr(void *ptr) { |
| 32 | + if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); |
| 33 | +} |
| 34 | + |
| 35 | +inline void init_factory_reset(instance *self) { |
| 36 | + // Reallocate the instance if there are any existing values |
| 37 | + bool need_reset = false; |
| 38 | + for (auto &v_h : values_and_holders(self)) |
| 39 | + if (v_h) { need_reset = true; break; } |
| 40 | + |
| 41 | + if (need_reset) { |
| 42 | + clear_instance((PyObject *) self); |
| 43 | + self->allocate_layout(); |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | + |
| 48 | +template <typename CFunc, typename CReturn, typename AFuncIn, typename AReturn, typename... Args> struct init_factory { |
| 49 | +private: |
| 50 | + using CFuncType = typename std::remove_reference<CFunc>::type; |
| 51 | + using AFunc = conditional_t<std::is_void<AFuncIn>::value, void_type, AFuncIn>; |
| 52 | + using AFuncType = typename std::remove_reference<AFunc>::type; |
| 53 | + |
| 54 | + template <typename Class> using Cpp = typename Class::type; |
| 55 | + template <typename Class> using Alias = typename Class::type_alias; |
| 56 | + template <typename Class> using Holder = typename Class::holder_type; |
| 57 | + |
| 58 | +public: |
| 59 | + // Constructor with a single function/lambda to call |
| 60 | + init_factory(CFunc &&f) : class_factory(std::forward<CFunc>(f)) {} |
| 61 | + |
| 62 | + // Constructor with two functions/lambdas, for a class with distinct class/alias factories: the |
| 63 | + // first is called when an alias is not needed, the second when the alias is needed. Requires |
| 64 | + // non-void AFunc. |
| 65 | + init_factory(CFunc &&c, AFunc &&a) : |
| 66 | + class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) {} |
| 67 | + |
| 68 | + // Add __init__ definition for a class that has no alias or has no separate alias factory |
| 69 | + template <typename Class, typename... Extra, |
| 70 | + enable_if_t<!Class::has_alias || std::is_void<AFuncIn>::value, int> = 0> |
| 71 | + void execute(Class &cl, const Extra&... extra) && { |
| 72 | + auto *cl_type = (PyTypeObject *) cl.ptr(); |
| 73 | + #if defined(PYBIND11_CPP14) |
| 74 | + cl.def("__init__", [cl_type, func = std::move(class_factory)] |
| 75 | + #else |
| 76 | + CFuncType func(std::move(class_factory)); |
| 77 | + cl.def("__init__", [cl_type, func] |
| 78 | + #endif |
| 79 | + (handle self, Args... args) { |
| 80 | + auto *inst = reinterpret_cast<instance *>(self.ptr()); |
| 81 | + construct<Class>(inst, func(std::forward<Args>(args)...), cl_type); |
| 82 | + }, extra...); |
| 83 | + } |
| 84 | + |
| 85 | + // Add __init__ definition for a class with an alias *and* distinct alias factory: |
| 86 | + template <typename Class, typename... Extra, |
| 87 | + enable_if_t<Class::has_alias && !std::is_void<AFuncIn>::value, int> = 0> |
| 88 | + void execute(Class &cl, const Extra&... extra) && { |
| 89 | + auto *cl_type = (PyTypeObject *) cl.ptr(); |
| 90 | + #if defined(PYBIND11_CPP14) |
| 91 | + cl.def("__init__", [cl_type, class_func = std::move(class_factory), alias_func = std::move(alias_factory)] |
| 92 | + #else |
| 93 | + CFuncType class_func(std::move(class_factory)); |
| 94 | + AFuncType alias_func(std::move(alias_factory)); |
| 95 | + cl.def("__init__", [cl_type, class_func, alias_func] |
| 96 | + #endif |
| 97 | + (handle self, Args... args) { |
| 98 | + auto *inst = reinterpret_cast<instance *>(self.ptr()); |
| 99 | + if (Py_TYPE(inst) == cl_type) |
| 100 | + construct<Class>(inst, class_func(std::forward<Args>(args)...), cl_type); |
| 101 | + else |
| 102 | + construct<Class>(inst, alias_func(std::forward<Args>(args)...), cl_type); |
| 103 | + }, extra...); |
| 104 | + } |
| 105 | + |
| 106 | +private: |
| 107 | + // Pointer assignment implementation: |
| 108 | + template <typename Class> |
| 109 | + static void construct_impl(instance *self, Cpp<Class> *ptr) { |
| 110 | + init_factory_reset(self); |
| 111 | + auto v_h = self->get_value_and_holder(get_type_info(Py_TYPE(self))); |
| 112 | + v_h.value_ptr() = ptr; |
| 113 | + } |
| 114 | + |
| 115 | + // Takes a cpp class pointer and returns true if it actually a polymorphic alias instance. |
| 116 | + template <typename Class, enable_if_t<Class::has_alias, int> = 0> |
| 117 | + static bool is_alias(Cpp<Class> *ptr) { |
| 118 | + return dynamic_cast<Alias<Class> *>(ptr) != nullptr; |
| 119 | + } |
| 120 | + // Failing fallback version for a no-alias class (always returns false) |
| 121 | + template <typename Class> |
| 122 | + constexpr static bool is_alias(void *) { return false; } |
| 123 | + |
| 124 | + // Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. Returns true if the |
| 125 | + // constructor existed, false is the alias cannot be constructed that way. |
| 126 | + template <typename Class, enable_if_t<std::is_constructible<Alias<Class>, Cpp<Class> &&>::value, int> = 0> |
| 127 | + static bool construct_alias_from_cpp(instance *self, Cpp<Class> &&base) { |
| 128 | + new (self->get_value_and_holder().value_ptr()) Alias<Class>(std::move(base)); |
| 129 | + return true; |
| 130 | + } |
| 131 | + // Base version for aliases without an appropriate constructor; does nothing and returns false. |
| 132 | + template <typename Class, enable_if_t<!std::is_constructible<Alias<Class>, Cpp<Class> &&>::value, int> = 0> |
| 133 | + static bool construct_alias_from_cpp(instance *, Cpp<Class> &&) { return false; } |
| 134 | + |
| 135 | + // Error-generating fallback |
| 136 | + template <typename Class> |
| 137 | + static void construct(...) { |
| 138 | + static_assert(!std::is_same<Class, Class>::value /* always false */, |
| 139 | + "pybind11::init(): wrapped factory function must return a compatible pointer, " |
| 140 | + "holder, or value"); |
| 141 | + } |
| 142 | + |
| 143 | + // Pointer return v1: a factory function that returns a class pointer for a registered class |
| 144 | + // without an alias |
| 145 | + template <typename Class, enable_if_t<!Class::has_alias, int> = 0> |
| 146 | + static void construct(instance *self, Cpp<Class> *ptr, PyTypeObject *) { |
| 147 | + init_factory_no_nullptr(ptr); |
| 148 | + construct_impl<Class>(self, ptr); |
| 149 | + } |
| 150 | + |
| 151 | + // Pointer return v2: a factory that always returns an alias instance ptr (like py::init_alias) |
| 152 | + template <typename Class, enable_if_t<Class::has_alias, int> = 0> |
| 153 | + static void construct(instance *self, Alias<Class> *alias_ptr, PyTypeObject *) { |
| 154 | + construct_impl<Class>(self, static_cast<Cpp<Class> *>(alias_ptr)); |
| 155 | + } |
| 156 | + |
| 157 | + // Pointer return v3: returning a cpp class for a registered class with an alias. If we don't |
| 158 | + // need an alias, we simply use the cpp pointer. Otherwise, we try a dynamic_cast to see if the |
| 159 | + // cpp pointer is actually a polymorphic alias instance. If it isn't, but we can construct the |
| 160 | + // alias via an `Alias(Cpp &&)` constructor, we do. Otherwise we throw a type error. |
| 161 | + template <typename Class, enable_if_t<Class::has_alias, int> = 0> |
| 162 | + static void construct(instance *self, Cpp<Class> *ptr, PyTypeObject *cl_type) { |
| 163 | + init_factory_no_nullptr(ptr); |
| 164 | + if (Py_TYPE(self) != cl_type) { |
| 165 | + // Inherited from on the Python side: an alias instance is required |
| 166 | + if (!is_alias<Class>(ptr)) { |
| 167 | + if (construct_alias_from_cpp<Class>(self, std::move(*ptr))) { |
| 168 | + delete ptr; |
| 169 | + return; |
| 170 | + } |
| 171 | + |
| 172 | + delete ptr; |
| 173 | + throw type_error("pybind11::init(): factory function pointer could not be cast or " |
| 174 | + "converted to an alias instance"); |
| 175 | + } |
| 176 | + } |
| 177 | + construct_impl<Class>(self, ptr); |
| 178 | + } |
| 179 | + |
| 180 | + // Holder return: copy its pointer, and move or copy the returned holder into the new instance's |
| 181 | + // holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a |
| 182 | + // derived type (through those holder's implicit conversion from derived class holder constructors). |
| 183 | + template <typename Class> |
| 184 | + static void construct(instance *self, Holder<Class> holder, PyTypeObject *cl_type) { |
| 185 | + auto *ptr = holder_helper<Holder<Class>>::get(holder); |
| 186 | + // If we need an alias, check that the held pointer is actually an alias instance |
| 187 | + if (Class::has_alias && Py_TYPE(self) != cl_type && !is_alias<Class>(ptr)) |
| 188 | + throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " |
| 189 | + "is not an alias instance"); |
| 190 | + |
| 191 | + construct_impl<Class>(self, ptr); |
| 192 | + Class::init_instance(self, &holder); |
| 193 | + } |
| 194 | + |
| 195 | + // return-by-value version 1: returning a cpp class by value when there is no alias |
| 196 | + template <typename Class, enable_if_t<!Class::has_alias, int> = 0> |
| 197 | + static void construct(instance *self, Cpp<Class> &&result, PyTypeObject *) { |
| 198 | + static_assert(std::is_move_constructible<Cpp<Class>>::value, |
| 199 | + "pybind11::init() return-by-value factory function requires a movable class"); |
| 200 | + new (self->get_value_and_holder().value_ptr()) Cpp<Class>(std::move(result)); |
| 201 | + } |
| 202 | + |
| 203 | + // return-by-value version 2: returning a cpp class by value when there is an alias: the alias |
| 204 | + // must have an `Alias(Base &&)` constructor so that we can construct the alias from the base |
| 205 | + // when needed. |
| 206 | + template <typename Class, enable_if_t<Class::has_alias, int> = 0> |
| 207 | + static void construct(instance *self, Cpp<Class> &&result, PyTypeObject *cl_type) { |
| 208 | + static_assert(std::is_move_constructible<Cpp<Class>>::value, |
| 209 | + "pybind11::init() return-by-value factory function requires a movable class"); |
| 210 | + if (Py_TYPE(self) != cl_type) { |
| 211 | + if (!construct_alias_from_cpp<Class>(self, std::move(result))) |
| 212 | + throw type_error("pybind11::init(): unable to convert returned instance to " |
| 213 | + "required alias class: no `Alias(Class &&)` constructor available"); |
| 214 | + } |
| 215 | + else |
| 216 | + new (self->get_value_and_holder().value_ptr()) Cpp<Class>(std::move(result)); |
| 217 | + } |
| 218 | + |
| 219 | + // return-by-value version 3: the alias type itself--always initialize via the alias type (this |
| 220 | + // is the factory equivalent of py::init_alias<...>()). |
| 221 | + template <typename Class> |
| 222 | + static void construct(instance *self, Alias<Class> &&result, PyTypeObject *) { |
| 223 | + static_assert(std::is_move_constructible<Alias<Class>>::value, |
| 224 | + "pybind11::init() return-by-alias-value factory function requires a movable alias class"); |
| 225 | + new (self->get_value_and_holder().value_ptr()) Alias<Class>(std::move(result)); |
| 226 | + } |
| 227 | + |
| 228 | + CFuncType class_factory; |
| 229 | + AFuncType alias_factory; |
| 230 | +}; |
| 231 | + |
| 232 | +template <typename Func> using init_factory_functype = |
| 233 | + conditional_t<std::is_function<remove_reference_t<Func>>::value, remove_reference_t<Func> *, |
| 234 | + conditional_t<is_function_pointer<remove_reference_t<Func>>::value, remove_reference_t<Func>, |
| 235 | + Func>>; |
| 236 | + |
| 237 | +// Helper definition to infer the detail::init_factory template type from a callable object |
| 238 | +template <typename Func, typename Return, typename... Args> |
| 239 | +init_factory<init_factory_functype<Func>, Return, void, void, Args...> init_factory_decltype(Return (*)(Args...)); |
| 240 | + |
| 241 | +template <typename Return1, typename Return2, typename... Args1, typename... Args2> |
| 242 | +inline constexpr bool init_factory_require_matching_arguments(Return1 (*)(Args1...), Return2 (*)(Args2...)) { |
| 243 | + static_assert(sizeof...(Args1) == sizeof...(Args2), |
| 244 | + "pybind11::init(class_factory, alias_factory): class and alias factories must have identical argument signatures"); |
| 245 | + static_assert(all_of<std::is_same<Args1, Args2>...>::value, |
| 246 | + "pybind11::init(class_factory, alias_factory): class and alias factories must have identical argument signatures"); |
| 247 | + return true; |
| 248 | +} |
| 249 | + |
| 250 | +template <typename CFunc, typename AFunc, |
| 251 | + typename CReturn, typename... CArgs, typename AReturn, typename... AArgs, |
| 252 | + bool = init_factory_require_matching_arguments((CReturn (*)(CArgs...)) nullptr, (AReturn (*)(AArgs...)) nullptr)> |
| 253 | +init_factory<init_factory_functype<CFunc>, CReturn, init_factory_functype<AFunc>, AReturn, CArgs...> init_factory_decltype( |
| 254 | + CReturn (*)(CArgs...), AReturn (*)(AArgs...)); |
| 255 | + |
| 256 | +template <typename... Func> using init_factory_t = decltype(init_factory_decltype<Func...>( |
| 257 | + (function_signature_t<Func> *) nullptr...)); |
| 258 | + |
| 259 | +NAMESPACE_END(detail) |
| 260 | + |
| 261 | +/// Single-argument factory function constructor wrapper |
| 262 | +template <typename Func, typename Ret = detail::init_factory_t<Func>> |
| 263 | +Ret init(Func &&f) { return {std::forward<Func>(f)}; } |
| 264 | + |
| 265 | +/// Dual-argument factory function: the first function is called when no alias is needed, the second |
| 266 | +/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. |
| 267 | +template <typename CFunc, typename AFunc, typename Ret = detail::init_factory_t<CFunc, AFunc>> |
| 268 | +Ret init(CFunc &&c, AFunc &&a) { |
| 269 | + return {std::forward<CFunc>(c), std::forward<AFunc>(a)}; |
| 270 | +} |
| 271 | + |
| 272 | +NAMESPACE_END(pybind11) |
| 273 | + |
| 274 | +#if defined(_MSC_VER) |
| 275 | +# pragma warning(pop) |
| 276 | +#endif |
0 commit comments