support for opaque types

This commit is contained in:
Wenzel Jakob 2016-03-15 15:05:40 +01:00
parent a3e34f4a5f
commit eda978e003
7 changed files with 120 additions and 1 deletions

View File

@ -119,6 +119,7 @@ set(PYBIND11_EXAMPLES
example/example11.cpp
example/example12.cpp
example/example13.cpp
example/example14.cpp
example/issues.cpp
)

View File

@ -1021,7 +1021,7 @@ Partitioning code over multiple extension modules
It's straightforward to split binding code over multiple extension modules, while
referencing types that are declared elsewhere. Everything "just" works without any special
precautions. One exception to this rule occurs when wanting to extend a type declared
precautions. One exception to this rule occurs when extending a type declared
in another extension module. Recall the basic example from Section
:ref:`inheritance`.
@ -1063,3 +1063,55 @@ Naturally, both methods will fail when there are cyclic dependencies.
py::class_<Dog>(m, "Dog", py::base<Pet>())
.def(py::init<const std::string &>())
.def("bark", &Dog::bark);
Treating STL data structures as opaque objects
==============================================
pybind11 heavily relies on a template matching mechanism to convert parameters
and return values that are constructed from STL data types such as vectors,
linked lists, hash tables, etc. This even works in a recursive manner, for
instance to deal with lists of hash maps of pairs of elementary and custom
types, etc.
The fundamental limitation of this approach is the internal conversion between
Python and C++ types involves a copy operation that prevents pass-by-reference
semantics. What does this mean?
Suppose we bind the following function
.. code-block:: cpp
void append_1(std::vector<int> &v) {
v.push_back(1);
}
and call it as follows from Python:
.. code-block:: python
>>> v = [5, 6]
>>> append_1(v)
>>> print(v)
[5, 6]
As you can see, when passing STL data structures by reference, modifications
are not propagated back the Python side. To deal with situations where this
desirable, pybind11 contains a simple template wrapper class named ``opaque<T>``.
``opaque<T>`` disables the underlying template 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:
.. code-block:: cpp
m.def("append_1", [](py::opaque<std::vector<int>> &v) { append_1(v); });
Opaque types must also have a dedicated ``class_`` declaration to define a
set of admissible operations.
.. seealso::
The file :file:`example/example14.cpp` contains a complete example that
demonstrates how to create opaque types using pybind11 in more detail.

View File

@ -22,6 +22,7 @@ void init_ex10(py::module &);
void init_ex11(py::module &);
void init_ex12(py::module &);
void init_ex13(py::module &);
void init_ex14(py::module &);
void init_issues(py::module &);
PYBIND11_PLUGIN(example) {
@ -40,6 +41,7 @@ PYBIND11_PLUGIN(example) {
init_ex11(m);
init_ex12(m);
init_ex13(m);
init_ex14(m);
init_issues(m);
return m.ptr();

29
example/example14.cpp Normal file
View File

@ -0,0 +1,29 @@
/*
example/example14.cpp -- opaque types
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
#include <pybind11/stl.h>
#include <vector>
typedef std::vector<std::string> StringList;
void init_ex14(py::module &m) {
py::class_<py::opaque<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(); });
m.def("print_opaque_list", [](py::opaque<StringList> &_l) {
StringList &l = _l;
std::cout << "Opaque list: " << std::endl;
for (auto entry : l)
std::cout << " " << entry << std::endl;
});
}

14
example/example14.py Normal file
View File

@ -0,0 +1,14 @@
from __future__ import print_function
import sys
sys.path.append('.')
from example import StringList, print_opaque_list
l = StringList()
l.push_back("Element 1")
l.push_back("Element 2")
print_opaque_list(l)
print("Back element is %s" % l.back())
l.pop_back()
print_opaque_list(l)

6
example/example14.ref Normal file
View File

@ -0,0 +1,6 @@
Opaque list:
Element 1
Element 2
Back element is Element 2
Opaque list:
Element 1

View File

@ -17,6 +17,21 @@
#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