Merge pull request #385 from jagerman/relax-class-arguments

Allow arbitrary class_ template option ordering
This commit is contained in:
Wenzel Jakob 2016-09-07 23:49:00 +09:00 committed by GitHub
commit 6fd3132e81
13 changed files with 235 additions and 64 deletions

View File

@ -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);

View File

@ -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

View File

@ -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"); } };

View File

@ -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;

View File

@ -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...);
}
};

View File

@ -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...);
}
};

View File

@ -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
View 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
View 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()

View File

@ -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);

View File

@ -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:

View File

@ -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);

View File

@ -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)