mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 13:15:12 +00:00
Simplify py::init() type deduction and error checking
This commit is contained in:
parent
39fd6a9463
commit
15f36d2b2d
@ -199,39 +199,28 @@ template <typename... Args> struct alias_constructor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Implementation class for py::init(Func) and py::init(Func, AliasFunc)
|
// Implementation class for py::init(Func) and py::init(Func, AliasFunc)
|
||||||
template <typename CFunc, typename AFuncIn, typename... Args>
|
template <typename CFunc, typename AFunc = void_type (*)(),
|
||||||
struct factory {
|
typename = function_signature_t<CFunc>, typename = function_signature_t<AFunc>>
|
||||||
private:
|
struct factory;
|
||||||
using CFuncType = typename std::remove_reference<CFunc>::type;
|
|
||||||
using AFunc = conditional_t<std::is_void<AFuncIn>::value, void_type, AFuncIn>;
|
|
||||||
using AFuncType = typename std::remove_reference<AFunc>::type;
|
|
||||||
|
|
||||||
CFuncType class_factory;
|
// Specialization for py::init(Func)
|
||||||
AFuncType alias_factory;
|
template <typename Func, typename Return, typename... Args>
|
||||||
|
struct factory<Func, void_type (*)(), Return(Args...)> {
|
||||||
|
remove_reference_t<Func> class_factory;
|
||||||
|
|
||||||
public:
|
factory(Func &&f) : class_factory(std::forward<Func>(f)) { }
|
||||||
// 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<CFunc>(f)) {}
|
|
||||||
|
|
||||||
// Constructor with two functions/lambdas, for a class with distinct class/alias factories: the
|
// The given class either has no alias or has no separate alias factory;
|
||||||
// first is called when an alias is not needed, the second when the alias is needed. Requires
|
// this always constructs the class itself. If the class is registered with an alias
|
||||||
// non-void AFunc.
|
|
||||||
factory(CFunc &&c, AFunc &&a) :
|
|
||||||
class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(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
|
|
||||||
// type and an alias instance is needed (i.e. because the final type is a Python class
|
// 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
|
// 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.
|
// instance, or the alias needs to be constructible from a `Class &&` argument.
|
||||||
template <typename Class, typename... Extra,
|
template <typename Class, typename... Extra>
|
||||||
enable_if_t<!Class::has_alias || std::is_void<AFuncIn>::value, int> = 0>
|
void execute(Class &cl, const Extra &...extra) && {
|
||||||
void execute(Class &cl, const Extra&... extra) && {
|
|
||||||
#if defined(PYBIND11_CPP14)
|
#if defined(PYBIND11_CPP14)
|
||||||
cl.def("__init__", [func = std::move(class_factory)]
|
cl.def("__init__", [func = std::move(class_factory)]
|
||||||
#else
|
#else
|
||||||
CFuncType &func = class_factory;
|
auto &func = class_factory;
|
||||||
cl.def("__init__", [func]
|
cl.def("__init__", [func]
|
||||||
#endif
|
#endif
|
||||||
(value_and_holder &v_h, Args... args) {
|
(value_and_holder &v_h, Args... args) {
|
||||||
@ -239,64 +228,49 @@ public:
|
|||||||
Py_TYPE(v_h.inst) != v_h.type->type);
|
Py_TYPE(v_h.inst) != v_h.type->type);
|
||||||
}, is_new_style_constructor(), extra...);
|
}, is_new_style_constructor(), extra...);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Add __init__ definition for a class with an alias *and* distinct alias factory; the former is
|
// Specialization for py::init(Func, AliasFunc)
|
||||||
// called when the `self` type passed to `__init__` is the direct class (i.e. not inherited), the latter
|
template <typename CFunc, typename AFunc,
|
||||||
// when `self` is a Python-side subtype.
|
typename CReturn, typename... CArgs, typename AReturn, typename... AArgs>
|
||||||
template <typename Class, typename... Extra,
|
struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
|
||||||
enable_if_t<Class::has_alias && !std::is_void<AFuncIn>::value, int> = 0>
|
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<std::is_same<CArgs, AArgs>...>::value,
|
||||||
|
"pybind11::init(class_factory, alias_factory): class and alias factories "
|
||||||
|
"must have identical argument signatures");
|
||||||
|
|
||||||
|
remove_reference_t<CFunc> class_factory;
|
||||||
|
remove_reference_t<AFunc> alias_factory;
|
||||||
|
|
||||||
|
factory(CFunc &&c, 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) && {
|
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)
|
#if defined(PYBIND11_CPP14)
|
||||||
cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
|
cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
|
||||||
#else
|
#else
|
||||||
CFuncType &class_func = class_factory;
|
auto &class_func = class_factory;
|
||||||
AFuncType &alias_func = alias_factory;
|
auto &alias_func = alias_factory;
|
||||||
cl.def("__init__", [class_func, alias_func]
|
cl.def("__init__", [class_func, alias_func]
|
||||||
#endif
|
#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 (Py_TYPE(v_h.inst) == v_h.type->type)
|
||||||
// If the instance type equals the registered type we don't have inheritance, so
|
// 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:
|
// don't need the alias and can construct using the class function:
|
||||||
construct<Class>(v_h, class_func(std::forward<Args>(args)...), false);
|
construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
|
||||||
else
|
else
|
||||||
construct<Class>(v_h, alias_func(std::forward<Args>(args)...), true);
|
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
|
||||||
}, is_new_style_constructor(), extra...);
|
}, is_new_style_constructor(), extra...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Func> using functype =
|
|
||||||
conditional_t<std::is_function<remove_reference_t<Func>>::value, remove_reference_t<Func> *,
|
|
||||||
conditional_t<is_function_pointer<remove_reference_t<Func>>::value, remove_reference_t<Func>,
|
|
||||||
Func>>;
|
|
||||||
|
|
||||||
// Helper definition to infer the detail::initimpl::factory template types from a callable object
|
|
||||||
template <typename Func, typename Return, typename... Args>
|
|
||||||
factory<functype<Func>, 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 <typename Return1, typename Return2, typename... Args1, typename... Args2>
|
|
||||||
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<std::is_same<Args1, Args2>...>::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 <typename CFunc, typename AFunc,
|
|
||||||
typename CReturn, typename... CArgs, typename AReturn, typename... AArgs,
|
|
||||||
bool = require_matching_arguments((CReturn (*)(CArgs...)) nullptr, (AReturn (*)(AArgs...)) nullptr)>
|
|
||||||
factory<functype<CFunc>, functype<AFunc>, 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 <typename... Func> using factory_t = decltype(func_decltype<Func...>(
|
|
||||||
(function_signature_t<Func> *) nullptr...));
|
|
||||||
|
|
||||||
NAMESPACE_END(initimpl)
|
NAMESPACE_END(initimpl)
|
||||||
NAMESPACE_END(detail)
|
NAMESPACE_END(detail)
|
||||||
NAMESPACE_END(pybind11)
|
NAMESPACE_END(pybind11)
|
||||||
|
@ -1389,12 +1389,12 @@ template <typename... Args> detail::initimpl::constructor<Args...> init() { retu
|
|||||||
template <typename... Args> detail::initimpl::alias_constructor<Args...> init_alias() { return {}; }
|
template <typename... Args> detail::initimpl::alias_constructor<Args...> init_alias() { return {}; }
|
||||||
|
|
||||||
/// Binds a factory function as a constructor
|
/// Binds a factory function as a constructor
|
||||||
template <typename Func, typename Ret = detail::initimpl::factory_t<Func>>
|
template <typename Func, typename Ret = detail::initimpl::factory<Func>>
|
||||||
Ret init(Func &&f) { return {std::forward<Func>(f)}; }
|
Ret init(Func &&f) { return {std::forward<Func>(f)}; }
|
||||||
|
|
||||||
/// Dual-argument factory function: the first function is called when no alias is needed, the second
|
/// 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.
|
/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical.
|
||||||
template <typename CFunc, typename AFunc, typename Ret = detail::initimpl::factory_t<CFunc, AFunc>>
|
template <typename CFunc, typename AFunc, typename Ret = detail::initimpl::factory<CFunc, AFunc>>
|
||||||
Ret init(CFunc &&c, AFunc &&a) {
|
Ret init(CFunc &&c, AFunc &&a) {
|
||||||
return {std::forward<CFunc>(c), std::forward<AFunc>(a)};
|
return {std::forward<CFunc>(c), std::forward<AFunc>(a)};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user