mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 22:52:01 +00:00
opaque type redesign
This commit is contained in:
parent
f64feaf3e4
commit
06f56ee1e9
@ -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
|
||||
|
@ -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::
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user