diff options
Diffstat (limited to 'include/pybind11/detail/init.h')
-rw-r--r-- | include/pybind11/detail/init.h | 296 |
1 files changed, 197 insertions, 99 deletions
diff --git a/include/pybind11/detail/init.h b/include/pybind11/detail/init.h index 3ef78c11..e2117168 100644 --- a/include/pybind11/detail/init.h +++ b/include/pybind11/detail/init.h @@ -12,6 +12,9 @@ #include "class.h" PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +PYBIND11_WARNING_DISABLE_MSVC(4127) + PYBIND11_NAMESPACE_BEGIN(detail) template <> @@ -22,9 +25,10 @@ public: return true; } - template <typename> using cast_op_type = value_and_holder &; - operator value_and_holder &() { return *value; } - static constexpr auto name = _<value_and_holder>(); + template <typename> + using cast_op_type = value_and_holder &; + explicit operator value_and_holder &() { return *value; } + static constexpr auto name = const_name<value_and_holder>(); private: value_and_holder *value = nullptr; @@ -33,15 +37,21 @@ private: PYBIND11_NAMESPACE_BEGIN(initimpl) inline void no_nullptr(void *ptr) { - if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); + if (!ptr) { + throw type_error("pybind11::init(): factory function returned nullptr"); + } } // Implementing functions for all forms of py::init<...> and py::init(...) -template <typename Class> using Cpp = typename Class::type; -template <typename Class> using Alias = typename Class::type_alias; -template <typename Class> using Holder = typename Class::holder_type; +template <typename Class> +using Cpp = typename Class::type; +template <typename Class> +using Alias = typename Class::type_alias; +template <typename Class> +using Holder = typename Class::holder_type; -template <typename Class> using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>; +template <typename Class> +using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>; // Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. template <typename Class, enable_if_t<Class::has_alias, int> = 0> @@ -50,17 +60,27 @@ bool is_alias(Cpp<Class> *ptr) { } // Failing fallback version of the above for a no-alias class (always returns false) template <typename /*Class*/> -constexpr bool is_alias(void *) { return false; } +constexpr bool is_alias(void *) { + return false; +} // Constructs and returns a new object; if the given arguments don't map to a constructor, we fall // back to brace aggregate initiailization so that for aggregate initialization can be used with // py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For // non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually // works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor). -template <typename Class, typename... Args, detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0> -inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward<Args>(args)...); } -template <typename Class, typename... Args, detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0> -inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward<Args>(args)...}; } +template <typename Class, + typename... Args, + detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { + return new Class(std::forward<Args>(args)...); +} +template <typename Class, + typename... Args, + detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { + return new Class{std::forward<Args>(args)...}; +} // Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with // an alias to provide only a single Cpp factory function as long as the Alias can be @@ -69,12 +89,14 @@ inline Class *construct_or_initialize(Args &&...args) { return new Class{std::fo // inherit all the base class constructors. template <typename Class> void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, - value_and_holder &v_h, Cpp<Class> &&base) { + value_and_holder &v_h, + Cpp<Class> &&base) { v_h.value_ptr() = new Alias<Class>(std::move(base)); } template <typename Class> [[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, - value_and_holder &, Cpp<Class> &&) { + value_and_holder &, + Cpp<Class> &&) { throw type_error("pybind11::init(): unable to convert returned instance to required " "alias class: no `Alias<Class>(Class &&)` constructor available"); } @@ -84,8 +106,8 @@ template <typename Class> template <typename Class> void construct(...) { static_assert(!std::is_same<Class, Class>::value /* always false */, - "pybind11::init(): init function must return a compatible pointer, " - "holder, or value"); + "pybind11::init(): init function must return a compatible pointer, " + "holder, or value"); } // Pointer return v1: the factory function returns a class pointer for a registered class. @@ -94,6 +116,7 @@ void construct(...) { // construct an Alias from the returned base instance. template <typename Class> void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); no_nullptr(ptr); if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) { // We're going to try to construct an alias by moving the cpp type. Whether or not @@ -105,7 +128,7 @@ void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) { // the holder and destruction happens when we leave the C++ scope, and the holder // class gets to handle the destruction however it likes. v_h.value_ptr() = ptr; - v_h.set_instance_registered(true); // To prevent init_instance from registering it + v_h.set_instance_registered(true); // To prevent init_instance from registering it v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null @@ -128,15 +151,18 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) { // Holder return: copy its pointer, and move or copy the returned holder into the new instance's // holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a -// derived type (through those holder's implicit conversion from derived class holder constructors). +// derived type (through those holder's implicit conversion from derived class holder +// constructors). template <typename Class> void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); auto *ptr = holder_helper<Holder<Class>>::get(holder); no_nullptr(ptr); // If we need an alias, check that the held pointer is actually an alias instance - if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) + if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) { throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " "is not an alias instance"); + } v_h.value_ptr() = ptr; v_h.type->init_instance(v_h.inst, &holder); @@ -148,12 +174,14 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { // need it, we simply move-construct the cpp value into a new instance. template <typename Class> void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) { - static_assert(std::is_move_constructible<Cpp<Class>>::value, - "pybind11::init() return-by-value factory function requires a movable class"); - if (Class::has_alias && need_alias) + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); + static_assert(is_move_constructible<Cpp<Class>>::value, + "pybind11::init() return-by-value factory function requires a movable class"); + if (Class::has_alias && need_alias) { construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result)); - else + } else { v_h.value_ptr() = new Cpp<Class>(std::move(result)); + } } // return-by-value version 2: returning a value of the alias type itself. We move-construct an @@ -161,7 +189,8 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) { // cases where Alias initialization is always desired. template <typename Class> void construct(value_and_holder &v_h, Alias<Class> &&result, bool) { - static_assert(std::is_move_constructible<Alias<Class>>::value, + static_assert( + is_move_constructible<Alias<Class>>::value, "pybind11::init() return-by-alias-value factory function requires a movable alias class"); v_h.value_ptr() = new Alias<Class>(std::move(result)); } @@ -170,48 +199,79 @@ void construct(value_and_holder &v_h, Alias<Class> &&result, bool) { template <typename... Args> struct constructor { template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); - }, is_new_style_constructor(), extra...); + static void execute(Class &cl, const Extra &...extra) { + cl.def( + "__init__", + [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); + }, + is_new_style_constructor(), + extra...); } - template <typename Class, typename... Extra, - enable_if_t<Class::has_alias && - std::is_constructible<Cpp<Class>, Args...>::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - if (Py_TYPE(v_h.inst) == v_h.type->type) - v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); - else - v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); - }, is_new_style_constructor(), extra...); + template < + typename Class, + typename... Extra, + enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value, int> + = 0> + static void execute(Class &cl, const Extra &...extra) { + cl.def( + "__init__", + [](value_and_holder &v_h, Args... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) { + v_h.value_ptr() + = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); + } else { + v_h.value_ptr() + = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); + } + }, + is_new_style_constructor(), + extra...); } - template <typename Class, typename... Extra, - enable_if_t<Class::has_alias && - !std::is_constructible<Cpp<Class>, Args...>::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); - }, is_new_style_constructor(), extra...); + template < + typename Class, + typename... Extra, + enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value, int> + = 0> + static void execute(Class &cl, const Extra &...extra) { + cl.def( + "__init__", + [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() + = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); + }, + is_new_style_constructor(), + extra...); } }; // Implementing class for py::init_alias<...>() -template <typename... Args> struct alias_constructor { - template <typename Class, typename... Extra, - enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int> = 0> - static void execute(Class &cl, const Extra&... extra) { - cl.def("__init__", [](value_and_holder &v_h, Args... args) { - v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); - }, is_new_style_constructor(), extra...); +template <typename... Args> +struct alias_constructor { + template < + typename Class, + typename... Extra, + enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int> + = 0> + static void execute(Class &cl, const Extra &...extra) { + cl.def( + "__init__", + [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() + = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); + }, + is_new_style_constructor(), + extra...); } }; // Implementation class for py::init(Func) and py::init(Func, AliasFunc) -template <typename CFunc, typename AFunc = void_type (*)(), - typename = function_signature_t<CFunc>, typename = function_signature_t<AFunc>> +template <typename CFunc, + typename AFunc = void_type (*)(), + typename = function_signature_t<CFunc>, + typename = function_signature_t<AFunc>> struct factory; // Specialization for py::init(Func) @@ -219,7 +279,8 @@ template <typename Func, typename Return, typename... Args> struct factory<Func, void_type (*)(), Return(Args...)> { remove_reference_t<Func> class_factory; - factory(Func &&f) : class_factory(std::forward<Func>(f)) { } + // NOLINTNEXTLINE(google-explicit-constructor) + factory(Func &&f) : class_factory(std::forward<Func>(f)) {} // The given class either has no alias or has no separate alias factory; // this always constructs the class itself. If the class is registered with an alias @@ -228,22 +289,32 @@ struct factory<Func, void_type (*)(), Return(Args...)> { // instance, or the alias needs to be constructible from a `Class &&` argument. template <typename Class, typename... Extra> void execute(Class &cl, const Extra &...extra) && { - #if defined(PYBIND11_CPP14) - cl.def("__init__", [func = std::move(class_factory)] - #else +#if defined(PYBIND11_CPP14) + cl.def( + "__init__", + [func = std::move(class_factory)] +#else auto &func = class_factory; - cl.def("__init__", [func] - #endif - (value_and_holder &v_h, Args... args) { - construct<Class>(v_h, func(std::forward<Args>(args)...), - Py_TYPE(v_h.inst) != v_h.type->type); - }, is_new_style_constructor(), extra...); + cl.def( + "__init__", + [func] +#endif + (value_and_holder &v_h, Args... args) { + construct<Class>( + v_h, func(std::forward<Args>(args)...), Py_TYPE(v_h.inst) != v_h.type->type); + }, + is_new_style_constructor(), + extra...); } }; // Specialization for py::init(Func, AliasFunc) -template <typename CFunc, typename AFunc, - typename CReturn, typename... CArgs, typename AReturn, typename... AArgs> +template <typename CFunc, + typename AFunc, + typename CReturn, + typename... CArgs, + typename AReturn, + typename... AArgs> struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> { static_assert(sizeof...(CArgs) == sizeof...(AArgs), "pybind11::init(class_factory, alias_factory): class and alias factories " @@ -256,29 +327,37 @@ struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> { remove_reference_t<AFunc> alias_factory; factory(CFunc &&c, AFunc &&a) - : class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) { } + : class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) {} // The class factory is called when the `self` type passed to `__init__` is the direct // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. template <typename Class, typename... Extra> - void execute(Class &cl, const Extra&... extra) && { - static_assert(Class::has_alias, "The two-argument version of `py::init()` can " - "only be used if the class has an alias"); - #if defined(PYBIND11_CPP14) - cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] - #else + void execute(Class &cl, const Extra &...extra) && { + static_assert(Class::has_alias, + "The two-argument version of `py::init()` can " + "only be used if the class has an alias"); +#if defined(PYBIND11_CPP14) + cl.def( + "__init__", + [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] +#else auto &class_func = class_factory; auto &alias_func = alias_factory; - cl.def("__init__", [class_func, alias_func] - #endif - (value_and_holder &v_h, CArgs... args) { - if (Py_TYPE(v_h.inst) == v_h.type->type) - // If the instance type equals the registered type we don't have inheritance, so - // don't need the alias and can construct using the class function: - construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false); - else - construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true); - }, is_new_style_constructor(), extra...); + cl.def( + "__init__", + [class_func, alias_func] +#endif + (value_and_holder &v_h, CArgs... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) { + // If the instance type equals the registered type we don't have inheritance, + // so don't need the alias and can construct using the class function: + construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false); + } else { + construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true); + } + }, + is_new_style_constructor(), + extra...); } }; @@ -289,20 +368,34 @@ void setstate(value_and_holder &v_h, T &&result, bool need_alias) { } /// Set both the C++ and Python states -template <typename Class, typename T, typename O, +template <typename Class, + typename T, + typename O, enable_if_t<std::is_convertible<O, handle>::value, int> = 0> void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) { construct<Class>(v_h, std::move(result.first), need_alias); - setattr((PyObject *) v_h.inst, "__dict__", result.second); + auto d = handle(result.second); + if (PyDict_Check(d.ptr()) && PyDict_Size(d.ptr()) == 0) { + // Skipping setattr below, to not force use of py::dynamic_attr() for Class unnecessarily. + // See PR #2972 for details. + return; + } + setattr((PyObject *) v_h.inst, "__dict__", d); } /// Implementation for py::pickle(GetState, SetState) -template <typename Get, typename Set, - typename = function_signature_t<Get>, typename = function_signature_t<Set>> +template <typename Get, + typename Set, + typename = function_signature_t<Get>, + typename = function_signature_t<Set>> struct pickle_factory; -template <typename Get, typename Set, - typename RetState, typename Self, typename NewInstance, typename ArgState> +template <typename Get, + typename Set, + typename RetState, + typename Self, + typename NewInstance, + typename ArgState> struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> { static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value, "The type returned by `__getstate__` must be the same " @@ -311,26 +404,31 @@ struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> { remove_reference_t<Get> get; remove_reference_t<Set> set; - pickle_factory(Get get, Set set) - : get(std::forward<Get>(get)), set(std::forward<Set>(set)) { } + pickle_factory(Get get, Set set) : get(std::forward<Get>(get)), set(std::forward<Set>(set)) {} template <typename Class, typename... Extra> void execute(Class &cl, const Extra &...extra) && { cl.def("__getstate__", std::move(get)); #if defined(PYBIND11_CPP14) - cl.def("__setstate__", [func = std::move(set)] + cl.def( + "__setstate__", + [func = std::move(set)] #else auto &func = set; - cl.def("__setstate__", [func] + cl.def( + "__setstate__", + [func] #endif - (value_and_holder &v_h, ArgState state) { - setstate<Class>(v_h, func(std::forward<ArgState>(state)), - Py_TYPE(v_h.inst) != v_h.type->type); - }, is_new_style_constructor(), extra...); + (value_and_holder &v_h, ArgState state) { + setstate<Class>( + v_h, func(std::forward<ArgState>(state)), Py_TYPE(v_h.inst) != v_h.type->type); + }, + is_new_style_constructor(), + extra...); } }; PYBIND11_NAMESPACE_END(initimpl) PYBIND11_NAMESPACE_END(detail) -PYBIND11_NAMESPACE_END(pybind11) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) |