mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-29 08:32:02 +00:00
Merge pull request #385 from jagerman/relax-class-arguments
Allow arbitrary class_ template option ordering
This commit is contained in:
commit
6fd3132e81
@ -311,7 +311,7 @@ The binding code also needs a few minor adaptations (highlighted):
|
||||
PYBIND11_PLUGIN(example) {
|
||||
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
|
||||
.def(py::init<>())
|
||||
.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
|
||||
by specifying it as the *third* template argument to :class:`class_`. The
|
||||
second argument with the unique pointer is simply the default holder type used
|
||||
by pybind11. Following this, we are able to define a constructor as usual.
|
||||
by specifying it as an extra template argument to :class:`class_`. (This can
|
||||
also be combined with other template arguments such as a custom holder type;
|
||||
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
|
||||
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
|
||||
|
||||
py::class_<Animal, std::unique_ptr<Animal>, PyAnimal<>> animal(m, "Animal");
|
||||
py::class_<Dog, std::unique_ptr<Dog>, PyDog<>> dog(m, "Dog");
|
||||
py::class_<Husky, std::unique_ptr<Husky>, PyDog<Husky>> husky(m, "Husky");
|
||||
py::class_<Animal, PyAnimal<>> animal(m, "Animal");
|
||||
py::class_<Dog, PyDog<>> dog(m, "Dog");
|
||||
py::class_<Husky, PyDog<Husky>> husky(m, "Husky");
|
||||
// ... add animal, dog, husky definitions
|
||||
|
||||
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) {
|
||||
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
|
||||
.def(py::init<>())
|
||||
.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,
|
||||
refer to the previous section.
|
||||
|
||||
The binding generator for classes, :class:`class_`, takes an optional second
|
||||
template type, which denotes a special *holder* type that is used to manage
|
||||
references to the object. When wrapping a type named ``Type``, the default
|
||||
value of this template parameter is ``std::unique_ptr<Type>``, which means that
|
||||
the object is deallocated when Python's reference count goes to zero.
|
||||
The binding generator for classes, :class:`class_`, can be passed a template
|
||||
type that denotes a special *holder* type that is used to manage references to
|
||||
the object. If no such holder type template argument is given, the default for
|
||||
a type named ``Type`` is ``std::unique_ptr<Type>``, which means that the object
|
||||
is deallocated when Python's reference count goes to zero.
|
||||
|
||||
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
|
||||
@ -977,6 +978,8 @@ code?
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
|
||||
|
||||
class Child { };
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
crucial that instances are deallocated on the C++ side to avoid memory leaks.
|
||||
|
||||
@ -1224,7 +1227,7 @@ section.
|
||||
the other existing exception translators.
|
||||
|
||||
The ``py::exception`` wrapper for creating custom exceptions cannot (yet)
|
||||
be used as a ``py::base``.
|
||||
be used as a base type.
|
||||
|
||||
.. _eigen:
|
||||
|
||||
@ -1808,16 +1811,17 @@ However, it can be acquired as follows:
|
||||
.def(py::init<const std::string &>())
|
||||
.def("bark", &Dog::bark);
|
||||
|
||||
Alternatively, we can rely on the ``base`` tag, which performs an automated
|
||||
lookup of the corresponding Python type. However, this also requires invoking
|
||||
the ``import`` function once to ensure that the pybind11 binding code of the
|
||||
module ``basic`` has been executed.
|
||||
Alternatively, you can specify the base class as a template parameter option to
|
||||
``class_``, which performs an automated lookup of the corresponding Python
|
||||
type. Like the above code, however, this also requires invoking the ``import``
|
||||
function once to ensure that the pybind11 binding code of the module ``basic``
|
||||
has been executed:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
py::module::import("basic");
|
||||
|
||||
py::class_<Dog>(m, "Dog", py::base<Pet>())
|
||||
py::class_<Dog, Pet>(m, "Dog")
|
||||
.def(py::init<const std::string &>())
|
||||
.def("bark", &Dog::bark);
|
||||
|
||||
|
@ -185,9 +185,10 @@ inheritance relationship:
|
||||
std::string bark() const { return "woof!"; }
|
||||
};
|
||||
|
||||
There are two different ways of indicating a hierarchical relationship to
|
||||
pybind11: the first is by specifying the C++ base class explicitly during
|
||||
construction using the ``base`` attribute:
|
||||
There are three different ways of indicating a hierarchical relationship to
|
||||
pybind11: the first specifies the C++ base class as an extra template
|
||||
parameter of the :class:`class_`; the second uses a special ``base`` attribute
|
||||
passed into the constructor:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
@ -195,6 +196,12 @@ construction using the ``base`` attribute:
|
||||
.def(py::init<const std::string &>())
|
||||
.def_readwrite("name", &Pet::name);
|
||||
|
||||
// Method 1: template parameter:
|
||||
py::class_<Dog, Pet /* <- specify C++ parent type */>(m, "Dog")
|
||||
.def(py::init<const std::string &>())
|
||||
.def("bark", &Dog::bark);
|
||||
|
||||
// Method 2: py::base attribute:
|
||||
py::class_<Dog>(m, "Dog", py::base<Pet>() /* <- specify C++ parent type */)
|
||||
.def(py::init<const std::string &>())
|
||||
.def("bark", &Dog::bark);
|
||||
@ -208,11 +215,12 @@ Alternatively, we can also assign a name to the previously bound ``Pet``
|
||||
pet.def(py::init<const std::string &>())
|
||||
.def_readwrite("name", &Pet::name);
|
||||
|
||||
// Method 3: pass parent class_ object:
|
||||
py::class_<Dog>(m, "Dog", pet /* <- specify Python parent type */)
|
||||
.def(py::init<const std::string &>())
|
||||
.def("bark", &Dog::bark);
|
||||
|
||||
Functionality-wise, both approaches are completely equivalent. Afterwards,
|
||||
Functionality-wise, all three approaches are completely equivalent. Afterwards,
|
||||
instances will expose fields and methods of both types:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
@ -800,6 +800,13 @@ protected:
|
||||
holder_type holder;
|
||||
};
|
||||
|
||||
// PYBIND11_DECLARE_HOLDER_TYPE holder types:
|
||||
template <typename base, typename holder> struct is_holder_type :
|
||||
std::is_base_of<detail::type_caster_holder<base, holder>, detail::type_caster<holder>> {};
|
||||
// Specialization for always-supported unique_ptr holders:
|
||||
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 <> 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"); } };
|
||||
|
@ -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...>> { };
|
||||
#endif
|
||||
|
||||
// Extracts the first type from the template parameter pack matching the predicate, or Default if none match.
|
||||
template <template<class> class Predicate, class Default, class... Ts> struct first_of;
|
||||
template <template<class> class Predicate, class Default> struct first_of<Predicate, Default> {
|
||||
using type = Default;
|
||||
};
|
||||
template <template<class> class Predicate, class Default, class T, class... Ts>
|
||||
struct first_of<Predicate, Default, T, Ts...> {
|
||||
using type = typename std::conditional<
|
||||
Predicate<T>::value,
|
||||
T,
|
||||
typename first_of<Predicate, Default, Ts...>::type
|
||||
>::type;
|
||||
};
|
||||
template <template<class> class Predicate, class Default, class... T> using first_of_t = typename first_of<Predicate, Default, 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
|
||||
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;
|
||||
|
@ -49,17 +49,19 @@ template <op_id, op_type, typename B, typename L, typename R> struct op_impl { }
|
||||
|
||||
/// Operator implementation generator
|
||||
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<R, self_t>::value, Base, R>::type R_type;
|
||||
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<R, self_t>::value, Base, R>::type R_type;
|
||||
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)
|
||||
/// Generic support for creating new Python heap types
|
||||
class generic_type : public object {
|
||||
template <typename type, typename holder_type, typename type_alias> friend class class_;
|
||||
template <typename...> friend class class_;
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
|
||||
protected:
|
||||
@ -802,12 +802,46 @@ protected:
|
||||
|
||||
static void releasebuffer(PyObject *, Py_buffer *view) { delete (buffer_info *) view->internal; }
|
||||
};
|
||||
|
||||
template <template<typename> class Predicate, typename... BaseTypes> struct class_selector;
|
||||
template <template<typename> class Predicate, typename Base, typename... Bases>
|
||||
struct class_selector<Predicate, Base, Bases...> {
|
||||
static inline void set_bases(detail::type_record &record) {
|
||||
if (Predicate<Base>::value) record.base_type = &typeid(Base);
|
||||
else class_selector<Predicate, Bases...>::set_bases(record);
|
||||
}
|
||||
};
|
||||
template <template<typename> class Predicate>
|
||||
struct class_selector<Predicate> {
|
||||
static inline void set_bases(detail::type_record &) {}
|
||||
};
|
||||
|
||||
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 {
|
||||
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_base_class = detail::bool_constant<std::is_base_of<T, type_>::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 ||
|
||||
is_base_class<T>::value
|
||||
>;
|
||||
|
||||
public:
|
||||
typedef detail::instance<type, holder_type> instance_type;
|
||||
using type = type_;
|
||||
using type_alias = detail::first_of_t<is_subtype, void, options...>;
|
||||
constexpr static bool has_alias = !std::is_void<type_alias>::value;
|
||||
using holder_type = detail::first_of_t<is_holder, std::unique_ptr<type>, options...>;
|
||||
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");
|
||||
|
||||
static_assert(detail::count_t<is_base_class, options...>::value <= 1,
|
||||
"Invalid class_ base types: multiple inheritance is not supported");
|
||||
|
||||
PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check)
|
||||
|
||||
@ -822,12 +856,14 @@ public:
|
||||
record.init_holder = init_holder;
|
||||
record.dealloc = dealloc;
|
||||
|
||||
detail::class_selector<is_base_class, options...>::set_bases(record);
|
||||
|
||||
/* Process optional arguments, if any */
|
||||
detail::process_attributes<Extra...>::init(extra..., &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;
|
||||
instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))];
|
||||
}
|
||||
@ -852,25 +888,25 @@ public:
|
||||
|
||||
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) {
|
||||
op.template execute<type>(*this, extra...);
|
||||
op.execute(*this, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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) {
|
||||
op.template execute_cast<type>(*this, extra...);
|
||||
op.execute_cast(*this, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args, typename... Extra>
|
||||
class_ &def(const detail::init<Args...> &init, const Extra&... extra) {
|
||||
init.template execute<type>(*this, extra...);
|
||||
init.execute(*this, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args, typename... Extra>
|
||||
class_ &def(const detail::init_alias<Args...> &init, const Extra&... extra) {
|
||||
init.template execute<type>(*this, extra...);
|
||||
init.execute(*this, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -1071,19 +1107,21 @@ private:
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
template <typename... Args> struct init {
|
||||
template <typename Base, typename Holder, typename Alias, typename... Extra,
|
||||
typename std::enable_if<std::is_same<Base, Alias>::value, int>::type = 0>
|
||||
void execute(pybind11::class_<Base, Holder, Alias> &class_, const Extra&... extra) const {
|
||||
template <typename Class, typename... Extra, typename std::enable_if<!Class::has_alias, int>::type = 0>
|
||||
void execute(Class &cl, const Extra&... extra) const {
|
||||
using Base = typename Class::type;
|
||||
/// 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,
|
||||
typename std::enable_if<!std::is_same<Base, Alias>::value &&
|
||||
std::is_constructible<Base, Args...>::value, int>::type = 0>
|
||||
void execute(pybind11::class_<Base, Holder, Alias> &class_, const Extra&... extra) const {
|
||||
handle cl_type = class_;
|
||||
class_.def("__init__", [cl_type](handle self_, Args... args) {
|
||||
template <typename Class, typename... Extra,
|
||||
typename std::enable_if<Class::has_alias &&
|
||||
std::is_constructible<typename Class::type, Args...>::value, int>::type = 0>
|
||||
void execute(Class &cl, const Extra&... extra) const {
|
||||
using Base = typename Class::type;
|
||||
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)
|
||||
new (self_.cast<Base *>()) Base(args...);
|
||||
else
|
||||
@ -1091,11 +1129,12 @@ template <typename... Args> struct init {
|
||||
}, extra...);
|
||||
}
|
||||
|
||||
template <typename Base, typename Holder, typename Alias, typename... Extra,
|
||||
typename std::enable_if<!std::is_same<Base, Alias>::value &&
|
||||
!std::is_constructible<Base, Args...>::value, int>::type = 0>
|
||||
void execute(pybind11::class_<Base, Holder, Alias> &class_, const Extra&... extra) const {
|
||||
class_.def("__init__", [](Alias *self_, Args... args) { new (self_) Alias(args...); }, extra...);
|
||||
template <typename Class, typename... Extra,
|
||||
typename std::enable_if<Class::has_alias &&
|
||||
!std::is_constructible<typename Class::type, Args...>::value, int>::type = 0>
|
||||
void execute(Class &cl, const Extra&... extra) const {
|
||||
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
|
||||
test_buffers.cpp
|
||||
test_callbacks.cpp
|
||||
test_class_args.cpp
|
||||
test_constants_and_functions.cpp
|
||||
test_eigen.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()
|
@ -31,6 +31,11 @@ public:
|
||||
Rabbit(const std::string &name) : Pet(name, "parrot") {}
|
||||
};
|
||||
|
||||
class Hamster : public Pet {
|
||||
public:
|
||||
Hamster(const std::string &name) : Pet(name, "rodent") {}
|
||||
};
|
||||
|
||||
std::string pet_name_species(const Pet &pet) {
|
||||
return pet.name() + " is a " + pet.species();
|
||||
}
|
||||
@ -59,6 +64,10 @@ test_initializer inheritance([](py::module &m) {
|
||||
py::class_<Rabbit>(m, "Rabbit", py::base<Pet>())
|
||||
.def(py::init<std::string>());
|
||||
|
||||
/* And another: list parent in class template arguments */
|
||||
py::class_<Hamster, Pet>(m, "Hamster")
|
||||
.def(py::init<std::string>());
|
||||
|
||||
m.def("pet_name_species", pet_name_species);
|
||||
m.def("dog_bark", dog_bark);
|
||||
|
||||
|
@ -2,7 +2,7 @@ import pytest
|
||||
|
||||
|
||||
def test_inheritance(msg):
|
||||
from pybind11_tests import Pet, Dog, Rabbit, dog_bark, pet_name_species
|
||||
from pybind11_tests import Pet, Dog, Rabbit, Hamster, dog_bark, pet_name_species
|
||||
|
||||
roger = Rabbit('Rabbit')
|
||||
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
|
||||
@ -16,6 +16,9 @@ def test_inheritance(msg):
|
||||
assert molly.name() + " is a " + molly.species() == "Molly is a dog"
|
||||
assert pet_name_species(molly) == "Molly is a dog"
|
||||
|
||||
fred = Hamster('Fred')
|
||||
assert fred.name() + " is a " + fred.species() == "Fred is a rodent"
|
||||
|
||||
assert dog_bark(molly) == "Woof!"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
|
@ -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("dispatch", &Base::dispatch);
|
||||
|
||||
@ -96,7 +96,7 @@ void init_issues(py::module &m) {
|
||||
|
||||
py::class_<ElementBase, std::shared_ptr<ElementBase>> (m2, "ElementBase");
|
||||
|
||||
py::class_<ElementA, std::shared_ptr<ElementA>>(m2, "ElementA", py::base<ElementBase>())
|
||||
py::class_<ElementA, ElementBase, std::shared_ptr<ElementA>>(m2, "ElementA")
|
||||
.def(py::init<int>())
|
||||
.def("value", &ElementA::value);
|
||||
|
||||
|
@ -253,31 +253,31 @@ public:
|
||||
|
||||
void initialize_inherited_virtuals(py::module &m) {
|
||||
// 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("unlucky_number", &A_Repeat::unlucky_number)
|
||||
.def("say_something", &A_Repeat::say_something)
|
||||
.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, A_Repeat, PyB_Repeat>(m, "B_Repeat")
|
||||
.def(py::init<>())
|
||||
.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, B_Repeat, PyC_Repeat>(m, "C_Repeat")
|
||||
.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, C_Repeat, PyD_Repeat>(m, "D_Repeat")
|
||||
.def(py::init<>());
|
||||
|
||||
// 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("unlucky_number", &A_Tpl::unlucky_number)
|
||||
.def("say_something", &A_Tpl::say_something)
|
||||
.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, A_Tpl, PyB_Tpl<>>(m, "B_Tpl")
|
||||
.def(py::init<>())
|
||||
.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, B_Tpl, PyB_Tpl<C_Tpl>>(m, "C_Tpl")
|
||||
.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, C_Tpl, PyB_Tpl<D_Tpl>>(m, "D_Tpl")
|
||||
.def(py::init<>());
|
||||
|
||||
};
|
||||
@ -287,7 +287,7 @@ test_initializer virtual_functions([](py::module &m) {
|
||||
/* Important: indicate the trampoline class PyExampleVirt using the third
|
||||
argument to py::class_. The second argument with the unique pointer
|
||||
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>())
|
||||
/* Reference original class in function definitions */
|
||||
.def("run", &ExampleVirt::run)
|
||||
@ -301,7 +301,7 @@ test_initializer virtual_functions([](py::module &m) {
|
||||
.def(py::init<int, int>());
|
||||
|
||||
#if !defined(__INTEL_COMPILER)
|
||||
py::class_<NCVirt, std::unique_ptr<NCVirt>, NCVirtTrampoline>(m, "NCVirt")
|
||||
py::class_<NCVirt, NCVirtTrampoline>(m, "NCVirt")
|
||||
.def(py::init<>())
|
||||
.def("get_noncopyable", &NCVirt::get_noncopyable)
|
||||
.def("get_movable", &NCVirt::get_movable)
|
||||
|
Loading…
Reference in New Issue
Block a user