mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 14:45:12 +00:00
Allow arbitrary class_ template option ordering
The current pybind11::class_<Type, Holder, Trampoline> fixed template ordering results in a requirement to repeat the Holder with its default value (std::unique_ptr<Type>) argument, which is a little bit annoying: it needs to be specified not because we want to override the default, but rather because we need to specify the third argument. This commit removes this limitation by making the class_ template take the type name plus a parameter pack of options. It then extracts the first valid holder type and the first subclass type for holder_type and trampoline type_alias, respectively. (If unfound, both fall back to their current defaults, `std::unique_ptr<type>` and `type`, respectively). If any unmatched template arguments are provided, a static assertion fails. What this means is that you can specify or omit the arguments in any order: py::class_<A, PyA> c1(m, "A"); py::class_<B, PyB, std::shared_ptr<B>> c2(m, "B"); py::class_<C, std::shared_ptr<C>, PyB> c3(m, "C"); It also allows future class attributes (such as base types in the next commit) to be passed as class template types rather than needing to use a py::base<> wrapper.
This commit is contained in:
parent
a3dbdc67f5
commit
5fffe200e3
@ -311,7 +311,7 @@ The binding code also needs a few minor adaptations (highlighted):
|
|||||||
PYBIND11_PLUGIN(example) {
|
PYBIND11_PLUGIN(example) {
|
||||||
py::module m("example", "pybind11 example plugin");
|
py::module m("example", "pybind11 example plugin");
|
||||||
|
|
||||||
py::class_<Animal, std::unique_ptr<Animal>, PyAnimal /* <--- trampoline*/> animal(m, "Animal");
|
py::class_<Animal, PyAnimal /* <--- trampoline*/> animal(m, "Animal");
|
||||||
animal
|
animal
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("go", &Animal::go);
|
.def("go", &Animal::go);
|
||||||
@ -325,9 +325,10 @@ The binding code also needs a few minor adaptations (highlighted):
|
|||||||
}
|
}
|
||||||
|
|
||||||
Importantly, pybind11 is made aware of the trampoline trampoline helper class
|
Importantly, pybind11 is made aware of the trampoline trampoline helper class
|
||||||
by specifying it as the *third* template argument to :class:`class_`. The
|
by specifying it as an extra template argument to :class:`class_`. (This can
|
||||||
second argument with the unique pointer is simply the default holder type used
|
also be combined with other template arguments such as a custom holder type;
|
||||||
by pybind11. Following this, we are able to define a constructor as usual.
|
the order of template types does not matter). Following this, we are able to
|
||||||
|
define a constructor as usual.
|
||||||
|
|
||||||
Note, however, that the above is sufficient for allowing python classes to
|
Note, however, that the above is sufficient for allowing python classes to
|
||||||
extend ``Animal``, but not ``Dog``: see ref:`virtual_and_inheritance` for the
|
extend ``Animal``, but not ``Dog``: see ref:`virtual_and_inheritance` for the
|
||||||
@ -453,9 +454,9 @@ The classes are then registered with pybind11 using:
|
|||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
py::class_<Animal, std::unique_ptr<Animal>, PyAnimal<>> animal(m, "Animal");
|
py::class_<Animal, PyAnimal<>> animal(m, "Animal");
|
||||||
py::class_<Dog, std::unique_ptr<Dog>, PyDog<>> dog(m, "Dog");
|
py::class_<Dog, PyDog<>> dog(m, "Dog");
|
||||||
py::class_<Husky, std::unique_ptr<Husky>, PyDog<Husky>> husky(m, "Husky");
|
py::class_<Husky, PyDog<Husky>> husky(m, "Husky");
|
||||||
// ... add animal, dog, husky definitions
|
// ... add animal, dog, husky definitions
|
||||||
|
|
||||||
Note that ``Husky`` did not require a dedicated trampoline template class at
|
Note that ``Husky`` did not require a dedicated trampoline template class at
|
||||||
@ -525,7 +526,7 @@ be realized as follows (important changes highlighted):
|
|||||||
PYBIND11_PLUGIN(example) {
|
PYBIND11_PLUGIN(example) {
|
||||||
py::module m("example", "pybind11 example plugin");
|
py::module m("example", "pybind11 example plugin");
|
||||||
|
|
||||||
py::class_<Animal, std::unique_ptr<Animal>, PyAnimal> animal(m, "Animal");
|
py::class_<Animal, PyAnimal> animal(m, "Animal");
|
||||||
animal
|
animal
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("go", &Animal::go);
|
.def("go", &Animal::go);
|
||||||
@ -939,11 +940,11 @@ This section explains how to pass values that are wrapped in "smart" pointer
|
|||||||
types with internal reference counting. For the simpler C++11 unique pointers,
|
types with internal reference counting. For the simpler C++11 unique pointers,
|
||||||
refer to the previous section.
|
refer to the previous section.
|
||||||
|
|
||||||
The binding generator for classes, :class:`class_`, takes an optional second
|
The binding generator for classes, :class:`class_`, can be passed a template
|
||||||
template type, which denotes a special *holder* type that is used to manage
|
type that denotes a special *holder* type that is used to manage references to
|
||||||
references to the object. When wrapping a type named ``Type``, the default
|
the object. If no such holder type template argument is given, the default for
|
||||||
value of this template parameter is ``std::unique_ptr<Type>``, which means that
|
a type named ``Type`` is ``std::unique_ptr<Type>``, which means that the object
|
||||||
the object is deallocated when Python's reference count goes to zero.
|
is deallocated when Python's reference count goes to zero.
|
||||||
|
|
||||||
It is possible to switch to other types of reference counting wrappers or smart
|
It is possible to switch to other types of reference counting wrappers or smart
|
||||||
pointers, which is useful in codebases that rely on them. For instance, the
|
pointers, which is useful in codebases that rely on them. For instance, the
|
||||||
@ -977,6 +978,8 @@ code?
|
|||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
|
||||||
|
|
||||||
class Child { };
|
class Child { };
|
||||||
|
|
||||||
class Parent {
|
class Parent {
|
||||||
@ -1089,7 +1092,7 @@ pybind11. The underlying issue is that the ``std::unique_ptr`` holder type that
|
|||||||
is responsible for managing the lifetime of instances will reference the
|
is responsible for managing the lifetime of instances will reference the
|
||||||
destructor even if no deallocations ever take place. In order to expose classes
|
destructor even if no deallocations ever take place. In order to expose classes
|
||||||
with private or protected destructors, it is possible to override the holder
|
with private or protected destructors, it is possible to override the holder
|
||||||
type via the second argument to ``class_``. Pybind11 provides a helper class
|
type via a holder type argument to ``class_``. Pybind11 provides a helper class
|
||||||
``py::nodelete`` that disables any destructor invocations. In this case, it is
|
``py::nodelete`` that disables any destructor invocations. In this case, it is
|
||||||
crucial that instances are deallocated on the C++ side to avoid memory leaks.
|
crucial that instances are deallocated on the C++ side to avoid memory leaks.
|
||||||
|
|
||||||
|
@ -798,6 +798,13 @@ protected:
|
|||||||
holder_type holder;
|
holder_type holder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename base, typename holder> struct is_holder_type :
|
||||||
|
// PYBIND11_DECLARE_HOLDER_TYPE holder types:
|
||||||
|
std::conditional<std::is_base_of<detail::type_caster_holder<base, holder>, detail::type_caster<holder>>::value,
|
||||||
|
std::true_type,
|
||||||
|
std::false_type>::type {};
|
||||||
|
template <typename base, typename deleter> struct is_holder_type<base, std::unique_ptr<base, deleter>> : std::true_type {};
|
||||||
|
|
||||||
template <typename T> struct handle_type_name { static PYBIND11_DESCR name() { return _<T>(); } };
|
template <typename T> struct handle_type_name { static PYBIND11_DESCR name() { return _<T>(); } };
|
||||||
template <> struct handle_type_name<bytes> { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } };
|
template <> struct handle_type_name<bytes> { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } };
|
||||||
template <> struct handle_type_name<args> { static PYBIND11_DESCR name() { return _("*args"); } };
|
template <> struct handle_type_name<args> { static PYBIND11_DESCR name() { return _("*args"); } };
|
||||||
|
@ -358,6 +358,28 @@ template <template<typename> class P, typename T, typename... Ts>
|
|||||||
struct any_of_t<P, T, Ts...> : conditional_t<P<T>::value, std::true_type, any_of_t<P, Ts...>> { };
|
struct any_of_t<P, T, Ts...> : conditional_t<P<T>::value, std::true_type, any_of_t<P, Ts...>> { };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Extracts the first type from the template parameter pack matching the predicate, or void if none match.
|
||||||
|
template <template<class> class Predicate, class... Ts> struct first_of;
|
||||||
|
template <template<class> class Predicate> struct first_of<Predicate> {
|
||||||
|
using type = void;
|
||||||
|
};
|
||||||
|
template <template<class> class Predicate, class T, class... Ts>
|
||||||
|
struct first_of<Predicate, T, Ts...> {
|
||||||
|
using type = typename std::conditional<
|
||||||
|
Predicate<T>::value,
|
||||||
|
T,
|
||||||
|
typename first_of<Predicate, Ts...>::type
|
||||||
|
>::type;
|
||||||
|
};
|
||||||
|
template <template<class> class Predicate, class... T> using first_of_t = typename first_of<Predicate, T...>::type;
|
||||||
|
|
||||||
|
// Counts the number of types in the template parameter pack matching the predicate
|
||||||
|
template <template<typename> class Predicate, typename... Ts> struct count_t;
|
||||||
|
template <template<typename> class Predicate> struct count_t<Predicate> : std::integral_constant<size_t, 0> {};
|
||||||
|
template <template<typename> class Predicate, class T, class... Ts>
|
||||||
|
struct count_t<Predicate, T, Ts...> : std::integral_constant<size_t,
|
||||||
|
Predicate<T>::value + count_t<Predicate, Ts...>::value> {};
|
||||||
|
|
||||||
/// Defer the evaluation of type T until types Us are instantiated
|
/// Defer the evaluation of type T until types Us are instantiated
|
||||||
template <typename T, typename... /*Us*/> struct deferred_type { using type = T; };
|
template <typename T, typename... /*Us*/> struct deferred_type { using type = T; };
|
||||||
template <typename T, typename... Us> using deferred_t = typename deferred_type<T, Us...>::type;
|
template <typename T, typename... Us> using deferred_t = typename deferred_type<T, Us...>::type;
|
||||||
|
@ -49,17 +49,19 @@ template <op_id, op_type, typename B, typename L, typename R> struct op_impl { }
|
|||||||
|
|
||||||
/// Operator implementation generator
|
/// Operator implementation generator
|
||||||
template <op_id id, op_type ot, typename L, typename R> struct op_ {
|
template <op_id id, op_type ot, typename L, typename R> struct op_ {
|
||||||
template <typename Base, typename Holder, typename... Extra> void execute(pybind11::class_<Base, Holder> &class_, const Extra&... extra) const {
|
template <typename Class, typename... Extra> void execute(Class &cl, const Extra&... extra) const {
|
||||||
|
typedef typename Class::type Base;
|
||||||
typedef typename std::conditional<std::is_same<L, self_t>::value, Base, L>::type L_type;
|
typedef typename std::conditional<std::is_same<L, self_t>::value, Base, L>::type L_type;
|
||||||
typedef typename std::conditional<std::is_same<R, self_t>::value, Base, R>::type R_type;
|
typedef typename std::conditional<std::is_same<R, self_t>::value, Base, R>::type R_type;
|
||||||
typedef op_impl<id, ot, Base, L_type, R_type> op;
|
typedef op_impl<id, ot, Base, L_type, R_type> op;
|
||||||
class_.def(op::name(), &op::execute, extra...);
|
cl.def(op::name(), &op::execute, extra...);
|
||||||
}
|
}
|
||||||
template <typename Base, typename Holder, typename... Extra> void execute_cast(pybind11::class_<Base, Holder> &class_, const Extra&... extra) const {
|
template <typename Class, typename... Extra> void execute_cast(Class &cl, const Extra&... extra) const {
|
||||||
|
typedef typename Class::type Base;
|
||||||
typedef typename std::conditional<std::is_same<L, self_t>::value, Base, L>::type L_type;
|
typedef typename std::conditional<std::is_same<L, self_t>::value, Base, L>::type L_type;
|
||||||
typedef typename std::conditional<std::is_same<R, self_t>::value, Base, R>::type R_type;
|
typedef typename std::conditional<std::is_same<R, self_t>::value, Base, R>::type R_type;
|
||||||
typedef op_impl<id, ot, Base, L_type, R_type> op;
|
typedef op_impl<id, ot, Base, L_type, R_type> op;
|
||||||
class_.def(op::name(), &op::execute_cast, extra...);
|
cl.def(op::name(), &op::execute_cast, extra...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -563,7 +563,7 @@ public:
|
|||||||
NAMESPACE_BEGIN(detail)
|
NAMESPACE_BEGIN(detail)
|
||||||
/// Generic support for creating new Python heap types
|
/// Generic support for creating new Python heap types
|
||||||
class generic_type : public object {
|
class generic_type : public object {
|
||||||
template <typename type, typename holder_type, typename type_alias> friend class class_;
|
template <typename...> friend class class_;
|
||||||
public:
|
public:
|
||||||
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
|
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
|
||||||
protected:
|
protected:
|
||||||
@ -804,10 +804,31 @@ protected:
|
|||||||
};
|
};
|
||||||
NAMESPACE_END(detail)
|
NAMESPACE_END(detail)
|
||||||
|
|
||||||
template <typename type, typename holder_type = std::unique_ptr<type>, typename type_alias = type>
|
template <typename type_, typename... options>
|
||||||
class class_ : public detail::generic_type {
|
class class_ : public detail::generic_type {
|
||||||
|
template <typename T> using is_holder = detail::is_holder_type<type_, T>;
|
||||||
|
template <typename T> using is_subtype = detail::bool_constant<std::is_base_of<type_, T>::value && !std::is_same<T, type_>::value>;
|
||||||
|
template <typename T> using is_valid_class_option =
|
||||||
|
detail::bool_constant<
|
||||||
|
is_holder<T>::value ||
|
||||||
|
is_subtype<T>::value
|
||||||
|
>;
|
||||||
|
|
||||||
|
using extracted_holder_t = typename detail::first_of_t<is_holder, options...>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef detail::instance<type, holder_type> instance_type;
|
using type = type_;
|
||||||
|
using type_alias = detail::first_of_t<is_subtype, options...>;
|
||||||
|
constexpr static bool has_alias = !std::is_void<type_alias>::value;
|
||||||
|
using holder_type = typename std::conditional<
|
||||||
|
std::is_void<extracted_holder_t>::value,
|
||||||
|
std::unique_ptr<type>,
|
||||||
|
extracted_holder_t
|
||||||
|
>::type;
|
||||||
|
using instance_type = detail::instance<type, holder_type>;
|
||||||
|
|
||||||
|
static_assert(detail::all_of_t<is_valid_class_option, options...>::value,
|
||||||
|
"Unknown/invalid class_ template parameters provided");
|
||||||
|
|
||||||
PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check)
|
PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check)
|
||||||
|
|
||||||
@ -827,7 +848,7 @@ public:
|
|||||||
|
|
||||||
detail::generic_type::initialize(&record);
|
detail::generic_type::initialize(&record);
|
||||||
|
|
||||||
if (!std::is_same<type, type_alias>::value) {
|
if (has_alias) {
|
||||||
auto &instances = pybind11::detail::get_internals().registered_types_cpp;
|
auto &instances = pybind11::detail::get_internals().registered_types_cpp;
|
||||||
instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))];
|
instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))];
|
||||||
}
|
}
|
||||||
@ -852,25 +873,25 @@ public:
|
|||||||
|
|
||||||
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
||||||
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
|
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
|
||||||
op.template execute<type>(*this, extra...);
|
op.execute(*this, extra...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
||||||
class_ & def_cast(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
|
class_ & def_cast(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
|
||||||
op.template execute_cast<type>(*this, extra...);
|
op.execute_cast(*this, extra...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args, typename... Extra>
|
template <typename... Args, typename... Extra>
|
||||||
class_ &def(const detail::init<Args...> &init, const Extra&... extra) {
|
class_ &def(const detail::init<Args...> &init, const Extra&... extra) {
|
||||||
init.template execute<type>(*this, extra...);
|
init.execute(*this, extra...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args, typename... Extra>
|
template <typename... Args, typename... Extra>
|
||||||
class_ &def(const detail::init_alias<Args...> &init, const Extra&... extra) {
|
class_ &def(const detail::init_alias<Args...> &init, const Extra&... extra) {
|
||||||
init.template execute<type>(*this, extra...);
|
init.execute(*this, extra...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1071,19 +1092,21 @@ private:
|
|||||||
|
|
||||||
NAMESPACE_BEGIN(detail)
|
NAMESPACE_BEGIN(detail)
|
||||||
template <typename... Args> struct init {
|
template <typename... Args> struct init {
|
||||||
template <typename Base, typename Holder, typename Alias, typename... Extra,
|
template <typename Class, typename... Extra, typename std::enable_if<!Class::has_alias, int>::type = 0>
|
||||||
typename std::enable_if<std::is_same<Base, Alias>::value, int>::type = 0>
|
void execute(Class &cl, const Extra&... extra) const {
|
||||||
void execute(pybind11::class_<Base, Holder, Alias> &class_, const Extra&... extra) const {
|
using Base = typename Class::type;
|
||||||
/// Function which calls a specific C++ in-place constructor
|
/// Function which calls a specific C++ in-place constructor
|
||||||
class_.def("__init__", [](Base *self_, Args... args) { new (self_) Base(args...); }, extra...);
|
cl.def("__init__", [](Base *self_, Args... args) { new (self_) Base(args...); }, extra...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Base, typename Holder, typename Alias, typename... Extra,
|
template <typename Class, typename... Extra,
|
||||||
typename std::enable_if<!std::is_same<Base, Alias>::value &&
|
typename std::enable_if<Class::has_alias &&
|
||||||
std::is_constructible<Base, Args...>::value, int>::type = 0>
|
std::is_constructible<typename Class::type, Args...>::value, int>::type = 0>
|
||||||
void execute(pybind11::class_<Base, Holder, Alias> &class_, const Extra&... extra) const {
|
void execute(Class &cl, const Extra&... extra) const {
|
||||||
handle cl_type = class_;
|
using Base = typename Class::type;
|
||||||
class_.def("__init__", [cl_type](handle self_, Args... args) {
|
using Alias = typename Class::type_alias;
|
||||||
|
handle cl_type = cl;
|
||||||
|
cl.def("__init__", [cl_type](handle self_, Args... args) {
|
||||||
if (self_.get_type() == cl_type)
|
if (self_.get_type() == cl_type)
|
||||||
new (self_.cast<Base *>()) Base(args...);
|
new (self_.cast<Base *>()) Base(args...);
|
||||||
else
|
else
|
||||||
@ -1091,11 +1114,12 @@ template <typename... Args> struct init {
|
|||||||
}, extra...);
|
}, extra...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Base, typename Holder, typename Alias, typename... Extra,
|
template <typename Class, typename... Extra,
|
||||||
typename std::enable_if<!std::is_same<Base, Alias>::value &&
|
typename std::enable_if<Class::has_alias &&
|
||||||
!std::is_constructible<Base, Args...>::value, int>::type = 0>
|
!std::is_constructible<typename Class::type, Args...>::value, int>::type = 0>
|
||||||
void execute(pybind11::class_<Base, Holder, Alias> &class_, const Extra&... extra) const {
|
void execute(Class &cl, const Extra&... extra) const {
|
||||||
class_.def("__init__", [](Alias *self_, Args... args) { new (self_) Alias(args...); }, extra...);
|
using Alias = typename Class::type_alias;
|
||||||
|
cl.def("__init__", [](Alias *self_, Args... args) { new (self_) Alias(args...); }, extra...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ endif()
|
|||||||
set(PYBIND11_TEST_FILES
|
set(PYBIND11_TEST_FILES
|
||||||
test_buffers.cpp
|
test_buffers.cpp
|
||||||
test_callbacks.cpp
|
test_callbacks.cpp
|
||||||
|
test_class_args.cpp
|
||||||
test_constants_and_functions.cpp
|
test_constants_and_functions.cpp
|
||||||
test_eigen.cpp
|
test_eigen.cpp
|
||||||
test_enum.cpp
|
test_enum.cpp
|
||||||
|
69
tests/test_class_args.cpp
Normal file
69
tests/test_class_args.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
tests/test_class_args.cpp -- tests that various way of defining a class work
|
||||||
|
|
||||||
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
|
||||||
|
|
||||||
|
template <int N> class BreaksBase {};
|
||||||
|
template <int N> class BreaksTramp : public BreaksBase<N> {};
|
||||||
|
// These should all compile just fine:
|
||||||
|
typedef py::class_<BreaksBase<1>, std::unique_ptr<BreaksBase<1>>, BreaksTramp<1>> DoesntBreak1;
|
||||||
|
typedef py::class_<BreaksBase<2>, BreaksTramp<2>, std::unique_ptr<BreaksBase<2>>> DoesntBreak2;
|
||||||
|
typedef py::class_<BreaksBase<3>, std::unique_ptr<BreaksBase<3>>> DoesntBreak3;
|
||||||
|
typedef py::class_<BreaksBase<4>, BreaksTramp<4>> DoesntBreak4;
|
||||||
|
typedef py::class_<BreaksBase<5>> DoesntBreak5;
|
||||||
|
typedef py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>> DoesntBreak6;
|
||||||
|
typedef py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>> DoesntBreak7;
|
||||||
|
typedef py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>> DoesntBreak8;
|
||||||
|
#define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<N>>::value, \
|
||||||
|
"DoesntBreak" #N " has wrong type!")
|
||||||
|
CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8);
|
||||||
|
#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<N>>::value, \
|
||||||
|
"DoesntBreak" #N " has wrong type_alias!")
|
||||||
|
#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \
|
||||||
|
"DoesntBreak" #N " has type alias, but shouldn't!")
|
||||||
|
CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8);
|
||||||
|
#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<N>>>::value, \
|
||||||
|
"DoesntBreak" #N " has wrong holder_type!")
|
||||||
|
CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique);
|
||||||
|
CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
|
||||||
|
|
||||||
|
// There's no nice way to test that these fail because they fail to compile; leave them here,
|
||||||
|
// though, so that they can be manually tested by uncommenting them (and seeing that compilation
|
||||||
|
// failures occurs).
|
||||||
|
|
||||||
|
// We have to actually look into the type: the typedef alone isn't enough to instantiate the type:
|
||||||
|
#define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-N>>::value, \
|
||||||
|
"Breaks1 has wrong type!");
|
||||||
|
|
||||||
|
//// Two holder classes:
|
||||||
|
//typedef py::class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>> Breaks1;
|
||||||
|
//CHECK_BROKEN(1);
|
||||||
|
//// Two aliases:
|
||||||
|
//typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
|
||||||
|
//CHECK_BROKEN(2);
|
||||||
|
//// Holder + 2 aliases
|
||||||
|
//typedef py::class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>> Breaks3;
|
||||||
|
//CHECK_BROKEN(3);
|
||||||
|
//// Alias + 2 holders
|
||||||
|
//typedef py::class_<BreaksBase<-4>, std::unique_ptr<BreaksBase<-4>>, BreaksTramp<-4>, std::shared_ptr<BreaksBase<-4>>> Breaks4;
|
||||||
|
//CHECK_BROKEN(4);
|
||||||
|
//// Invalid option (not a subclass or holder)
|
||||||
|
//typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5;
|
||||||
|
//CHECK_BROKEN(5);
|
||||||
|
//// Invalid option: multiple inheritance not supported:
|
||||||
|
//template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
|
||||||
|
//typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
|
||||||
|
//CHECK_BROKEN(8);
|
||||||
|
|
||||||
|
test_initializer class_args([](py::module &m) {
|
||||||
|
// Just test that this compiled okay
|
||||||
|
m.def("class_args_noop", []() {});
|
||||||
|
});
|
7
tests/test_class_args.py
Normal file
7
tests/test_class_args.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
def test_class_args():
|
||||||
|
# There's basically nothing to test here; just make sure the code compiled and declared its definition
|
||||||
|
from pybind11_tests import class_args_noop
|
||||||
|
class_args_noop()
|
@ -51,7 +51,7 @@ void init_issues(py::module &m) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
py::class_<Base, std::unique_ptr<Base>, DispatchIssue>(m2, "DispatchIssue")
|
py::class_<Base, DispatchIssue>(m2, "DispatchIssue")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("dispatch", &Base::dispatch);
|
.def("dispatch", &Base::dispatch);
|
||||||
|
|
||||||
|
@ -253,31 +253,31 @@ public:
|
|||||||
|
|
||||||
void initialize_inherited_virtuals(py::module &m) {
|
void initialize_inherited_virtuals(py::module &m) {
|
||||||
// Method 1: repeat
|
// Method 1: repeat
|
||||||
py::class_<A_Repeat, std::unique_ptr<A_Repeat>, PyA_Repeat>(m, "A_Repeat")
|
py::class_<A_Repeat, PyA_Repeat>(m, "A_Repeat")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("unlucky_number", &A_Repeat::unlucky_number)
|
.def("unlucky_number", &A_Repeat::unlucky_number)
|
||||||
.def("say_something", &A_Repeat::say_something)
|
.def("say_something", &A_Repeat::say_something)
|
||||||
.def("say_everything", &A_Repeat::say_everything);
|
.def("say_everything", &A_Repeat::say_everything);
|
||||||
py::class_<B_Repeat, std::unique_ptr<B_Repeat>, PyB_Repeat>(m, "B_Repeat", py::base<A_Repeat>())
|
py::class_<B_Repeat, PyB_Repeat>(m, "B_Repeat", py::base<A_Repeat>())
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("lucky_number", &B_Repeat::lucky_number);
|
.def("lucky_number", &B_Repeat::lucky_number);
|
||||||
py::class_<C_Repeat, std::unique_ptr<C_Repeat>, PyC_Repeat>(m, "C_Repeat", py::base<B_Repeat>())
|
py::class_<C_Repeat, PyC_Repeat>(m, "C_Repeat", py::base<B_Repeat>())
|
||||||
.def(py::init<>());
|
.def(py::init<>());
|
||||||
py::class_<D_Repeat, std::unique_ptr<D_Repeat>, PyD_Repeat>(m, "D_Repeat", py::base<C_Repeat>())
|
py::class_<D_Repeat, PyD_Repeat>(m, "D_Repeat", py::base<C_Repeat>())
|
||||||
.def(py::init<>());
|
.def(py::init<>());
|
||||||
|
|
||||||
// Method 2: Templated trampolines
|
// Method 2: Templated trampolines
|
||||||
py::class_<A_Tpl, std::unique_ptr<A_Tpl>, PyA_Tpl<>>(m, "A_Tpl")
|
py::class_<A_Tpl, PyA_Tpl<>>(m, "A_Tpl")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("unlucky_number", &A_Tpl::unlucky_number)
|
.def("unlucky_number", &A_Tpl::unlucky_number)
|
||||||
.def("say_something", &A_Tpl::say_something)
|
.def("say_something", &A_Tpl::say_something)
|
||||||
.def("say_everything", &A_Tpl::say_everything);
|
.def("say_everything", &A_Tpl::say_everything);
|
||||||
py::class_<B_Tpl, std::unique_ptr<B_Tpl>, PyB_Tpl<>>(m, "B_Tpl", py::base<A_Tpl>())
|
py::class_<B_Tpl, PyB_Tpl<>>(m, "B_Tpl", py::base<A_Tpl>())
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("lucky_number", &B_Tpl::lucky_number);
|
.def("lucky_number", &B_Tpl::lucky_number);
|
||||||
py::class_<C_Tpl, std::unique_ptr<C_Tpl>, PyB_Tpl<C_Tpl>>(m, "C_Tpl", py::base<B_Tpl>())
|
py::class_<C_Tpl, PyB_Tpl<C_Tpl>>(m, "C_Tpl", py::base<B_Tpl>())
|
||||||
.def(py::init<>());
|
.def(py::init<>());
|
||||||
py::class_<D_Tpl, std::unique_ptr<D_Tpl>, PyB_Tpl<D_Tpl>>(m, "D_Tpl", py::base<C_Tpl>())
|
py::class_<D_Tpl, PyB_Tpl<D_Tpl>>(m, "D_Tpl", py::base<C_Tpl>())
|
||||||
.def(py::init<>());
|
.def(py::init<>());
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -287,7 +287,7 @@ test_initializer virtual_functions([](py::module &m) {
|
|||||||
/* Important: indicate the trampoline class PyExampleVirt using the third
|
/* Important: indicate the trampoline class PyExampleVirt using the third
|
||||||
argument to py::class_. The second argument with the unique pointer
|
argument to py::class_. The second argument with the unique pointer
|
||||||
is simply the default holder type used by pybind11. */
|
is simply the default holder type used by pybind11. */
|
||||||
py::class_<ExampleVirt, std::unique_ptr<ExampleVirt>, PyExampleVirt>(m, "ExampleVirt")
|
py::class_<ExampleVirt, PyExampleVirt>(m, "ExampleVirt")
|
||||||
.def(py::init<int>())
|
.def(py::init<int>())
|
||||||
/* Reference original class in function definitions */
|
/* Reference original class in function definitions */
|
||||||
.def("run", &ExampleVirt::run)
|
.def("run", &ExampleVirt::run)
|
||||||
@ -301,7 +301,7 @@ test_initializer virtual_functions([](py::module &m) {
|
|||||||
.def(py::init<int, int>());
|
.def(py::init<int, int>());
|
||||||
|
|
||||||
#if !defined(__INTEL_COMPILER)
|
#if !defined(__INTEL_COMPILER)
|
||||||
py::class_<NCVirt, std::unique_ptr<NCVirt>, NCVirtTrampoline>(m, "NCVirt")
|
py::class_<NCVirt, NCVirtTrampoline>(m, "NCVirt")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("get_noncopyable", &NCVirt::get_noncopyable)
|
.def("get_noncopyable", &NCVirt::get_noncopyable)
|
||||||
.def("get_movable", &NCVirt::get_movable)
|
.def("get_movable", &NCVirt::get_movable)
|
||||||
|
Loading…
Reference in New Issue
Block a user