mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 13:15:12 +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
|
Tutorial and reference documentation is provided at
|
||||||
[http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest).
|
[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
|
## Core features
|
||||||
pybind11 can map the following core C++ features to Python
|
pybind11 can map the following core C++ features to Python
|
||||||
|
@ -1203,7 +1203,7 @@ Suppose we bind the following function
|
|||||||
v.push_back(1);
|
v.push_back(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
and call it as follows from Python:
|
and call it from Python, the following happens:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@ -1244,21 +1244,36 @@ In this case, properties can be read and written in their entirety. However, an
|
|||||||
>>> print(m.contents)
|
>>> print(m.contents)
|
||||||
[5, 6]
|
[5, 6]
|
||||||
|
|
||||||
To deal with both of the above situations, pybind11 contains a simple template
|
To deal with both of the above situations, pybind11 provides a macro named
|
||||||
wrapper class named ``opaque<T>``.
|
``PYBIND11_MAKE_OPAQUE(T)`` that disables the template-based conversion
|
||||||
|
machinery of types, thus rendering them *opaque*. The contents of opaque
|
||||||
``opaque<T>`` disables pybind11's template-based conversion machinery for
|
objects are never inspected or extracted, hence they can be passed by
|
||||||
``T`` and can be used to treat STL types as opaque objects, whose contents are
|
reference. For instance, to turn ``std::vector<int>`` into an opaque type, add
|
||||||
never inspected or extracted (thus, they can be passed by reference).
|
the declaration
|
||||||
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:
|
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. 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::
|
.. seealso::
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ example can be compiled using the following command
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
In general, it is advisable to include several additional build parameters
|
||||||
that can considerably reduce the size of the created binary. Refer to section
|
that can considerably reduce the size of the created binary. Refer to section
|
||||||
|
@ -18,22 +18,26 @@ public:
|
|||||||
StringList stringList;
|
StringList stringList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures */
|
||||||
|
PYBIND11_MAKE_OPAQUE(StringList);
|
||||||
|
|
||||||
void init_ex14(py::module &m) {
|
void init_ex14(py::module &m) {
|
||||||
py::class_<py::opaque<StringList>>(m, "StringList")
|
py::class_<StringList>(m, "StringList")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("push_back", [](py::opaque<StringList> &l, const std::string &str) { l->push_back(str); })
|
.def("pop_back", &StringList::pop_back)
|
||||||
.def("pop_back", [](py::opaque<StringList> &l) { l->pop_back(); })
|
/* There are multiple versions of push_back(), etc. Select the right ones. */
|
||||||
.def("back", [](py::opaque<StringList> &l) { return l->back(); });
|
.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")
|
py::class_<ClassWithSTLVecProperty>(m, "ClassWithSTLVecProperty")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
/* Need to cast properties to opaque types to avoid pybind11-internal
|
.def_readwrite("stringList", &ClassWithSTLVecProperty::stringList);
|
||||||
STL conversion code from becoming active */
|
|
||||||
.def_readwrite("stringList", (py::opaque<StringList> ClassWithSTLVecProperty:: *)
|
|
||||||
&ClassWithSTLVecProperty::stringList);
|
|
||||||
|
|
||||||
m.def("print_opaque_list", [](py::opaque<StringList> &_l) {
|
m.def("print_opaque_list", [](const StringList &l) {
|
||||||
StringList &l = _l;
|
|
||||||
std::cout << "Opaque list: [";
|
std::cout << "Opaque list: [";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto entry : l) {
|
for (auto entry : l) {
|
||||||
|
@ -16,6 +16,8 @@ l.push_back("Element 1")
|
|||||||
l.push_back("Element 2")
|
l.push_back("Element 2")
|
||||||
print_opaque_list(l)
|
print_opaque_list(l)
|
||||||
print("Back element is %s" % l.back())
|
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()
|
l.pop_back()
|
||||||
print_opaque_list(l)
|
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]
|
Opaque list: [Element 1, Element 2]
|
||||||
Back element is Element 2
|
Back element is Element 2
|
||||||
|
1/2 : Element 1
|
||||||
|
2/2 : Element 2
|
||||||
Opaque list: [Element 1]
|
Opaque list: [Element 1]
|
||||||
Opaque list: []
|
Opaque list: []
|
||||||
Opaque list: [Element 1, Element 3]
|
Opaque list: [Element 1, Element 3]
|
||||||
Got void ptr : 1234
|
Got void ptr : 1234
|
||||||
None
|
None
|
||||||
Got null str : 0
|
Got null str : 0
|
||||||
[u'some value']
|
<example.StringList object at 0x104a47500>
|
||||||
|
Opaque list: [some value]
|
||||||
|
@ -17,21 +17,6 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(pybind11)
|
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)
|
NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
/// Additional type information which does not fit into the PyTypeObject
|
/// 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;
|
typename std::add_lvalue_reference<typename intrinsic_type<T>::type>::type>::type;
|
||||||
|
|
||||||
/// Generic type caster for objects stored on the heap
|
/// 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:
|
public:
|
||||||
static PYBIND11_DESCR name() { return type_descr(_<type>()); }
|
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) {
|
static handle cast(const type &src, return_value_policy policy, handle parent) {
|
||||||
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference)
|
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; }
|
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:
|
public:
|
||||||
static handle cast(const std::reference_wrapper<type> &src, return_value_policy policy, handle parent) {
|
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>;
|
template <typename T> using cast_op_type = std::reference_wrapper<type>;
|
||||||
operator std::reference_wrapper<type>() { return std::ref(*((type *) this->value)); }
|
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>> {
|
template <typename type> class type_caster<std::unique_ptr<type>> {
|
||||||
public:
|
public:
|
||||||
static handle cast(std::unique_ptr<type> &&src, return_value_policy policy, handle parent) {
|
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)
|
if (result)
|
||||||
src.release();
|
src.release();
|
||||||
return result;
|
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> {
|
template <> class type_caster<std::wstring> {
|
||||||
@ -672,14 +659,14 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Type caster for holder types like std::shared_ptr, etc.
|
/// 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:
|
public:
|
||||||
using type_caster<type>::cast;
|
using type_caster_base<type>::cast;
|
||||||
using type_caster<type>::typeinfo;
|
using type_caster_base<type>::typeinfo;
|
||||||
using type_caster<type>::value;
|
using type_caster_base<type>::value;
|
||||||
using type_caster<type>::temp;
|
using type_caster_base<type>::temp;
|
||||||
using type_caster<type>::copy_constructor;
|
using type_caster_base<type>::copy_constructor;
|
||||||
using type_caster<type>::move_constructor;
|
using type_caster_base<type>::move_constructor;
|
||||||
|
|
||||||
bool load(handle src, bool convert) {
|
bool load(handle src, bool convert) {
|
||||||
if (!src || !typeinfo) {
|
if (!src || !typeinfo) {
|
||||||
@ -790,4 +777,9 @@ template <typename... Args> inline object handle::call(Args&&... args) const {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define PYBIND11_MAKE_OPAQUE(Type) \
|
||||||
|
namespace pybind11 { namespace detail { \
|
||||||
|
template<> class type_caster<Type> : public type_caster_base<Type> { }; \
|
||||||
|
}}
|
||||||
|
|
||||||
NAMESPACE_END(pybind11)
|
NAMESPACE_END(pybind11)
|
||||||
|
Loading…
Reference in New Issue
Block a user