opaque type redesign
diff --git a/README.md b/README.md
index baefb2b..93e30dc 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,8 @@
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
diff --git a/docs/advanced.rst b/docs/advanced.rst
index b70c593..9659709 100644
--- a/docs/advanced.rst
+++ b/docs/advanced.rst
@@ -1203,7 +1203,7 @@
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 @@
>>> 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>);
-Opaque types must also have a dedicated ``class_`` declaration to define a
-set of admissible operations.
+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 */
+ // ....
+
.. seealso::
diff --git a/docs/basics.rst b/docs/basics.rst
index f81c103..cf39844 100644
--- a/docs/basics.rst
+++ b/docs/basics.rst
@@ -117,7 +117,7 @@
.. 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
diff --git a/example/example14.cpp b/example/example14.cpp
index 97b4d1f..754424e 100644
--- a/example/example14.cpp
+++ b/example/example14.cpp
@@ -18,22 +18,26 @@
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) {
diff --git a/example/example14.py b/example/example14.py
index 95d014d..6bca45b 100644
--- a/example/example14.py
+++ b/example/example14.py
@@ -16,6 +16,8 @@
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(return_unique_ptr())
+ptr = return_unique_ptr()
+print(ptr)
+print_opaque_list(ptr)
diff --git a/example/example14.ref b/example/example14.ref
index 63c46b7..7c67b38 100644
--- a/example/example14.ref
+++ b/example/example14.ref
@@ -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]
diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h
index 5d7b99d..f7ba896 100644
--- a/include/pybind11/cast.h
+++ b/include/pybind11/cast.h
@@ -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 @@
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 @@
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 @@
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 @@
};
/// 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 @@
return result;
}
+#define PYBIND11_MAKE_OPAQUE(Type) \
+ namespace pybind11 { namespace detail { \
+ template<> class type_caster<Type> : public type_caster_base<Type> { }; \
+ }}
+
NAMESPACE_END(pybind11)