diff --git a/include/pybind11/detail/init.h b/include/pybind11/detail/init.h index 430f4bf5..ee2de8c2 100644 --- a/include/pybind11/detail/init.h +++ b/include/pybind11/detail/init.h @@ -199,39 +199,28 @@ template struct alias_constructor { }; // Implementation class for py::init(Func) and py::init(Func, AliasFunc) -template -struct factory { -private: - using CFuncType = typename std::remove_reference::type; - using AFunc = conditional_t::value, void_type, AFuncIn>; - using AFuncType = typename std::remove_reference::type; +template , typename = function_signature_t> +struct factory; - CFuncType class_factory; - AFuncType alias_factory; +// Specialization for py::init(Func) +template +struct factory { + remove_reference_t class_factory; -public: - // Constructor with a single function/lambda to call; for classes without aliases or with - // aliases that can be move constructed from the base. - factory(CFunc &&f) : class_factory(std::forward(f)) {} + factory(Func &&f) : class_factory(std::forward(f)) { } - // Constructor with two functions/lambdas, for a class with distinct class/alias factories: the - // first is called when an alias is not needed, the second when the alias is needed. Requires - // non-void AFunc. - factory(CFunc &&c, AFunc &&a) : - class_factory(std::forward(c)), alias_factory(std::forward(a)) {} - - // Add __init__ definition for a class that either has no alias or has no separate alias - // factory; this always constructs the class itself. If the class is registered with an alias + // 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 // type and an alias instance is needed (i.e. because the final type is a Python class // inheriting from the C++ type) the returned value needs to either already be an alias // instance, or the alias needs to be constructible from a `Class &&` argument. - template ::value, int> = 0> - void execute(Class &cl, const Extra&... extra) && { + template + void execute(Class &cl, const Extra &...extra) && { #if defined(PYBIND11_CPP14) cl.def("__init__", [func = std::move(class_factory)] #else - CFuncType &func = class_factory; + auto &func = class_factory; cl.def("__init__", [func] #endif (value_and_holder &v_h, Args... args) { @@ -239,64 +228,49 @@ public: Py_TYPE(v_h.inst) != v_h.type->type); }, is_new_style_constructor(), extra...); } +}; - // Add __init__ definition for a class with an alias *and* distinct alias factory; the former is - // called when the `self` type passed to `__init__` is the direct class (i.e. not inherited), the latter - // when `self` is a Python-side subtype. - template ::value, int> = 0> +// Specialization for py::init(Func, AliasFunc) +template +struct factory { + static_assert(sizeof...(CArgs) == sizeof...(AArgs), + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + static_assert(all_of...>::value, + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + + remove_reference_t class_factory; + remove_reference_t alias_factory; + + factory(CFunc &&c, AFunc &&a) + : class_factory(std::forward(c)), alias_factory(std::forward(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 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 - CFuncType &class_func = class_factory; - AFuncType &alias_func = alias_factory; + auto &class_func = class_factory; + auto &alias_func = alias_factory; cl.def("__init__", [class_func, alias_func] #endif - (value_and_holder &v_h, Args... args) { + (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(v_h, class_func(std::forward(args)...), false); + construct(v_h, class_func(std::forward(args)...), false); else - construct(v_h, alias_func(std::forward(args)...), true); + construct(v_h, alias_func(std::forward(args)...), true); }, is_new_style_constructor(), extra...); } }; -template using functype = - conditional_t>::value, remove_reference_t *, - conditional_t>::value, remove_reference_t, - Func>>; - -// Helper definition to infer the detail::initimpl::factory template types from a callable object -template -factory, void, Args...> func_decltype(Return (*)(Args...)); - -// metatemplate that ensures the Class and Alias factories take identical arguments: we need to be -// able to call either one with the given arguments (depending on the final instance type). -template -inline constexpr bool require_matching_arguments(Return1 (*)(Args1...), Return2 (*)(Args2...)) { - static_assert(sizeof...(Args1) == sizeof...(Args2), - "pybind11::init(class_factory, alias_factory): class and alias factories must have identical argument signatures"); - static_assert(all_of...>::value, - "pybind11::init(class_factory, alias_factory): class and alias factories must have identical argument signatures"); - return true; -} - -// Unimplemented function provided only for its type signature (via `decltype`), which resolves to -// the appropriate specialization of the above `init` struct with the appropriate function, argument -// and return types. -template -factory, functype, CArgs...> func_decltype(CReturn (*)(CArgs...), AReturn (*)(AArgs...)); - -// Resolves to the appropriate specialization of the `pybind11::detail::initimpl::factory<...>` for a -// given init function or pair of class/alias init functions. -template using factory_t = decltype(func_decltype( - (function_signature_t *) nullptr...)); - NAMESPACE_END(initimpl) NAMESPACE_END(detail) NAMESPACE_END(pybind11) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 3d2d2f05..9ed5036d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1389,12 +1389,12 @@ template detail::initimpl::constructor init() { retu template detail::initimpl::alias_constructor init_alias() { return {}; } /// Binds a factory function as a constructor -template > +template > Ret init(Func &&f) { return {std::forward(f)}; } /// Dual-argument factory function: the first function is called when no alias is needed, the second /// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. -template > +template > Ret init(CFunc &&c, AFunc &&a) { return {std::forward(c), std::forward(a)}; }