opaque type redesign

This commit is contained in:
Wenzel Jakob 2016-04-28 16:25:24 +02:00
parent f64feaf3e4
commit 06f56ee1e9
7 changed files with 73 additions and 53 deletions

View File

@ -32,6 +32,8 @@ simpler binding code in many common situations.
Tutorial and reference documentation is provided at
[http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest).
A PDF version of the manual is available
[here](https://media.readthedocs.org/pdf/pybind11/latest/pybind11.pdf).
## Core features
pybind11 can map the following core C++ features to Python

View File

@ -1203,7 +1203,7 @@ Suppose we bind the following function
v.push_back(1);
}
and call it as follows from Python:
and call it from Python, the following happens:
.. code-block:: python
@ -1244,21 +1244,36 @@ In this case, properties can be read and written in their entirety. However, an
>>> print(m.contents)
[5, 6]
To deal with both of the above situations, pybind11 contains a simple template
wrapper class named ``opaque<T>``.
``opaque<T>`` disables pybind11's template-based conversion machinery for
``T`` and can be used to treat STL types as opaque objects, whose contents are
never inspected or extracted (thus, they can be passed by reference).
The downside of this approach is that it the binding code becomes a bit more
wordy. The above function can be bound using the following wrapper code:
To deal with both of the above situations, pybind11 provides a macro named
``PYBIND11_MAKE_OPAQUE(T)`` that disables the template-based conversion
machinery of types, thus rendering them *opaque*. The contents of opaque
objects are never inspected or extracted, hence they can be passed by
reference. For instance, to turn ``std::vector<int>`` into an opaque type, add
the declaration
.. code-block:: cpp
m.def("append_1", [](py::opaque<std::vector<int>> &v) { append_1(v); });
PYBIND11_MAKE_OPAQUE(std::vector<int>);
before any binding code (e.g. invocations to ``class_::def()``, etc). This
macro must be specified at the top level, since instantiates a partial template
overload. If your binding code consists of multiple compilation units, it must
be present in every file preceding any usage of ``std::vector<int>``. Opaque
types must also have a corresponding ``class_`` declaration to associate them
with a name in Python, and to define a set of available operations:
.. code-block:: cpp
py::class_<std::vector<int>>(m, "IntVector")
.def(py::init<>())
.def("clear", &std::vector<int>::clear)
.def("pop_back", &std::vector<int>::pop_back)
.def("__len__", [](const std::vector<int> &v) { return v.size(); })
.def("__iter__", [](std::vector<int> &v) {
return py::make_iterator(v.begin(), v.end());
}, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */
// ....
Opaque types must also have a dedicated ``class_`` declaration to define a
set of admissible operations.
.. seealso::

View File

@ -117,7 +117,7 @@ example can be compiled using the following command
.. code-block:: bash
$ c++ -O3 -shared -std=c++11 -I <path-to-pybind11>/include `python-config --cflags --ldflags --libs` example.cpp -o example.so
$ c++ -O3 -shared -std=c++11 -I <path-to-pybind11>/include `python-config --cflags --ldflags` example.cpp -o example.so
In general, it is advisable to include several additional build parameters
that can considerably reduce the size of the created binary. Refer to section

View File

@ -18,22 +18,26 @@ public:
StringList stringList;
};
/* IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures */
PYBIND11_MAKE_OPAQUE(StringList);
void init_ex14(py::module &m) {
py::class_<py::opaque<StringList>>(m, "StringList")
py::class_<StringList>(m, "StringList")
.def(py::init<>())
.def("push_back", [](py::opaque<StringList> &l, const std::string &str) { l->push_back(str); })
.def("pop_back", [](py::opaque<StringList> &l) { l->pop_back(); })
.def("back", [](py::opaque<StringList> &l) { return l->back(); });
.def("pop_back", &StringList::pop_back)
/* There are multiple versions of push_back(), etc. Select the right ones. */
.def("push_back", (void (StringList::*)(const std::string &)) &StringList::push_back)
.def("back", (std::string &(StringList::*)()) &StringList::back)
.def("__len__", [](const StringList &v) { return v.size(); })
.def("__iter__", [](StringList &v) {
return py::make_iterator(v.begin(), v.end());
}, py::keep_alive<0, 1>());
py::class_<ClassWithSTLVecProperty>(m, "ClassWithSTLVecProperty")
.def(py::init<>())
/* Need to cast properties to opaque types to avoid pybind11-internal
STL conversion code from becoming active */
.def_readwrite("stringList", (py::opaque<StringList> ClassWithSTLVecProperty:: *)
&ClassWithSTLVecProperty::stringList);
.def_readwrite("stringList", &ClassWithSTLVecProperty::stringList);
m.def("print_opaque_list", [](py::opaque<StringList> &_l) {
StringList &l = _l;
m.def("print_opaque_list", [](const StringList &l) {
std::cout << "Opaque list: [";
bool first = true;
for (auto entry : l) {

View File

@ -16,6 +16,8 @@ l.push_back("Element 1")
l.push_back("Element 2")
print_opaque_list(l)
print("Back element is %s" % l.back())
for i, k in enumerate(l):
print("%i/%i : %s" % (i + 1, len(l), k))
l.pop_back()
print_opaque_list(l)
@ -36,4 +38,6 @@ print_null_str(return_null_str())
#####
print(return_unique_ptr())
ptr = return_unique_ptr()
print(ptr)
print_opaque_list(ptr)

View File

@ -1,9 +1,12 @@
Opaque list: [Element 1, Element 2]
Back element is Element 2
1/2 : Element 1
2/2 : Element 2
Opaque list: [Element 1]
Opaque list: []
Opaque list: [Element 1, Element 3]
Got void ptr : 1234
None
Got null str : 0
[u'some value']
<example.StringList object at 0x104a47500>
Opaque list: [some value]

View File

@ -17,21 +17,6 @@
#include <limits>
NAMESPACE_BEGIN(pybind11)
/// Thin wrapper type used to treat certain data types as opaque (e.g. STL vectors, etc.)
template <typename Type> class opaque {
public:
template <typename... Args> opaque(Args&&... args) : value(std::forward<Args>(args)...) { }
operator Type&() { return value; }
operator const Type&() const { return value; }
operator Type*() { return &value; }
operator const Type*() const { return &value; }
Type* operator->() { return &value; }
const Type* operator->() const { return &value; }
private:
Type value;
};
NAMESPACE_BEGIN(detail)
/// Additional type information which does not fit into the PyTypeObject
@ -238,11 +223,11 @@ using cast_op_type = typename std::conditional<std::is_pointer<typename std::rem
typename std::add_lvalue_reference<typename intrinsic_type<T>::type>::type>::type;
/// Generic type caster for objects stored on the heap
template <typename type, typename Enable = void> class type_caster : public type_caster_generic {
template <typename type> class type_caster_base : public type_caster_generic {
public:
static PYBIND11_DESCR name() { return type_descr(_<type>()); }
type_caster() : type_caster_generic(typeid(type)) { }
type_caster_base() : type_caster_generic(typeid(type)) { }
static handle cast(const type &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference)
@ -277,10 +262,12 @@ protected:
static void *move_constructor(const void *) { return nullptr; }
};
template <typename type> class type_caster<std::reference_wrapper<type>> : public type_caster<type> {
template <typename type, typename SFINAE = void> class type_caster : public type_caster_base<type> { };
template <typename type> class type_caster<std::reference_wrapper<type>> : public type_caster_base<type> {
public:
static handle cast(const std::reference_wrapper<type> &src, return_value_policy policy, handle parent) {
return type_caster<type>::cast(&src.get(), policy, parent);
return type_caster_base<type>::cast(&src.get(), policy, parent);
}
template <typename T> using cast_op_type = std::reference_wrapper<type>;
operator std::reference_wrapper<type>() { return std::ref(*((type *) this->value)); }
@ -461,12 +448,12 @@ protected:
template <typename type> class type_caster<std::unique_ptr<type>> {
public:
static handle cast(std::unique_ptr<type> &&src, return_value_policy policy, handle parent) {
handle result = type_caster<type>::cast(src.get(), policy, parent);
handle result = type_caster_base<type>::cast(src.get(), policy, parent);
if (result)
src.release();
return result;
}
static PYBIND11_DESCR name() { return type_caster<type>::name(); }
static PYBIND11_DESCR name() { return type_caster_base<type>::name(); }
};
template <> class type_caster<std::wstring> {
@ -672,14 +659,14 @@ protected:
};
/// Type caster for holder types like std::shared_ptr, etc.
template <typename type, typename holder_type> class type_caster_holder : public type_caster<type> {
template <typename type, typename holder_type> class type_caster_holder : public type_caster_base<type> {
public:
using type_caster<type>::cast;
using type_caster<type>::typeinfo;
using type_caster<type>::value;
using type_caster<type>::temp;
using type_caster<type>::copy_constructor;
using type_caster<type>::move_constructor;
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 type_caster_base<type>::copy_constructor;
using type_caster_base<type>::move_constructor;
bool load(handle src, bool convert) {
if (!src || !typeinfo) {
@ -790,4 +777,9 @@ template <typename... Args> inline object handle::call(Args&&... args) const {
return result;
}
#define PYBIND11_MAKE_OPAQUE(Type) \
namespace pybind11 { namespace detail { \
template<> class type_caster<Type> : public type_caster_base<Type> { }; \
}}
NAMESPACE_END(pybind11)