Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pybind11
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
pybind11
Commits
fe34241e
Commit
fe34241e
authored
Sep 06, 2016
by
Wenzel Jakob
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
minor doc & style fixes
parent
07082eec
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
190 additions
and
146 deletions
+190
-146
docs/advanced.rst
+135
-114
docs/basics.rst
+0
-0
docs/compiling.rst
+0
-0
docs/faq.rst
+1
-1
include/pybind11/cast.h
+0
-0
include/pybind11/pybind11.h
+0
-0
include/pybind11/stl_bind.h
+25
-22
tests/object.h
+0
-0
tests/test_callbacks.cpp
+0
-0
tests/test_eigen.cpp
+0
-0
tests/test_enum.cpp
+0
-0
tests/test_kwargs_and_defaults.cpp
+0
-0
tests/test_stl_binders.cpp
+10
-8
tools/check-style.sh
+19
-1
No files found.
docs/advanced.rst
View file @
fe34241e
...
@@ -551,27 +551,150 @@ and the Python ``list``, ``set`` and ``dict`` data structures are automatically
...
@@ -551,27 +551,150 @@ and the Python ``list``, ``set`` and ``dict`` data structures are automatically
enabled. The types ``std::pair<>`` and ``std::tuple<>`` are already supported
enabled. The types ``std::pair<>`` and ``std::tuple<>`` are already supported
out of the box with just the core :file:`pybind11/pybind11.h` header.
out of the box with just the core :file:`pybind11/pybind11.h` header.
Alternatively it might be desirable to bind STL containers as native C++ classes,
The major downside of these implicit conversions is that containers must be
eliminating the need of converting back and forth between C++ representation
converted (i.e. copied) on every Python->C++ and C++->Python transition, which
and Python one. The downside of this approach in this case users will have to
can have implications on the program semantics and performance. Please read the
deal with C++ containers directly instead of using already familiar Python lists
next sections for more details and alternative approaches that avoid this.
or dicts.
Pybind11 provide set of binder functions to bind various STL containers like vectors,
maps etc. All binder functions are designed to return instances of pybind11::class_
objects so developers can bind extra functions if needed. For complete set of
available functions please see :file:`pybind11/stl_bind.h`. For an example on using
this feature, please see :file:`tests/test_stl_binders.cpp`.
.. note::
.. note::
Arbitrary nesting of any of these types is
supported
.
Arbitrary nesting of any of these types is
possible
.
.. seealso::
.. seealso::
The file :file:`tests/test_python_types.cpp` contains a complete
The file :file:`tests/test_python_types.cpp` contains a complete
example that demonstrates how to pass STL data types in more detail.
example that demonstrates how to pass STL data types in more detail.
.. _opaque:
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.
However, a fundamental limitation of this approach is that internal conversions
between Python and C++ types involve 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 from Python, the following happens:
.. code-block:: pycon
>>> 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. A similar situation arises when
exposing STL data structures using the ``def_readwrite`` or ``def_readonly``
functions:
.. code-block:: cpp
/* ... definition ... */
class MyClass {
std::vector<int> contents;
};
/* ... binding code ... */
py::class_<MyClass>(m, "MyClass")
.def(py::init<>)
.def_readwrite("contents", &MyClass::contents);
In this case, properties can be read and written in their entirety. However, an
``append`` operaton involving such a list type has no effect:
.. code-block:: pycon
>>> m = MyClass()
>>> m.contents = [5, 6]
>>> print(m.contents)
[5, 6]
>>> m.contents.append(7)
>>> print(m.contents)
[5, 6]
Finally, the involved copy operations can be costly when dealing with very
large lists. To deal with all 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
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 (and outside of any namespaces), since
it 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, e.g.:
.. 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 */
// ....
The ability to expose STL containers as native Python objects is a fairly
common request, hence pybind11 also provides an optional header file named
:file:`pybind11/stl_bind.h` that does exactly this. The mapped containers try
to match the behavior of their native Python counterparts as much as possible.
The following example showcases usage of :file:`pybind11/stl_bind.h`:
.. code-block:: cpp
// Don't forget this
#include <pybind11/stl_bind.h>
PYBIND11_MAKE_OPAQUE(std::vector<int>);
PYBIND11_MAKE_OPAQUE(std::map<std::string, double>);
// ...
// later in binding code:
py::bind_vector<std::vector<int>>(m, "VectorInt");
py::bind_map<std::map<std::string, double>>(m, "MapStringDouble");
Please take a look at the :ref:`macro_notes` before using the
``PYBIND11_MAKE_OPAQUE`` macro.
.. seealso::
The file :file:`tests/test_opaque_types.cpp` contains a complete
example that demonstrates how to create and expose opaque types using
pybind11 in more detail.
The file :file:`tests/test_stl_binders.cpp` shows how to use the
convenience STL container wrappers.
Binding sequence data types, iterators, the slicing protocol, etc.
Binding sequence data types, iterators, the slicing protocol, etc.
==================================================================
==================================================================
...
@@ -1103,108 +1226,6 @@ section.
...
@@ -1103,108 +1226,6 @@ section.
The ``py::exception`` wrapper for creating custom exceptions cannot (yet)
The ``py::exception`` wrapper for creating custom exceptions cannot (yet)
be used as a ``py::base``.
be used as a ``py::base``.
.. _opaque:
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.
However, a fundamental limitation of this approach is that internal conversions
between Python and C++ types involve 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 from Python, the following happens:
.. code-block:: pycon
>>> 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. A similar situation arises when
exposing STL data structures using the ``def_readwrite`` or ``def_readonly``
functions:
.. code-block:: cpp
/* ... definition ... */
class MyClass {
std::vector<int> contents;
};
/* ... binding code ... */
py::class_<MyClass>(m, "MyClass")
.def(py::init<>)
.def_readwrite("contents", &MyClass::contents);
In this case, properties can be read and written in their entirety. However, an
``append`` operaton involving such a list type has no effect:
.. code-block:: pycon
>>> m = MyClass()
>>> m.contents = [5, 6]
>>> print(m.contents)
[5, 6]
>>> m.contents.append(7)
>>> print(m.contents)
[5, 6]
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
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 */
// ....
Please take a look at the :ref:`macro_notes` before using this feature.
.. seealso::
The file :file:`tests/test_opaque_types.cpp` contains a complete
example that demonstrates how to create and expose opaque types using
pybind11 in more detail.
.. _eigen:
.. _eigen:
Transparent conversion of dense and sparse Eigen data types
Transparent conversion of dense and sparse Eigen data types
...
...
docs/basics.rst
View file @
fe34241e
docs/compiling.rst
View file @
fe34241e
docs/faq.rst
View file @
fe34241e
...
@@ -140,7 +140,7 @@ the included test suite contains the following symbol:
...
@@ -140,7 +140,7 @@ the included test suite contains the following symbol:
.. code-block:: none
.. code-block:: none
__ZN8pybind1112cpp_functionC1Iv8Example2JRNSt3__16vectorINS3_12basic_stringIwNS3_11char_traitsIwEENS3_9allocatorIwEEEENS8_ISA_EEEEEJNS_4nameENS_7siblingENS_9is_methodEA28_cEEEMT0_FT_DpT1_EDpRKT2_
__ZN8pybind1112cpp_functionC1Iv8Example2JRNSt3__16vectorINS3_12basic_stringIwNS3_11char_traitsIwEENS3_9allocatorIwEEEENS8_ISA_EEEEEJNS_4nameENS_7siblingENS_9is_methodEA28_cEEEMT0_FT_DpT1_EDpRKT2_
.. only:: not html
.. only:: not html
...
...
include/pybind11/cast.h
View file @
fe34241e
include/pybind11/pybind11.h
View file @
fe34241e
include/pybind11/stl_bind.h
View file @
fe34241e
...
@@ -12,7 +12,6 @@
...
@@ -12,7 +12,6 @@
#include "common.h"
#include "common.h"
#include "operators.h"
#include "operators.h"
#include <map>
#include <type_traits>
#include <type_traits>
#include <utility>
#include <utility>
#include <algorithm>
#include <algorithm>
...
@@ -356,7 +355,7 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
...
@@ -356,7 +355,7 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
//
//
// std::map
// std::map
, std::unordered_map
//
//
NAMESPACE_BEGIN
(
detail
)
NAMESPACE_BEGIN
(
detail
)
...
@@ -373,8 +372,8 @@ template <typename Map, typename Class_, typename... Args> void map_if_copy_assi
...
@@ -373,8 +372,8 @@ template <typename Map, typename Class_, typename... Args> void map_if_copy_assi
auto
it
=
m
.
find
(
k
);
auto
it
=
m
.
find
(
k
);
if
(
it
!=
m
.
end
())
it
->
second
=
v
;
if
(
it
!=
m
.
end
())
it
->
second
=
v
;
else
m
.
emplace
(
k
,
v
);
else
m
.
emplace
(
k
,
v
);
}
);
}
);
}
}
template
<
typename
Map
,
typename
Class_
,
typename
std
::
enable_if
<!
std
::
is_copy_assignable
<
typename
Map
::
mapped_type
>::
value
,
int
>::
type
=
0
>
template
<
typename
Map
,
typename
Class_
,
typename
std
::
enable_if
<!
std
::
is_copy_assignable
<
typename
Map
::
mapped_type
>::
value
,
int
>::
type
=
0
>
...
@@ -384,12 +383,15 @@ void map_if_copy_assignable(Class_ &cl) {
...
@@ -384,12 +383,15 @@ void map_if_copy_assignable(Class_ &cl) {
cl
.
def
(
"__setitem__"
,
cl
.
def
(
"__setitem__"
,
[](
Map
&
m
,
const
KeyType
&
k
,
const
MappedType
&
v
)
{
[](
Map
&
m
,
const
KeyType
&
k
,
const
MappedType
&
v
)
{
auto
r
=
m
.
insert
(
std
::
make_pair
(
k
,
v
)
);
// We can't use m[k] = v; because value type might not be default constructable
// We can't use m[k] = v; because value type might not be default constructable
if
(
!
r
.
second
)
{
// value type might be const so the only way to insert it is to errase it first...
auto
r
=
m
.
insert
(
std
::
make_pair
(
k
,
v
));
if
(
!
r
.
second
)
{
// value type might be const so the only way to insert it is to erase it first...
m
.
erase
(
r
.
first
);
m
.
erase
(
r
.
first
);
m
.
insert
(
std
::
make_pair
(
k
,
v
)
);
m
.
insert
(
std
::
make_pair
(
k
,
v
)
);
}
}
});
}
);
}
}
...
@@ -401,8 +403,9 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
...
@@ -401,8 +403,9 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
std
::
ostringstream
s
;
std
::
ostringstream
s
;
s
<<
name
<<
'{'
;
s
<<
name
<<
'{'
;
bool
f
=
false
;
bool
f
=
false
;
for
(
auto
const
&
kv
:
m
)
{
for
(
auto
const
&
kv
:
m
)
{
if
(
f
)
s
<<
", "
;
if
(
f
)
s
<<
", "
;
s
<<
kv
.
first
<<
": "
<<
kv
.
second
;
s
<<
kv
.
first
<<
": "
<<
kv
.
second
;
f
=
true
;
f
=
true
;
}
}
...
@@ -428,16 +431,12 @@ pybind11::class_<Map, holder_type> bind_map(module &m, const std::string &name,
...
@@ -428,16 +431,12 @@ pybind11::class_<Map, holder_type> bind_map(module &m, const std::string &name,
detail
::
map_if_insertion_operator
<
Map
,
Class_
>
(
cl
,
name
);
detail
::
map_if_insertion_operator
<
Map
,
Class_
>
(
cl
,
name
);
cl
.
def
(
"__bool__"
,
cl
.
def
(
"__bool__"
,
[](
const
Map
&
m
)
->
bool
{
[](
const
Map
&
m
)
->
bool
{
return
!
m
.
empty
();
},
return
!
m
.
empty
();
},
"Check whether the map is nonempty"
"Check whether the map is nonempty"
);
);
cl
.
def
(
"__iter__"
,
cl
.
def
(
"__iter__"
,
[](
Map
&
m
)
{
[](
Map
&
m
)
{
return
pybind11
::
make_key_iterator
(
m
.
begin
(),
m
.
end
());
},
return
pybind11
::
make_key_iterator
(
m
.
begin
(),
m
.
end
());
},
pybind11
::
keep_alive
<
0
,
1
>
()
/* Essential: keep list alive while iterator exists */
pybind11
::
keep_alive
<
0
,
1
>
()
/* Essential: keep list alive while iterator exists */
);
);
...
@@ -449,18 +448,22 @@ pybind11::class_<Map, holder_type> bind_map(module &m, const std::string &name,
...
@@ -449,18 +448,22 @@ pybind11::class_<Map, holder_type> bind_map(module &m, const std::string &name,
cl
.
def
(
"__getitem__"
,
cl
.
def
(
"__getitem__"
,
[](
Map
&
m
,
const
KeyType
&
k
)
->
MappedType
{
[](
Map
&
m
,
const
KeyType
&
k
)
->
MappedType
{
auto
it
=
m
.
find
(
k
);
auto
it
=
m
.
find
(
k
);
if
(
it
!=
m
.
end
())
return
it
->
second
;
if
(
it
==
m
.
end
())
else
throw
pybind11
::
key_error
();
// it is not always possible to convert key to string // pybind11::key_error(k)
throw
pybind11
::
key_error
();
});
return
it
->
second
;
}
);
detail
::
map_if_copy_assignable
<
Map
,
Class_
>
(
cl
);
detail
::
map_if_copy_assignable
<
Map
,
Class_
>
(
cl
);
cl
.
def
(
"__delitem__"
,
cl
.
def
(
"__delitem__"
,
[](
Map
&
m
,
const
KeyType
&
k
)
{
[](
Map
&
m
,
const
KeyType
&
k
)
{
auto
it
=
m
.
find
(
k
);
auto
it
=
m
.
find
(
k
);
if
(
it
!=
m
.
end
())
return
m
.
erase
(
it
);
if
(
it
==
m
.
end
())
else
throw
pybind11
::
key_error
();
// it is not always possible to convert key to string // pybind11::key_error(k)
throw
pybind11
::
key_error
();
});
return
m
.
erase
(
it
);
}
);
cl
.
def
(
"__len__"
,
&
Map
::
size
);
cl
.
def
(
"__len__"
,
&
Map
::
size
);
...
...
tests/object.h
View file @
fe34241e
tests/test_callbacks.cpp
View file @
fe34241e
tests/test_eigen.cpp
View file @
fe34241e
tests/test_enum.cpp
View file @
fe34241e
tests/test_kwargs_and_defaults.cpp
View file @
fe34241e
tests/test_stl_binders.cpp
View file @
fe34241e
...
@@ -10,6 +10,8 @@
...
@@ -10,6 +10,8 @@
#include "pybind11_tests.h"
#include "pybind11_tests.h"
#include <pybind11/stl_bind.h>
#include <pybind11/stl_bind.h>
#include <map>
#include <unordered_map>
class
El
{
class
El
{
public
:
public
:
...
@@ -28,18 +30,18 @@ test_initializer stl_binder_vector([](py::module &m) {
...
@@ -28,18 +30,18 @@ test_initializer stl_binder_vector([](py::module &m) {
py
::
class_
<
El
>
(
m
,
"El"
)
py
::
class_
<
El
>
(
m
,
"El"
)
.
def
(
py
::
init
<
int
>
());
.
def
(
py
::
init
<
int
>
());
py
::
bind_vector
<
std
::
vector
<
unsigned
int
>
>
(
m
,
"VectorInt"
);
py
::
bind_vector
<
std
::
vector
<
unsigned
int
>
>
(
m
,
"VectorInt"
);
py
::
bind_vector
<
std
::
vector
<
bool
>
>
(
m
,
"VectorBool"
);
py
::
bind_vector
<
std
::
vector
<
bool
>
>
(
m
,
"VectorBool"
);
py
::
bind_vector
<
std
::
vector
<
El
>
>
(
m
,
"VectorEl"
);
py
::
bind_vector
<
std
::
vector
<
El
>
>
(
m
,
"VectorEl"
);
py
::
bind_vector
<
std
::
vector
<
std
::
vector
<
El
>
>
>
(
m
,
"VectorVectorEl"
);
py
::
bind_vector
<
std
::
vector
<
std
::
vector
<
El
>>
>
(
m
,
"VectorVectorEl"
);
});
});
test_initializer
stl_binder_map
([](
py
::
module
&
m
)
{
test_initializer
stl_binder_map
([](
py
::
module
&
m
)
{
py
::
bind_map
<
std
::
map
<
std
::
string
,
double
>
>
(
m
,
"MapStringDouble"
);
py
::
bind_map
<
std
::
map
<
std
::
string
,
double
>
>
(
m
,
"MapStringDouble"
);
py
::
bind_map
<
std
::
unordered_map
<
std
::
string
,
double
>
>
(
m
,
"UnorderedMapStringDouble"
);
py
::
bind_map
<
std
::
unordered_map
<
std
::
string
,
double
>
>
(
m
,
"UnorderedMapStringDouble"
);
py
::
bind_map
<
std
::
map
<
std
::
string
,
double
const
>
>
(
m
,
"MapStringDoubleConst"
);
py
::
bind_map
<
std
::
map
<
std
::
string
,
double
const
>
>
(
m
,
"MapStringDoubleConst"
);
py
::
bind_map
<
std
::
unordered_map
<
std
::
string
,
double
const
>
>
(
m
,
"UnorderedMapStringDoubleConst"
);
py
::
bind_map
<
std
::
unordered_map
<
std
::
string
,
double
const
>
>
(
m
,
"UnorderedMapStringDoubleConst"
);
});
});
tools/check-style.sh
View file @
fe34241e
#!/bin/bash
#!/bin/bash
#
#
# Script to check include/test code for common pybind11 code style errors.
# Script to check include/test code for common pybind11 code style errors.
# Currently just checks for tabs used instead of spaces.
#
# This script currently checks for
#
# 1. use of tabs instead of spaces
# 2. trailing spaces
# 3. missing space between keyword and parenthesis, e.g.: for(, if(, while(
#
#
# Invoke as: tools/check-style.sh
# Invoke as: tools/check-style.sh
#
#
...
@@ -22,6 +27,19 @@ while read -u 3 f; do
...
@@ -22,6 +27,19 @@ while read -u 3 f; do
done
done
found
=
found
=
# The mt=41 sets a red background for matched trailing spaces
exec
3< <
(
GREP_COLORS
=
'mt=41'
grep
'\s\+$'
include/ tests/
*
.
{
cpp,py,h
}
docs/
*
.rst
-rn
--color
=
always
)
while
read
-u
3 f
;
do
if
[
-z
"
$found
"
]
;
then
echo
-e
'\e[31m\e[01mError: found trailing spaces in the following files:\e[0m'
found
=
1
errors
=
1
fi
echo
"
$f
"
done
found
=
exec
3< <
(
grep
'\<\(if\|for\|while\)(\|){'
include/ tests/
*
.
{
cpp,py,h
}
-rn
--color
=
always
)
exec
3< <
(
grep
'\<\(if\|for\|while\)(\|){'
include/ tests/
*
.
{
cpp,py,h
}
-rn
--color
=
always
)
while
read
-u
3 line
;
do
while
read
-u
3 line
;
do
if
[
-z
"
$found
"
]
;
then
if
[
-z
"
$found
"
]
;
then
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment