Merge pull request #410 from wjakob/mi

WIP: Multiple inheritance support
This commit is contained in:
Wenzel Jakob 2016-09-19 18:55:05 +02:00 committed by GitHub
commit d922dffec4
25 changed files with 529 additions and 188 deletions

View File

@ -45,10 +45,12 @@ pybind11 can map the following core C++ features to Python
- Arbitrary exception types
- Enumerations
- Callbacks
- Iterators and ranges
- Custom operators
- Single and multiple inheritance
- STL data structures
- Iterators and ranges
- Smart pointers with reference counting like `std::shared_ptr`
- Smart pointers with reference counting like ``std::shared_ptr``
- Internal references with correct reference counting
- C++ classes with virtual (and pure virtual) methods can be extended in Python

View File

@ -217,6 +217,8 @@ The following interactive session shows how to call them from Python.
that demonstrates how to work with callbacks and anonymous functions in
more detail.
.. _overriding_virtuals:
Overriding virtual functions in Python
======================================
@ -2151,3 +2153,45 @@ type is explicitly allowed.
};
}
};
Multiple Inheritance
====================
pybind11 can create bindings for types that derive from multiple base types
(aka. *multiple inheritance*). To do so, specify all bases in the template
arguments of the ``class_`` declaration:
.. code-block:: cpp
py::class_<MyType, BaseType1, BaseType2, BaseType3>(m, "MyType")
...
The base types can be specified in arbitrary order, and they can even be
interspersed with alias types and holder types (discussed earlier in this
document)---pybind11 will automatically find out which is which. The only
requirement is that the first template argument is the type to be declared.
There are two caveats regarding the implementation of this feature:
1. When only one base type is specified for a C++ type that actually has
multiple bases, pybind11 will assume that it does not participate in
multiple inheritance, which can lead to undefined behavior. In such cases,
add the tag ``multiple_inheritance``:
.. code-block:: cpp
py::class_<MyType, BaseType2>(m, "MyType", py::multiple_inheritance());
The tag is redundant and does not need to be specified when multiple base
types are listed.
2. As was previously discussed in the section on :ref:`overriding_virtuals`, it
is easy to create Python types that derive from C++ classes. It is even
possible to make use of multiple inheritance to declare a Python class which
has e.g. a C++ and a Python class as bases. However, any attempt to create a
type that has *two or more* C++ classes in its hierarchy of base types will
fail with a fatal error message: ``TypeError: multiple bases have instance
lay-out conflict``. Core Python types that are implemented in C (e.g.
``dict``, ``list``, ``Exception``, etc.) also fall under this combination
and cannot be combined with C++ types bound using pybind11 via multiple
inheritance.

View File

@ -185,10 +185,9 @@ inheritance relationship:
std::string bark() const { return "woof!"; }
};
There are three different ways of indicating a hierarchical relationship to
There are two 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:
parameter of the :class:`class_`:
.. code-block:: cpp
@ -201,11 +200,6 @@ passed into the constructor:
.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);
Alternatively, we can also assign a name to the previously bound ``Pet``
:class:`class_` object and reference it when binding the ``Dog`` class:
@ -215,13 +209,13 @@ 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:
// Method 2: 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, all three approaches are completely equivalent. Afterwards,
instances will expose fields and methods of both types:
Functionality-wise, both approaches are equivalent. Afterwards, instances will
expose fields and methods of both types:
.. code-block:: pycon

View File

@ -35,12 +35,14 @@ The following core C++ features can be mapped to Python
- Instance methods and static methods
- Overloaded functions
- Instance attributes and static attributes
- Exceptions
- Arbitrary exception types
- Enumerations
- Iterators and ranges
- Callbacks
- Iterators and ranges
- Custom operators
- Single and multiple inheritance
- STL data structures
- Iterators and ranges
- Smart pointers with reference counting like ``std::shared_ptr``
- Internal references with correct reference counting
- C++ classes with virtual (and pure virtual) methods can be extended in Python

View File

@ -9,15 +9,12 @@ certain limitations:
values. This means that some additional care is needed to avoid bugs that
would be caught by the type checker in a traditional C++ program.
- Multiple inheritance relationships on the C++ side cannot be mapped to
Python.
- The NumPy interface ``pybind11::array`` greatly simplifies accessing
numerical data from C++ (and vice versa), but it's not a full-blown array
class like ``Eigen::Array`` or ``boost.multi_array``.
All of these features could be implemented but would lead to a significant
increase in complexity. I've decided to draw the line here to keep this project
simple and compact. Users who absolutely require these features are encouraged
to fork pybind11.
These features could be implemented but would lead to a significant increase in
complexity. I've decided to draw the line here to keep this project simple and
compact. Users who absolutely require these features are encouraged to fork
pybind11.

View File

