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
4ffa76ec
Commit
4ffa76ec
authored
Apr 21, 2017
by
Dean Moldovan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add type caster for std::variant and other variant-like classes
parent
a01b6b80
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
142 additions
and
0 deletions
+142
-0
docs/advanced/cast/overview.rst
+2
-0
docs/advanced/cast/stl.rst
+45
-0
include/pybind11/stl.h
+65
-0
tests/test_python_types.cpp
+17
-0
tests/test_python_types.py
+13
-0
No files found.
docs/advanced/cast/overview.rst
View file @
4ffa76ec
...
@@ -144,6 +144,8 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
...
@@ -144,6 +144,8 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
+------------------------------------+---------------------------+-------------------------------+
+------------------------------------+---------------------------+-------------------------------+
| ``std::experimental::optional<T>`` | STL optional type (exp.) | :file:`pybind11/stl.h` |
| ``std::experimental::optional<T>`` | STL optional type (exp.) | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+
+------------------------------------+---------------------------+-------------------------------+
| ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+
| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` |
| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` |
+------------------------------------+---------------------------+-------------------------------+
+------------------------------------+---------------------------+-------------------------------+
| ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` |
| ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` |
...
...
docs/advanced/cast/stl.rst
View file @
4ffa76ec
...
@@ -26,6 +26,51 @@ next sections for more details and alternative approaches that avoid this.
...
@@ -26,6 +26,51 @@ next sections for more details and alternative approaches that avoid this.
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.
C++17 library containers
========================
The :file:`pybind11/stl.h` header also includes support for ``std::optional<>``
and ``std::variant<>``. These require a C++17 compiler and standard library.
In C++14 mode, ``std::experimental::optional<>`` is supported if available.
Various versions of these containers also exist for C++11 (e.g. in Boost).
pybind11 provides an easy way to specialize the ``type_caster`` for such
types:
.. code-block:: cpp
// `boost::optional` as an example -- can be any `std::optional`-like container
namespace pybind11 { namespace detail {
template <typename T>
struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {};
}}
The above should be placed in a header file and included in all translation units
where automatic conversion is needed. Similarly, a specialization can be provided
for custom variant types:
.. code-block:: cpp
// `boost::variant` as an example -- can be any `std::variant`-like container
namespace pybind11 { namespace detail {
template <typename... Ts>
struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};
// Specifies the function used to visit the variant -- `apply_visitor` instead of `visit`
template <>
struct visit_helper<boost::variant> {
template <typename... Args>
static auto call(Args &&...args)
-> decltype(boost::apply_visitor(std::forward<Args>(args)...)) {
return boost::apply_visitor(std::forward<Args>(args)...);
}
};
}} // namespace pybind11::detail
The ``visit_helper`` specialization is not required if your ``name::variant`` provides
a ``name::visit()`` function. For any other function name, the specialization must be
included to tell pybind11 how to visit the variant.
.. _opaque:
.. _opaque:
Making opaque types
Making opaque types
...
...
include/pybind11/stl.h
View file @
4ffa76ec
...
@@ -36,6 +36,11 @@
...
@@ -36,6 +36,11 @@
# define PYBIND11_HAS_EXP_OPTIONAL 1
# define PYBIND11_HAS_EXP_OPTIONAL 1
# endif
# endif
# endif
# endif
// std::variant
# if defined(PYBIND11_CPP17) && __has_include(<variant>)
# include <variant>
# define PYBIND11_HAS_VARIANT 1
# endif
#endif
#endif
NAMESPACE_BEGIN
(
pybind11
)
NAMESPACE_BEGIN
(
pybind11
)
...
@@ -262,6 +267,66 @@ template<> struct type_caster<std::experimental::nullopt_t>
...
@@ -262,6 +267,66 @@ template<> struct type_caster<std::experimental::nullopt_t>
:
public
void_caster
<
std
::
experimental
::
nullopt_t
>
{};
:
public
void_caster
<
std
::
experimental
::
nullopt_t
>
{};
#endif
#endif
/// Visit a variant and cast any found type to Python
struct
variant_caster_visitor
{
return_value_policy
policy
;
handle
parent
;
template
<
typename
T
>
handle
operator
()(
T
&&
src
)
const
{
return
make_caster
<
T
>::
cast
(
std
::
forward
<
T
>
(
src
),
policy
,
parent
);
}
};
/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar
/// `namespace::variant` types which provide a `namespace::visit()` function are handled here
/// automatically using argument-dependent lookup. Users can provide specializations for other
/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`.
template
<
template
<
typename
...
>
class
Variant
>
struct
visit_helper
{
template
<
typename
...
Args
>
static
auto
call
(
Args
&&
...
args
)
->
decltype
(
visit
(
std
::
forward
<
Args
>
(
args
)...))
{
return
visit
(
std
::
forward
<
Args
>
(
args
)...);
}
};
/// Generic variant caster
template
<
typename
Variant
>
struct
variant_caster
;
template
<
template
<
typename
...
>
class
V
,
typename
...
Ts
>
struct
variant_caster
<
V
<
Ts
...
>>
{
static_assert
(
sizeof
...(
Ts
)
>
0
,
"Variant must consist of at least one alternative."
);
template
<
typename
U
,
typename
...
Us
>
bool
load_alternative
(
handle
src
,
bool
convert
,
type_list
<
U
,
Us
...
>
)
{
auto
caster
=
make_caster
<
U
>
();
if
(
caster
.
load
(
src
,
convert
))
{
value
=
cast_op
<
U
>
(
caster
);
return
true
;
}
return
load_alternative
(
src
,
convert
,
type_list
<
Us
...
>
{});
}
bool
load_alternative
(
handle
,
bool
,
type_list
<>
)
{
return
false
;
}
bool
load
(
handle
src
,
bool
convert
)
{
return
load_alternative
(
src
,
convert
,
type_list
<
Ts
...
>
{});
}
template
<
typename
Variant
>
static
handle
cast
(
Variant
&&
src
,
return_value_policy
policy
,
handle
parent
)
{
return
visit_helper
<
V
>::
call
(
variant_caster_visitor
{
policy
,
parent
},
std
::
forward
<
Variant
>
(
src
));
}
using
Type
=
V
<
Ts
...
>
;
PYBIND11_TYPE_CASTER
(
Type
,
_
(
"Union["
)
+
detail
::
concat
(
make_caster
<
Ts
>::
name
()...)
+
_
(
"]"
));
};
#if PYBIND11_HAS_VARIANT
template
<
typename
...
Ts
>
struct
type_caster
<
std
::
variant
<
Ts
...
>>
:
variant_caster
<
std
::
variant
<
Ts
...
>>
{
};
#endif
NAMESPACE_END
(
detail
)
NAMESPACE_END
(
detail
)
inline
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
handle
&
obj
)
{
inline
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
handle
&
obj
)
{
...
...
tests/test_python_types.cpp
View file @
4ffa76ec
...
@@ -354,6 +354,23 @@ test_initializer python_types([](py::module &m) {
...
@@ -354,6 +354,23 @@ test_initializer python_types([](py::module &m) {
m
.
attr
(
"has_optional"
)
=
has_optional
;
m
.
attr
(
"has_optional"
)
=
has_optional
;
m
.
attr
(
"has_exp_optional"
)
=
has_exp_optional
;
m
.
attr
(
"has_exp_optional"
)
=
has_exp_optional
;
#ifdef PYBIND11_HAS_VARIANT
struct
visitor
{
const
char
*
operator
()(
int
)
{
return
"int"
;
}
const
char
*
operator
()(
std
::
string
)
{
return
"std::string"
;
}
const
char
*
operator
()(
double
)
{
return
"double"
;
}
};
m
.
def
(
"load_variant"
,
[](
std
::
variant
<
int
,
std
::
string
,
double
>
v
)
{
return
std
::
visit
(
visitor
(),
v
);
});
m
.
def
(
"cast_variant"
,
[]()
{
using
V
=
std
::
variant
<
int
,
std
::
string
>
;
return
py
::
make_tuple
(
V
(
5
),
V
(
"Hello"
));
});
#endif
m
.
def
(
"test_default_constructors"
,
[]()
{
m
.
def
(
"test_default_constructors"
,
[]()
{
return
py
::
dict
(
return
py
::
dict
(
"str"
_a
=
py
::
str
(),
"str"
_a
=
py
::
str
(),
...
...
tests/test_python_types.py
View file @
4ffa76ec
# Python < 3 needs this: coding=utf-8
# Python < 3 needs this: coding=utf-8
import
pytest
import
pytest
import
pybind11_tests
from
pybind11_tests
import
ExamplePythonTypes
,
ConstructorStats
,
has_optional
,
has_exp_optional
from
pybind11_tests
import
ExamplePythonTypes
,
ConstructorStats
,
has_optional
,
has_exp_optional
...
@@ -370,6 +371,18 @@ def test_exp_optional():
...
@@ -370,6 +371,18 @@ def test_exp_optional():
assert
test_nullopt_exp
(
43
)
==
43
assert
test_nullopt_exp
(
43
)
==
43
@pytest.mark.skipif
(
not
hasattr
(
pybind11_tests
,
"load_variant"
),
reason
=
'no <variant>'
)
def
test_variant
(
doc
):
from
pybind11_tests
import
load_variant
,
cast_variant
assert
load_variant
(
1
)
==
"int"
assert
load_variant
(
"1"
)
==
"std::string"
assert
load_variant
(
1.0
)
==
"double"
assert
cast_variant
()
==
(
5
,
"Hello"
)
assert
doc
(
load_variant
)
==
"load_variant(arg0: Union[int, str, float]) -> str"
def
test_constructors
():
def
test_constructors
():
"""C++ default and converting constructors are equivalent to type calls in Python"""
"""C++ default and converting constructors are equivalent to type calls in Python"""
from
pybind11_tests
import
(
test_default_constructors
,
test_converting_constructors
,
from
pybind11_tests
import
(
test_default_constructors
,
test_converting_constructors
,
...
...
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