Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
gaoqiong
pybind11
Commits
4ffa76ec
Commit
4ffa76ec
authored
Apr 21, 2017
by
Dean Moldovan
Browse files
Add type caster for std::variant and other variant-like classes
parent
a01b6b80
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
142 additions
and
0 deletions
+142
-0
docs/advanced/cast/overview.rst
docs/advanced/cast/overview.rst
+2
-0
docs/advanced/cast/stl.rst
docs/advanced/cast/stl.rst
+45
-0
include/pybind11/stl.h
include/pybind11/stl.h
+65
-0
tests/test_python_types.cpp
tests/test_python_types.cpp
+17
-0
tests/test_python_types.py
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`.
+------------------------------------+---------------------------+-------------------------------+
| ``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::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.
The file :file:`tests/test_python_types.cpp` contains a complete
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:
Making opaque types
...
...
include/pybind11/stl.h
View file @
4ffa76ec
...
...
@@ -36,6 +36,11 @@
# define PYBIND11_HAS_EXP_OPTIONAL 1
# endif
# endif
// std::variant
# if defined(PYBIND11_CPP17) && __has_include(<variant>)
# include <variant>
# define PYBIND11_HAS_VARIANT 1
# endif
#endif
NAMESPACE_BEGIN
(
pybind11
)
...
...
@@ -262,6 +267,66 @@ template<> struct type_caster<std::experimental::nullopt_t>
:
public
void_caster
<
std
::
experimental
::
nullopt_t
>
{};
#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
)
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) {
m
.
attr
(
"has_optional"
)
=
has_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"
,
[]()
{
return
py
::
dict
(
"str"
_a
=
py
::
str
(),
...
...
tests/test_python_types.py
View file @
4ffa76ec
# Python < 3 needs this: coding=utf-8
import
pytest
import
pybind11_tests
from
pybind11_tests
import
ExamplePythonTypes
,
ConstructorStats
,
has_optional
,
has_exp_optional
...
...
@@ -370,6 +371,18 @@ def test_exp_optional():
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
():
"""C++ default and converting constructors are equivalent to type calls in Python"""
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