diff --git a/README.md b/README.md index baefb2baf..93e30dc69 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/advanced.rst b/docs/advanced.rst index b70c593f4..9659709d0 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -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``. - -``opaque`` 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`` into an opaque type, add +the declaration .. code-block:: cpp - m.def("append_1", [](py::opaque> &v) { append_1(v); }); + PYBIND11_MAKE_OPAQUE(std::vector); + +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``. 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_>(m, "IntVector") + .def(py::init<>()) + .def("clear", &std::vector::clear) + .def("pop_back", &std::vector::pop_back) + .def("__len__", [](const std::vector &v) { return v.size(); }) + .def("__iter__", [](std::vector &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:: diff --git a/docs/basics.rst b/docs/basics.rst index f81c103b3..cf39844ee 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -117,7 +117,7 @@ example can be compiled using the following command .. code-block:: bash - $ c++ -O3 -shared -std=c++11 -I /include `python-config --cflags --ldflags --libs` example.cpp -o example.so + $ c++ -O3 -shared -std=c++11 -I /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 97b4d1fe9..754424e78 100644 --- a/example/example14.cpp +++ b/example/example14.cpp @@ -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_>(m, "StringList") + py::class_(m, "StringList") .def(py::init<>()) - .def("push_back", [](py::opaque &l, const std::string &str) { l->push_back(str); }) - .def("pop_back", [](py::opaque &l) { l->pop_back(); }) - .def("back", [](py::opaque &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_(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 ClassWithSTLVecProperty:: *) - &ClassWithSTLVecProperty::stringList); + .def_readwrite("stringList", &ClassWithSTLVecProperty::stringList); - m.def("print_opaque_list", [](py::opaque &_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 95d014d6e..6bca45b7a 100644 --- a/example/example14.py +++ b/example/example14.py @@ -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) diff --git a/example/example14.ref b/example/example14.ref index 63c46b7ec..7c67b386c 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'] + +Opaque list: [some value] diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 5d7b99dfe..f7ba896b7 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -17,21 +17,6 @@ #include NAMESPACE_BEGIN(pybind11) - -/// Thin wrapper type used to treat certain data types as opaque (e.g. STL vectors, etc.) -template class opaque { -public: - template opaque(Args&&... args) : value(std::forward(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::type>::type>::type; /// Generic type caster for objects stored on the heap -template class type_caster : public type_caster_generic { +template class type_caster_base : public type_caster_generic { public: static PYBIND11_DESCR name() { return type_descr(_()); } - 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 class type_caster> : public type_caster { +template class type_caster : public type_caster_base { }; + +template class type_caster> : public type_caster_base { public: static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { - return type_caster::cast(&src.get(), policy, parent); + return type_caster_base::cast(&src.get(), policy, parent); } template using cast_op_type = std::reference_wrapper; operator std::reference_wrapper() { return std::ref(*((type *) this->value)); } @@ -461,12 +448,12 @@ protected: template class type_caster> { public: static handle cast(std::unique_ptr &&src, return_value_policy policy, handle parent) { - handle result = type_caster::cast(src.get(), policy, parent); + handle result = type_caster_base::cast(src.get(), policy, parent); if (result) src.release(); return result; } - static PYBIND11_DESCR name() { return type_caster::name(); } + static PYBIND11_DESCR name() { return type_caster_base::name(); } }; template <> class type_caster { @@ -672,14 +659,14 @@ protected: }; /// Type caster for holder types like std::shared_ptr, etc. -template class type_caster_holder : public type_caster { +template class type_caster_holder : public type_caster_base { public: - using type_caster::cast; - using type_caster::typeinfo; - using type_caster::value; - using type_caster::temp; - using type_caster::copy_constructor; - using type_caster::move_constructor; + using type_caster_base::cast; + using type_caster_base::typeinfo; + using type_caster_base::value; + using type_caster_base::temp; + using type_caster_base::copy_constructor; + using type_caster_base::move_constructor; bool load(handle src, bool convert) { if (!src || !typeinfo) { @@ -790,4 +777,9 @@ template inline object handle::call(Args&&... args) const { return result; } +#define PYBIND11_MAKE_OPAQUE(Type) \ + namespace pybind11 { namespace detail { \ + template<> class type_caster : public type_caster_base { }; \ + }} + NAMESPACE_END(pybind11)