@ -33,11 +33,17 @@ struct name { const char *value; name(const char *value) : value(value) { } };
struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } };
/// Annotation indicating that a class derives from another given type
template <typename T> struct base { };
template <typename T> struct base {
PYBIND11_DEPRECATED("base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
base() { }
};
/// Keep patient alive while nurse lives
template <int Nurse, int Patient> struct keep_alive { };
/// Annotation indicating that a class is involved in a multiple inheritance relationship
struct multiple_inheritance { };
NAMESPACE_BEGIN(detail)
/* Forward declarations */
enum op_id : int;
@ -124,6 +130,8 @@ struct function_record {
/// Special data structure which (temporarily) holds metadata about a bound class
struct type_record {
PYBIND11_NOINLINE type_record() { }
/// Handle to the parent scope
handle scope;
@ -145,21 +153,36 @@ struct type_record {
/// Function pointer to class_<..>::dealloc
void (*dealloc)(PyObject *) = nullptr;
// Pointer to RTTI type_info data structure of base class
const std::type_info *base_type = nullptr;
/// OR: Python handle to base class
handle base_handle;
/// List of base classes of the newly created type
list bases;
/// Optional docstring
const char *doc = nullptr;
/// Multiple inheritance marker
bool multiple_inheritance = false;
PYBIND11_NOINLINE void add_base(const std::type_info *base, void *(*caster)(void *)) {
auto base_info = detail::get_type_info(*base, false);
if (!base_info) {
std::string tname(base->name());
detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(name) +
"\" referenced unknown base type \"" + tname + "\"");
}
bases.append((PyObject *) base_info->type);
if (caster)
base_info->implicit_casts.push_back(std::make_pair(type, caster));
}
};
/**
* Partial template specializations to process custom attributes provided to
* cpp_function_ and class_. These are either used to initialize the respective
* fields in the type_record and function_record data structures or executed
* at runtime to deal with custom call policies (e.g. keep_alive).
* fields in the type_record and function_record data structures or executed at
* runtime to deal with custom call policies (e.g. keep_alive).
*/
template <typename T, typename SFINAE = void> struct process_attribute;
@ -253,14 +276,20 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
/// Process a parent class attribute
template <typename T>
struct process_attribute<T, typename std::enable_if<std::is_base_of<handle, T>::value>::type> : process_attribute_default<handle> {
static void init(const handle &h, type_record *r) { r->base_handle = h; }
struct process_attribute<T, enable_if_t<std::is_base_of<handle, T>::value>> : process_attribute_default<handle> {
static void init(const handle &h, type_record *r) { r->bases.append(h); }
};
/// Process a parent class attribute
/// Process a parent class attribute (deprecated, does not support multiple inheritance)
template <typename T>
struct process_attribute<base<T>> : process_attribute_default<base<T>> {
static void init(const base<T> &, type_record *r) { r->base_type = &typeid(T); }
static void init(const base<T> &, type_record *r) { r->add_base(&typeid(T), nullptr); }
};
/// Process a multiple inheritance attribute
template <>
struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> {
static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; }
};
/***
@ -269,13 +298,13 @@ struct process_attribute<base<T>> : process_attribute_default<base<T>> {
* otherwise
*/
template <int Nurse, int Patient> struct process_attribute<keep_alive<Nurse, Patient>> : public process_attribute_default<keep_alive<Nurse, Patient>> {
template <int N = Nurse, int P = Patient, typename std::enable_if<N != 0 && P != 0, int>::type = 0>
template <int N = Nurse, int P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
static void precall(handle args) { keep_alive_impl(Nurse, Patient, args, handle()); }
template <int N = Nurse, int P = Patient, typename std::enable_if<N != 0 && P != 0, int>::type = 0>
template <int N = Nurse, int P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
static void postcall(handle, handle) { }
template <int N = Nurse, int P = Patient, typename std::enable_if<N == 0 || P == 0, int>::type = 0>
template <int N = Nurse, int P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
static void precall(handle) { }
template <int N = Nurse, int P = Patient, typename std::enable_if<N == 0 || P == 0, int>::type = 0>
template <int N = Nurse, int P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
static void postcall(handle args, handle ret) { keep_alive_impl(Nurse, Patient, args, ret); }
};

View File

@ -15,7 +15,6 @@
#include "descr.h"
#include <array>
#include <limits>
#include <iostream>
NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail)
@ -25,9 +24,13 @@ struct type_info {
PyTypeObject *type;
size_t type_size;
void (*init_holder)(PyObject *, const void *);
std::vector<PyObject *(*)(PyObject *, PyTypeObject *) > implicit_conversions;
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts;
buffer_info *(*get_buffer)(PyObject *, void *) = nullptr;
void *get_buffer_data = nullptr;
/** A simple type never occurs as a (direct or indirect) parent
* of a class that makes use of multiple inheritance */
bool simple_type = true;
};
PYBIND11_NOINLINE inline internals &get_internals() {
@ -72,32 +75,34 @@ PYBIND11_NOINLINE inline internals &get_internals() {
return *internals_ptr;
}
PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type, bool throw_if_missing = true) {
PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) {
auto const &type_dict = get_internals().registered_types_py;
do {
auto it = type_dict.find(type);
if (it != type_dict.end())
return (detail::type_info *) it->second;
type = type->tp_base;
if (!type) {
if (throw_if_missing)
pybind11_fail("pybind11::detail::get_type_info: unable to find type object!");
if (!type)
return nullptr;
}
} while (true);
}
PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp) {
PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp, bool throw_if_missing) {
auto &types = get_internals().registered_types_cpp;
auto it = types.find(std::type_index(tp));
if (it != types.end())
return (detail::type_info *) it->second;
if (throw_if_missing) {
std::string tname = tp.name();
detail::clean_type_id(tname);
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\"");
}
return nullptr;
}
PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp) {
detail::type_info *type_info = get_type_info(tp);
PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) {
detail::type_info *type_info = get_type_info(tp, throw_if_missing);
return handle(type_info ? ((PyObject *) type_info->type) : nullptr);
}
@ -124,7 +129,7 @@ PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail:
auto &instances = get_internals().registered_instances;
auto range = instances.equal_range(ptr);
for (auto it = range.first; it != range.second; ++it) {
auto instance_type = detail::get_type_info(Py_TYPE(it->second), false);
auto instance_type = detail::get_type_info(Py_TYPE(it->second));
if (instance_type && instance_type == type)
return handle((PyObject *) it->second);
}
@ -149,18 +154,56 @@ inline void keep_alive_impl(handle nurse, handle patient);
class type_caster_generic {
public:
PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info)
: typeinfo(get_type_info(type_info)) { }
: typeinfo(get_type_info(type_info, false)) { }
PYBIND11_NOINLINE bool load(handle src, bool convert) {
return load(src, convert, Py_TYPE(src.ptr()));
}
bool load(handle src, bool convert, PyTypeObject *tobj) {
if (!src || !typeinfo)
return false;
if (src.is_none()) {
value = nullptr;
return true;
} else if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) {
value = ((instance<void> *) src.ptr())->value;
return true;
}
if (typeinfo->simple_type) { /* Case 1: no multiple inheritance etc. involved */
/* Check if we can safely perform a reinterpret-style cast */
if (PyType_IsSubtype(tobj, typeinfo->type)) {
value = reinterpret_cast<instance<void> *>(src.ptr())->value;
return true;
}
} else { /* Case 2: multiple inheritance */
/* Check if we can safely perform a reinterpret-style cast */
if (tobj == typeinfo->type) {
value = reinterpret_cast<instance<void> *>(src.ptr())->value;
return true;
}
/* If this is a python class, also check the parents recursively */
auto const &type_dict = get_internals().registered_types_py;
bool new_style_class = PyType_Check(tobj);
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
tuple parents(tobj->tp_bases, true);
for (handle parent : parents) {
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
if (result)
return true;
}
}
/* Try implicit casts */
for (auto &cast : typeinfo->implicit_casts) {
type_caster_generic sub_caster(*cast.first);
if (sub_caster.load(src, convert)) {
value = cast.second(sub_caster.value);
return true;
}
}
}
/* Perform an implicit conversion */
if (convert) {
for (auto &converter : typeinfo->implicit_conversions) {
temp = object(converter(src.ptr(), typeinfo->type), false);
@ -201,7 +244,7 @@ public:
auto it_instances = internals.registered_instances.equal_range(src);
for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
auto instance_type = detail::get_type_info(Py_TYPE(it_i->second), false);
auto instance_type = detail::get_type_info(Py_TYPE(it_i->second));
if (instance_type && instance_type == tinfo)
return handle((PyObject *) it_i->second).inc_ref();
}
@ -262,7 +305,8 @@ template <typename type> class type_caster_base : public type_caster_generic {
public:
static PYBIND11_DESCR name() { return type_descr(_<type>()); }
type_caster_base() : type_caster_generic(typeid(type)) { }
type_caster_base() : type_caster_base(typeid(type)) { }
type_caster_base(const std::type_info &info) : type_caster_generic(info) { }
static handle cast(const itype &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference)
@ -299,10 +343,10 @@ protected:
#else
/* Visual Studio 2015's SFINAE implementation doesn't yet handle the above robustly in all situations.
Use a workaround that only tests for constructibility for now. */
template <typename T = type, typename = typename std::enable_if<std::is_copy_constructible<T>::value>::type>
template <typename T = type, typename = enable_if_t<std::is_copy_constructible<T>::value>>
static Constructor make_copy_constructor(const T *value) {
return [](const void *arg) -> void * { return new T(*((const T *)arg)); }; }
template <typename T = type, typename = typename std::enable_if<std::is_move_constructible<T>::value>::type>
template <typename T = type, typename = enable_if_t<std::is_move_constructible<T>::value>>
static Constructor make_move_constructor(const T *value) {
return [](const void *arg) -> void * { return (void *) new T(std::move(*((T *)arg))); }; }
#endif
@ -343,8 +387,8 @@ public:
template <typename T>
struct type_caster<
T, typename std::enable_if<std::is_integral<T>::value ||
std::is_floating_point<T>::value>::type> {
T, enable_if_t<std::is_integral<T>::value ||
std::is_floating_point<T>::value>> {
typedef typename std::conditional<sizeof(T) <= sizeof(long), long, long long>::type _py_type_0;
typedef typename std::conditional<std::is_signed<T>::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>::type _py_type_1;
typedef typename std::conditional<std::is_floating_point<T>::value, double, _py_type_1>::type py_type;
@ -433,7 +477,7 @@ public:
}
/* Check if this is a C++ type */
if (get_type_info((PyTypeObject *) h.get_type().ptr(), false)) {
if (get_type_info((PyTypeObject *) h.get_type().ptr())) {
value = ((instance<void> *) h.ptr())->value;
return true;
}
@ -658,20 +702,20 @@ public:
return load(src, convert, typename make_index_sequence<sizeof...(Tuple)>::type());
}
template <typename T = itype, typename std::enable_if<
template <typename T = itype, enable_if_t<
!std::is_same<T, args_type>::value &&
!std::is_same<T, args_kwargs_type>::value, int>::type = 0>
!std::is_same<T, args_kwargs_type>::value, int> = 0>
bool load_args(handle args, handle, bool convert) {
return load(args, convert, typename make_index_sequence<sizeof...(Tuple)>::type());
}
template <typename T = itype, typename std::enable_if<std::is_same<T, args_type>::value, int>::type = 0>
template <typename T = itype, enable_if_t<std::is_same<T, args_type>::value, int> = 0>
bool load_args(handle args, handle, bool convert) {
std::get<0>(value).load(args, convert);
return true;
}
template <typename T = itype, typename std::enable_if<std::is_same<T, args_kwargs_type>::value, int>::type = 0>
template <typename T = itype, enable_if_t<std::is_same<T, args_kwargs_type>::value, int> = 0>
bool load_args(handle args, handle kwargs, bool convert) {
std::get<0>(value).load(args, convert);
std::get<1>(value).load(kwargs, convert);
@ -690,11 +734,11 @@ public:
return type_descr(_("Tuple[") + element_names() + _("]"));
}
template <typename ReturnValue, typename Func> typename std::enable_if<!std::is_void<ReturnValue>::value, ReturnValue>::type call(Func &&f) {
template <typename ReturnValue, typename Func> enable_if_t<!std::is_void<ReturnValue>::value, ReturnValue> call(Func &&f) {
return call<ReturnValue>(std::forward<Func>(f), typename make_index_sequence<sizeof...(Tuple)>::type());
}
template <typename ReturnValue, typename Func> typename std::enable_if<std::is_void<ReturnValue>::value, void_type>::type call(Func &&f) {
template <typename ReturnValue, typename Func> enable_if_t<std::is_void<ReturnValue>::value, void_type> call(Func &&f) {
call<ReturnValue>(std::forward<Func>(f), typename make_index_sequence<sizeof...(Tuple)>::type());
return void_type();
}
@ -749,22 +793,56 @@ protected:
/// Type caster for holder types like std::shared_ptr, etc.
template <typename type, typename holder_type> class type_caster_holder : public type_caster_base<type> {
public:
using type_caster_base<type>::cast;
using type_caster_base<type>::typeinfo;
using type_caster_base<type>::value;
using type_caster_base<type>::temp;
using base = type_caster_base<type>;
using base::base;
using base::cast;
using base::typeinfo;
using base::value;
using base::temp;
bool load(handle src, bool convert) {
if (!src || !typeinfo) {
PYBIND11_NOINLINE bool load(handle src, bool convert) {
return load(src, convert, Py_TYPE(src.ptr()));
}
bool load(handle src, bool convert, PyTypeObject *tobj) {
if (!src || !typeinfo)
return false;
} else if (src.is_none()) {
if (src.is_none()) {
value = nullptr;
return true;
} else if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) {
auto inst = (instance<type, holder_type> *) src.ptr();
value = (void *) inst->value;
holder = inst->holder;
return true;
}
if (typeinfo->simple_type) { /* Case 1: no multiple inheritance etc. involved */
/* Check if we can safely perform a reinterpret-style cast */
if (PyType_IsSubtype(tobj, typeinfo->type)) {
auto inst = (instance<type, holder_type> *) src.ptr();
value = (void *) inst->value;
holder = inst->holder;
return true;
}
} else { /* Case 2: multiple inheritance */
/* Check if we can safely perform a reinterpret-style cast */
if (tobj == typeinfo->type) {
auto inst = (instance<type, holder_type> *) src.ptr();
value = (void *) inst->value;
holder = inst->holder;
return true;
}
/* If this is a python class, also check the parents recursively */
auto const &type_dict = get_internals().registered_types_py;
bool new_style_class = PyType_Check(tobj);
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
tuple parents(tobj->tp_bases, true);
for (handle parent : parents) {
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
if (result)
return true;
}
}
if (try_implicit_casts(src, convert))
return true;
}
if (convert) {
@ -774,6 +852,23 @@ public:
return true;
}
}
return false;
}
template <typename T = holder_type, detail::enable_if_t<!std::is_constructible<T, const T &, type*>::value, int> = 0>
bool try_implicit_casts(handle, bool) { return false; }
template <typename T = holder_type, detail::enable_if_t<std::is_constructible<T, const T &, type*>::value, int> = 0>
bool try_implicit_casts(handle src, bool convert) {
for (auto &cast : typeinfo->implicit_casts) {
type_caster_holder sub_caster(*cast.first);
if (sub_caster.load(src, convert)) {
value = cast.second(sub_caster.value);
holder = holder_type(sub_caster.holder, (type *) value);
return true;
}
}
return false;
}
@ -813,12 +908,12 @@ template <> struct handle_type_name<args> { static PYBIND11_DESCR name() { retur
template <> struct handle_type_name<kwargs> { static PYBIND11_DESCR name() { return _("**kwargs"); } };
template <typename type>
struct type_caster<type, typename std::enable_if<std::is_base_of<handle, type>::value>::type> {
struct type_caster<type, enable_if_t<std::is_base_of<handle, type>::value>> {
public:
template <typename T = type, typename std::enable_if<!std::is_base_of<object, T>::value, int>::type = 0>
template <typename T = type, enable_if_t<!std::is_base_of<object, T>::value, int> = 0>
bool load(handle src, bool /* convert */) { value = type(src); return value.check(); }
template <typename T = type, typename std::enable_if<std::is_base_of<object, T>::value, int>::type = 0>
template <typename T = type, enable_if_t<std::is_base_of<object, T>::value, int> = 0>
bool load(handle src, bool /* convert */) { value = type(src, true); return value.check(); }
static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
@ -837,21 +932,21 @@ public:
// must have ref_count() == 1)h
// If any of the above are not satisfied, we fall back to copying.
template <typename T, typename SFINAE = void> struct move_is_plain_type : std::false_type {};
template <typename T> struct move_is_plain_type<T, typename std::enable_if<
template <typename T> struct move_is_plain_type<T, enable_if_t<
!std::is_void<T>::value && !std::is_pointer<T>::value && !std::is_reference<T>::value && !std::is_const<T>::value
>::type> : std::true_type {};
>> : std::true_type { };
template <typename T, typename SFINAE = void> struct move_always : std::false_type {};
template <typename T> struct move_always<T, typename std::enable_if<
template <typename T> struct move_always<T, enable_if_t<
move_is_plain_type<T>::value &&
!std::is_copy_constructible<T>::value && std::is_move_constructible<T>::value &&
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>::value
>::type> : std::true_type {};
>> : std::true_type { };
template <typename T, typename SFINAE = void> struct move_if_unreferenced : std::false_type {};
template <typename T> struct move_if_unreferenced<T, typename std::enable_if<
template <typename T> struct move_if_unreferenced<T, enable_if_t<
move_is_plain_type<T>::value &&
!move_always<T>::value && std::is_move_constructible<T>::value &&
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>::value
>::type> : std::true_type {};
>> : std::true_type { };
template <typename T> using move_never = std::integral_constant<bool, !move_always<T>::value && !move_if_unreferenced<T>::value>;
// Detect whether returning a `type` from a cast on type's type_caster is going to result in a
@ -968,7 +1063,6 @@ template <> inline void cast_safe<void>(object &&) {}
NAMESPACE_END(detail)
template <return_value_policy policy = return_value_policy::automatic_reference,
typename... Args> tuple make_tuple(Args&&... args_) {
const size_t size = sizeof...(Args);
@ -1023,7 +1117,7 @@ struct arg_v : arg {
template <typename T>
arg_v arg::operator=(T &&value) const { return {name, std::forward<T>(value)}; }
/// Alias for backward compatibility -- to be remove in version 2.0
/// Alias for backward compatibility -- to be removed in version 2.0
template <typename /*unused*/> using arg_t = arg_v;
inline namespace literals {
@ -1199,7 +1293,7 @@ unpacking_collector<policy> collect_arguments(Args &&...args) {
"Invalid function call: positional args must precede keywords and ** unpacking; "
"* unpacking must precede ** unpacking"
);
return {std::forward<Args>(args)...};
return { std::forward<Args>(args)... };
}
NAMESPACE_END(detail)

View File

@ -30,6 +30,16 @@
# define PYBIND11_NOINLINE __attribute__ ((noinline))
#endif
#if __cplusplus > 201103L
# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]]
#elif defined(__clang__)
# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason)))
#elif defined(__GNUG__)
# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated))
#elif defined(_MSC_VER)
# define PYBIND11_DEPRECATED(reason) __declspec(deprecated)
#endif
#define PYBIND11_VERSION_MAJOR 1
#define PYBIND11_VERSION_MINOR 9
#define PYBIND11_VERSION_PATCH dev0
@ -79,6 +89,7 @@
#include <unordered_map>
#include <memory>
#include <typeindex>
#include <type_traits>
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
@ -431,14 +442,14 @@ PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used in
template <typename T, typename SFINAE = void> struct format_descriptor { };
template <typename T> struct format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> {
template <typename T> struct format_descriptor<T, detail::enable_if_t<std::is_integral<T>::value>> {
static constexpr const char value[2] =
{ "bBhHiIqQ"[detail::log2(sizeof(T))*2 + (std::is_unsigned<T>::value ? 1 : 0)], '\0' };
static std::string format() { return value; }
};
template <typename T> constexpr const char format_descriptor<
T, typename std::enable_if<std::is_integral<T>::value>::type>::value[2];
T, detail::enable_if_t<std::is_integral<T>::value>>::value[2];
/// RAII wrapper that temporarily clears any Python error state
struct error_scope {

View File

@ -86,11 +86,11 @@ template <size_t...Digits> struct int_to_str<0, Digits...> {
// Ternary description (like std::conditional)
template <bool B, size_t Size1, size_t Size2>
constexpr typename std::enable_if<B, descr<Size1 - 1, 0>>::type _(char const(&text1)[Size1], char const(&)[Size2]) {
constexpr enable_if_t<B, descr<Size1 - 1, 0>> _(char const(&text1)[Size1], char const(&)[Size2]) {
return _(text1);
}
template <bool B, size_t Size1, size_t Size2>
constexpr typename std::enable_if<!B, descr<Size2 - 1, 0>>::type _(char const(&)[Size1], char const(&text2)[Size2]) {
constexpr enable_if_t<!B, descr<Size2 - 1, 0>> _(char const(&)[Size1], char const(&text2)[Size2]) {
return _(text2);
}
@ -164,8 +164,8 @@ PYBIND11_NOINLINE inline descr _(const char *text) {
return descr(text, types);
}
template <bool B> PYBIND11_NOINLINE typename std::enable_if<B, descr>::type _(const char *text1, const char *) { return _(text1); }
template <bool B> PYBIND11_NOINLINE typename std::enable_if<!B, descr>::type _(char const *, const char *text2) { return _(text2); }
template <bool B> PYBIND11_NOINLINE enable_if_t<B, descr> _(const char *text1, const char *) { return _(text1); }
template <bool B> PYBIND11_NOINLINE enable_if_t<!B, descr> _(char const *, const char *text2) { return _(text2); }
template <typename Type> PYBIND11_NOINLINE descr _() {
const std::type_info *types[2] = { &typeid(Type), nullptr };

View File

@ -46,9 +46,9 @@ public:
// type_caster to handle argument copying/forwarding.
template <typename T> class is_eigen_ref {
private:
template<typename Derived> static typename std::enable_if<
template<typename Derived> static enable_if_t<
std::is_same<typename std::remove_const<T>::type, Eigen::Ref<Derived>>::value,
Derived>::type test(const Eigen::Ref<Derived> &);
Derived> test(const Eigen::Ref<Derived> &);
static void test(...);
public:
typedef decltype(test(std::declval<T>())) Derived;
@ -77,7 +77,7 @@ public:
};
template<typename Type>
struct type_caster<Type, typename std::enable_if<is_eigen_dense<Type>::value && !is_eigen_ref<Type>::value>::type> {
struct type_caster<Type, enable_if_t<is_eigen_dense<Type>::value && !is_eigen_ref<Type>::value>> {
typedef typename Type::Scalar Scalar;
static constexpr bool rowMajor = Type::Flags & Eigen::RowMajorBit;
static constexpr bool isVector = Type::IsVectorAtCompileTime;
@ -149,18 +149,18 @@ struct type_caster<Type, typename std::enable_if<is_eigen_dense<Type>::value &&
_("[") + rows() + _(", ") + cols() + _("]]"));
protected:
template <typename T = Type, typename std::enable_if<T::RowsAtCompileTime == Eigen::Dynamic, int>::type = 0>
template <typename T = Type, enable_if_t<T::RowsAtCompileTime == Eigen::Dynamic, int> = 0>
static PYBIND11_DESCR rows() { return _("m"); }
template <typename T = Type, typename std::enable_if<T::RowsAtCompileTime != Eigen::Dynamic, int>::type = 0>
template <typename T = Type, enable_if_t<T::RowsAtCompileTime != Eigen::Dynamic, int> = 0>
static PYBIND11_DESCR rows() { return _<T::RowsAtCompileTime>(); }
template <typename T = Type, typename std::enable_if<T::ColsAtCompileTime == Eigen::Dynamic, int>::type = 0>
template <typename T = Type, enable_if_t<T::ColsAtCompileTime == Eigen::Dynamic, int> = 0>
static PYBIND11_DESCR cols() { return _("n"); }
template <typename T = Type, typename std::enable_if<T::ColsAtCompileTime != Eigen::Dynamic, int>::type = 0>
template <typename T = Type, enable_if_t<T::ColsAtCompileTime != Eigen::Dynamic, int> = 0>
static PYBIND11_DESCR cols() { return _<T::ColsAtCompileTime>(); }
};
template<typename Type>
struct type_caster<Type, typename std::enable_if<is_eigen_dense<Type>::value && is_eigen_ref<Type>::value>::type> {
struct type_caster<Type, enable_if_t<is_eigen_dense<Type>::value && is_eigen_ref<Type>::value>> {
protected:
using Derived = typename std::remove_const<typename is_eigen_ref<Type>::Derived>::type;
using DerivedCaster = type_caster<Derived>;
@ -181,7 +181,7 @@ public:
// type_caster for special matrix types (e.g. DiagonalMatrix): load() is not supported, but we can
// cast them into the python domain by first copying to a regular Eigen::Matrix, then casting that.
template <typename Type>
struct type_caster<Type, typename std::enable_if<is_eigen_base<Type>::value && !is_eigen_ref<Type>::value>::type> {
struct type_caster<Type, enable_if_t<is_eigen_base<Type>::value && !is_eigen_ref<Type>::value>> {
protected:
using Matrix = Eigen::Matrix<typename Type::Scalar, Eigen::Dynamic, Eigen::Dynamic>;
using MatrixCaster = type_caster<Matrix>;
@ -198,7 +198,7 @@ public:
};
template<typename Type>
struct type_caster<Type, typename std::enable_if<is_eigen_sparse<Type>::value>::type> {
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
typedef typename Type::Scalar Scalar;
typedef typename std::remove_reference<decltype(*std::declval<Type>().outerIndexPtr())>::type StorageIndex;
typedef typename Type::Index Index;

View File

@ -486,7 +486,7 @@ public:
};
template <typename T>
struct format_descriptor<T, typename std::enable_if<detail::is_pod_struct<T>::value>::type> {
struct format_descriptor<T, detail::enable_if_t<detail::is_pod_struct<T>::value>> {
static std::string format() {
return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::format();
}
@ -517,7 +517,7 @@ struct is_pod_struct {
!std::is_same<typename std::remove_cv<T>::type, std::complex<double>>::value };
};
template <typename T> struct npy_format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> {
template <typename T> struct npy_format_descriptor<T, enable_if_t<std::is_integral<T>::value>> {
private:
constexpr static const int values[8] = {
npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_,
@ -529,13 +529,13 @@ public:
return object(ptr, true);
pybind11_fail("Unsupported buffer format!");
}
template <typename T2 = T, typename std::enable_if<std::is_signed<T2>::value, int>::type = 0>
template <typename T2 = T, enable_if_t<std::is_signed<T2>::value, int> = 0>
static PYBIND11_DESCR name() { return _("int") + _<sizeof(T)*8>(); }
template <typename T2 = T, typename std::enable_if<!std::is_signed<T2>::value, int>::type = 0>
template <typename T2 = T, enable_if_t<!std::is_signed<T2>::value, int> = 0>
static PYBIND11_DESCR name() { return _("uint") + _<sizeof(T)*8>(); }
};
template <typename T> constexpr const int npy_format_descriptor<
T, typename std::enable_if<std::is_integral<T>::value>::type>::values[8];
T, enable_if_t<std::is_integral<T>::value>>::values[8];
#define DECL_FMT(Type, NumPyName, Name) template<> struct npy_format_descriptor<Type> { \
enum { value = npy_api::NumPyName }; \
@ -568,7 +568,7 @@ struct field_descriptor {
};
template <typename T>
struct npy_format_descriptor<T, typename std::enable_if<is_pod_struct<T>::value>::type> {
struct npy_format_descriptor<T, enable_if_t<is_pod_struct<T>::value>> {
static PYBIND11_DESCR name() { return _("struct"); }
static pybind11::dtype dtype() {
@ -634,9 +634,9 @@ private:
};
template <typename T>
std::string npy_format_descriptor<T, typename std::enable_if<is_pod_struct<T>::value>::type>::format_str;
std::string npy_format_descriptor<T, enable_if_t<is_pod_struct<T>::value>>::format_str;
template <typename T>
PyObject* npy_format_descriptor<T, typename std::enable_if<is_pod_struct<T>::value>::type>::dtype_ptr = nullptr;
PyObject* npy_format_descriptor<T, enable_if_t<is_pod_struct<T>::value>>::dtype_ptr = nullptr;
// Extract name, offset and format descriptor for a struct field
#define PYBIND11_FIELD_DESCRIPTOR(Type, Field) \

View File

@ -10,7 +10,6 @@
#pragma once
#include "pybind11.h"
#include <type_traits>
#if defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type()))

View File

@ -17,6 +17,7 @@
# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted
# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning)
# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
# pragma warning(disable: 4702) // warning C4702: unreachable code
#elif defined(__INTEL_COMPILER)
# pragma warning(push)
# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero
@ -460,8 +461,10 @@ protected:
if (overloads->is_operator)
return handle(Py_NotImplemented).inc_ref().ptr();
std::string msg = "Incompatible " + std::string(overloads->is_constructor ? "constructor" : "function") +
" arguments. The following argument types are supported:\n";
std::string msg = std::string(overloads->name) + "(): incompatible " +
std::string(overloads->is_constructor ? "constructor" : "function") +
" arguments. The following argument types are supported:\n";
int ctr = 0;
for (detail::function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) {
msg += " "+ std::to_string(++ctr) + ". ";
@ -489,7 +492,7 @@ protected:
msg += "\n";
}
msg += " Invoked with: ";
msg += "\nInvoked with: ";
tuple args_(args, true);
for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) {
msg += static_cast<std::string>(static_cast<object>(args_[ti]).str());
@ -574,18 +577,6 @@ public:
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
protected:
void initialize(type_record *rec) {
if (rec->base_type) {
if (rec->base_handle)
pybind11_fail("generic_type: specified base type multiple times!");
rec->base_handle = detail::get_type_handle(*(rec->base_type));
if (!rec->base_handle) {
std::string tname(rec->base_type->name());
detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(rec->name) +
"\" referenced unknown base type \"" + tname + "\"");
}
}
auto &internals = get_internals();
auto tindex = std::type_index(*(rec->type));
@ -615,6 +606,12 @@ protected:
ht_qualname = name;
}
#endif
size_t num_bases = rec->bases.size();
tuple bases(num_bases);
for (size_t i = 0; i < num_bases; ++i)
bases[i] = rec->bases[i];
std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + rec->name)
: std::string(rec->name));
@ -627,11 +624,16 @@ protected:
memcpy((void *) tp_doc, rec->doc, size);
}
/* Danger zone: from now (and until PyType_Ready), make sure to
issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */
object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
auto type = (PyHeapTypeObject*) type_holder.ptr();
if (!type_holder || !name)
pybind11_fail("generic_type: unable to create type object!");
pybind11_fail(std::string(rec->name) + ": Unable to create type object!");
/* Register supplemental type information in C++ dict */
detail::type_info *tinfo = new detail::type_info();
@ -644,8 +646,12 @@ protected:
/* Basic type attributes */
type->ht_type.tp_name = strdup(full_name.c_str());
type->ht_type.tp_basicsize = (ssize_t) rec->instance_size;
type->ht_type.tp_base = (PyTypeObject *) rec->base_handle.ptr();
rec->base_handle.inc_ref();
if (num_bases > 0) {
type->ht_type.tp_base = (PyTypeObject *) ((object) bases[0]).inc_ref().ptr();
type->ht_type.tp_bases = bases.release().ptr();
rec->multiple_inheritance |= num_bases > 1;
}
type->ht_name = name.release().ptr();
@ -676,7 +682,8 @@ protected:
type->ht_type.tp_doc = tp_doc;
if (PyType_Ready(&type->ht_type) < 0)
pybind11_fail("generic_type: PyType_Ready failed!");
pybind11_fail(std::string(rec->name) + ": PyType_Ready failed (" +
detail::error_string() + ")!");
m_ptr = type_holder.ptr();
@ -687,9 +694,23 @@ protected:
if (rec->scope)
rec->scope.attr(handle(type->ht_name)) = *this;
if (rec->multiple_inheritance)
mark_parents_nonsimple(&type->ht_type);
type_holder.release();
}
/// Helper function which tags all parents of a type using mult. inheritance
void mark_parents_nonsimple(PyTypeObject *value) {
tuple t(value->tp_bases, true);
for (handle h : t) {
auto tinfo2 = get_type_info((PyTypeObject *) h.ptr());
if (tinfo2)
tinfo2->simple_type = false;
mark_parents_nonsimple((PyTypeObject *) h.ptr());
}
}
/// Allocate a metaclass on demand (for static properties)
handle metaclass() {
auto &ht_type = ((PyHeapTypeObject *) m_ptr)->ht_type;
@ -809,31 +830,18 @@ 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... 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_base = 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
is_base<T>::value
>;
public:
@ -846,9 +854,6 @@ public:
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)
template <typename... Extra>
@ -862,7 +867,9 @@ public:
record.init_holder = init_holder;
record.dealloc = dealloc;
detail::class_selector<is_base_class, options...>::set_bases(record);
/* Register base classes specified via template arguments to class_, if any */
bool unused[] = { (add_base<options>(record), false)..., false };
(void) unused;
/* Process optional arguments, if any */
detail::process_attributes<Extra...>::init(extra..., &record);
@ -875,6 +882,16 @@ public:
}
}
template <typename Base, detail::enable_if_t<is_base<Base>::value, int> = 0>
static void add_base(detail::type_record &rec) {
rec.add_base(&typeid(Base), [](void *src) -> void * {
return static_cast<Base *>(reinterpret_cast<type *>(src));
});
}
template <typename Base, detail::enable_if_t<!is_base<Base>::value, int> = 0>
static void add_base(detail::type_record &) { }
template <typename Func, typename... Extra>
class_ &def(const char *name_, Func&& f, const Extra&... extra) {
cpp_function cf(std::forward<Func>(f), name(name_),
@ -1016,7 +1033,7 @@ private:
/// Initialize holder object, variant 2: try to construct from existing holder object, if possible
template <typename T = holder_type,
typename std::enable_if<std::is_copy_constructible<T>::value, int>::type = 0>
detail::enable_if_t<std::is_copy_constructible<T>::value, int> = 0>
static void init_holder_helper(instance_type *inst, const holder_type *holder_ptr, const void * /* dummy */) {
if (holder_ptr)
new (&inst->holder) holder_type(*holder_ptr);
@ -1026,7 +1043,7 @@ private:
/// Initialize holder object, variant 3: holder is not copy constructible (e.g. unique_ptr), always initialize from raw pointer
template <typename T = holder_type,
typename std::enable_if<!std::is_copy_constructible<T>::value, int>::type = 0>
detail::enable_if_t<!std::is_copy_constructible<T>::value, int> = 0>
static void init_holder_helper(instance_type *inst, const holder_type * /* unused */, const void * /* dummy */) {
new (&inst->holder) holder_type(inst->value);
}
@ -1196,7 +1213,7 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
typedef detail::iterator_state<Iterator, Sentinel, false, Policy> state;
if (!detail::get_type_info(typeid(state))) {
if (!detail::get_type_info(typeid(state), false)) {
class_<state>(handle(), "iterator")
.def("__iter__", [](state &s) -> state& { return s; })
.def("__next__", [](state &s) -> ValueType {
@ -1221,7 +1238,7 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) {
typedef detail::iterator_state<Iterator, Sentinel, true, Policy> state;
if (!detail::get_type_info(typeid(state))) {
if (!detail::get_type_info(typeid(state), false)) {
class_<state>(handle(), "iterator")
.def("__iter__", [](state &s) -> state& { return s; })
.def("__next__", [](state &s) -> KeyType {

View File

@ -43,10 +43,8 @@ public:
bool is_none() const { return m_ptr == Py_None; }
template <typename T> T cast() const;
template <return_value_policy policy = return_value_policy::automatic_reference, typename ... Args>
#if __cplusplus > 201103L
[[deprecated("call(...) was deprecated in favor of operator()(...)")]]
#endif
object call(Args&&... args) const;
PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)")
object call(Args&&... args) const;
template <return_value_policy policy = return_value_policy::automatic_reference, typename ... Args>
object operator()(Args&&... args) const;
operator bool() const { return m_ptr != nullptr; }
@ -492,7 +490,7 @@ class int_ : public object {
public:
PYBIND11_OBJECT_DEFAULT(int_, object, PYBIND11_LONG_CHECK)
template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
int_(T value) {
if (sizeof(T) <= sizeof(long)) {
if (std::is_signed<T>::value)
@ -509,7 +507,7 @@ public:
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
operator T() const {
if (sizeof(T) <= sizeof(long)) {
if (std::is_signed<T>::value)
@ -614,7 +612,7 @@ public:
}
size_t size() const { return (size_t) PyList_Size(m_ptr); }
detail::list_accessor operator[](size_t index) const { return detail::list_accessor(*this, index); }
void append(const object &object) const { PyList_Append(m_ptr, object.ptr()); }
void append(handle h) const { PyList_Append(m_ptr, h.ptr()); }
};
class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) };

View File

@ -112,7 +112,7 @@ template <typename Type, typename Value> struct list_caster {
}
template <typename T = Type,
typename std::enable_if<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int>::type = 0>
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
void reserve_maybe(list l, Type *) { value.reserve(l.size()); }
void reserve_maybe(list, void *) { }

View File

@ -12,8 +12,6 @@
#include "common.h"
#include "operators.h"
#include <type_traits>
#include <utility>
#include <algorithm>
#include <sstream>
@ -42,20 +40,20 @@ struct is_comparable : std::false_type { };
/* For non-map data structures, check whether operator== can be instantiated */
template <typename T>
struct is_comparable<
T, typename std::enable_if<container_traits<T>::is_element &&
container_traits<T>::is_comparable>::type>
T, enable_if_t<container_traits<T>::is_element &&
container_traits<T>::is_comparable>>
: std::true_type { };
/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */
template <typename T>
struct is_comparable<T, typename std::enable_if<container_traits<T>::is_vector>::type> {
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> {
static constexpr const bool value =
is_comparable<typename T::value_type>::value;
};
/* For pairs, recursively check the two data types */
template <typename T>
struct is_comparable<T, typename std::enable_if<container_traits<T>::is_pair>::type> {
struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> {
static constexpr const bool value =
is_comparable<typename T::first_type>::value &&
is_comparable<typename T::second_type>::value;
@ -66,13 +64,13 @@ template <typename, typename, typename... Args> void vector_if_copy_constructibl
template <typename, typename, typename... Args> void vector_if_equal_operator(const Args&...) { }
template <typename, typename, typename... Args> void vector_if_insertion_operator(const Args&...) { }
template<typename Vector, typename Class_, typename std::enable_if<std::is_copy_constructible<typename Vector::value_type>::value, int>::type = 0>
template<typename Vector, typename Class_, enable_if_t<std::is_copy_constructible<typename Vector::value_type>::value, int> = 0>
void vector_if_copy_constructible(Class_ &cl) {
cl.def(pybind11::init<const Vector &>(),
"Copy constructor");
}
template<typename Vector, typename Class_, typename std::enable_if<is_comparable<Vector>::value, int>::type = 0>
template<typename Vector, typename Class_, enable_if_t<is_comparable<Vector>::value, int> = 0>
void vector_if_equal_operator(Class_ &cl) {
using T = typename Vector::value_type;
@ -378,7 +376,7 @@ template <typename Map, typename Class_, typename... Args> void map_if_copy_assi
);
}
template<typename Map, typename Class_, typename std::enable_if<!std::is_copy_assignable<typename Map::mapped_type>::value, int>::type = 0>
template<typename Map, typename Class_, enable_if_t<!std::is_copy_assignable<typename Map::mapped_type>::value, int> = 0>
void map_if_copy_assignable(Class_ &cl) {
using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type;

View File

@ -29,9 +29,9 @@ set(PYBIND11_TEST_FILES
test_pickling.cpp
test_python_types.cpp
test_sequences_and_iterators.cpp
test_smart_ptr.cpp
test_stl_binders.cpp
test_virtual_functions.cpp
test_multiple_inheritance.cpp
)
string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}")

View File

@ -83,7 +83,7 @@ def test_cpp_function_roundtrip():
with pytest.raises(TypeError) as excinfo:
test_dummy_function(dummy_function2)
assert "Incompatible function arguments" in str(excinfo.value)
assert "incompatible function arguments" in str(excinfo.value)
with pytest.raises(TypeError) as excinfo:
test_dummy_function(lambda x, y: x + y)

View File

@ -61,7 +61,7 @@ test_initializer inheritance([](py::module &m) {
.def(py::init<std::string>());
/* Another way of declaring a subclass relationship: reference parent's C++ type */
py::class_<Rabbit>(m, "Rabbit", py::base<Pet>())
py::class_<Rabbit, Pet>(m, "Rabbit")
.def(py::init<std::string>());
/* And another: list parent in class template arguments */

View File

@ -24,9 +24,10 @@ def test_inheritance(msg):
with pytest.raises(TypeError) as excinfo:
dog_bark(polly)
assert msg(excinfo.value) == """
Incompatible function arguments. The following argument types are supported:
dog_bark(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.Dog) -> str
Invoked with: <m.Pet object at 0>
Invoked with: <m.Pet object at 0>
"""

View File

@ -65,17 +65,19 @@ def test_no_id(capture, msg):
with pytest.raises(TypeError) as excinfo:
get_element(None)
assert msg(excinfo.value) == """
Incompatible function arguments. The following argument types are supported:
get_element(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.issues.ElementA) -> int
Invoked with: None
Invoked with: None
"""
with pytest.raises(TypeError) as excinfo:
expect_int(5.2)
assert msg(excinfo.value) == """
Incompatible function arguments. The following argument types are supported:
expect_int(): incompatible function arguments. The following argument types are supported:
1. (arg0: int) -> int
Invoked with: 5.2
Invoked with: 5.2
"""
assert expect_float(12) == 12
@ -90,10 +92,11 @@ def test_str_issue(msg):
with pytest.raises(TypeError) as excinfo:
str(StrIssue("no", "such", "constructor"))
assert msg(excinfo.value) == """
Incompatible constructor arguments. The following argument types are supported:
__init__(): incompatible constructor arguments. The following argument types are supported:
1. m.issues.StrIssue(arg0: int)
2. m.issues.StrIssue()
Invoked with: no, such, constructor
Invoked with: no, such, constructor
"""

View File

@ -35,9 +35,10 @@ def test_named_arguments(msg):
# noinspection PyArgumentList
kw_func2(x=5, y=10, z=12)
assert msg(excinfo.value) == """
Incompatible function arguments. The following argument types are supported:
kw_func2(): incompatible function arguments. The following argument types are supported:
1. (x: int=100, y: int=200) -> str
Invoked with:
Invoked with:
"""
assert kw_func4() == "{13 17}"

View File

@ -0,0 +1,85 @@
/*
tests/test_multiple_inheritance.cpp -- multiple inheritance,
implicit MI casts
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>);
struct Base1 {
Base1(int i) : i(i) { }
int foo() { return i; }
int i;
};
struct Base2 {
Base2(int i) : i(i) { }
int bar() { return i; }
int i;
};
struct Base12 : Base1, Base2 {
Base12(int i, int j) : Base1(i), Base2(j) { }
};
struct MIType : Base12 {
MIType(int i, int j) : Base12(i, j) { }
};
test_initializer multiple_inheritance([](py::module &m) {
py::class_<Base1>(m, "Base1")
.def(py::init<int>())
.def("foo", &Base1::foo);
py::class_<Base2>(m, "Base2")
.def(py::init<int>())
.def("bar", &Base2::bar);
py::class_<Base12, Base1, Base2>(m, "Base12");
py::class_<MIType, Base12>(m, "MIType")
.def(py::init<int, int>());
});
/* Test the case where not all base classes are specified,
and where pybind11 requires the py::multiple_inheritance
flag to perform proper casting between types */
struct Base1a {
Base1a(int i) : i(i) { }
int foo() { return i; }
int i;
};
struct Base2a {
Base2a(int i) : i(i) { }
int bar() { return i; }
int i;
};
struct Base12a : Base1a, Base2a {
Base12a(int i, int j) : Base1a(i), Base2a(j) { }
};
test_initializer multiple_inheritance_nonexplicit([](py::module &m) {
py::class_<Base1a, std::shared_ptr<Base1a>>(m, "Base1a")
.def(py::init<int>())
.def("foo", &Base1a::foo);
py::class_<Base2a, std::shared_ptr<Base2a>>(m, "Base2a")
.def(py::init<int>())
.def("bar", &Base2a::bar);
py::class_<Base12a, /* Base1 missing */ Base2a,
std::shared_ptr<Base12a>>(m, "Base12a", py::multiple_inheritance())
.def(py::init<int, int>());
m.def("bar_base2a", [](Base2a *b) { return b->bar(); });
m.def("bar_base2a_sharedptr", [](std::shared_ptr<Base2a> b) { return b->bar(); });
});

View File

@ -0,0 +1,65 @@
import pytest
def test_multiple_inheritance_cpp(msg):
from pybind11_tests import MIType
mt = MIType(3, 4)
assert mt.foo() == 3
assert mt.bar() == 4
def test_multiple_inheritance_mix1(msg):
from pybind11_tests import Base2
class Base1:
def __init__(self, i):
self.i = i
def foo(self):
return self.i
class MITypePy(Base1, Base2):
def __init__(self, i, j):
Base1.__init__(self, i)
Base2.__init__(self, j)
mt = MITypePy(3, 4)
assert mt.foo() == 3
assert mt.bar() == 4
def test_multiple_inheritance_mix2(msg):
from pybind11_tests import Base1
class Base2:
def __init__(self, i):
self.i = i
def bar(self):
return self.i
class MITypePy(Base1, Base2):
def __init__(self, i, j):
Base1.__init__(self, i)
Base2.__init__(self, j)
mt = MITypePy(3, 4)
assert mt.foo() == 3
assert mt.bar() == 4
def test_multiple_inheritance_virtbase(msg):
from pybind11_tests import Base12a, bar_base2a, bar_base2a_sharedptr
class MITypePy(Base12a):
def __init__(self, i, j):
Base12a.__init__(self, i, j)
mt = MITypePy(3, 4)
assert mt.bar() == 4
assert bar_base2a(mt) == 4
assert bar_base2a_sharedptr(mt) == 4

View File

@ -35,9 +35,10 @@ def test_pointers(msg):
with pytest.raises(TypeError) as excinfo:
get_void_ptr_value([1, 2, 3]) # This should not work
assert msg(excinfo.value) == """
Incompatible function arguments. The following argument types are supported:
get_void_ptr_value(): incompatible function arguments. The following argument types are supported:
1. (arg0: capsule) -> int
Invoked with: [1, 2, 3]
Invoked with: [1, 2, 3]
"""
assert return_null_str() is None