Improve constructor resolution in variant_caster

Currently, `py::int_(1).cast<variant<double, int>>()` fills the `double`
slot of the variant. This commit switches the loader to a 2-pass scheme
in order to correctly fill the `int` slot.
This commit is contained in:
Dean Moldovan 2017-05-10 11:15:17 +02:00
parent 93e3eac6f9
commit 94d0a9f7bc
3 changed files with 15 additions and 1 deletions

View File

@ -315,6 +315,12 @@ struct variant_caster<V<Ts...>> {
bool load_alternative(handle, bool, type_list<>) { return false; }
bool load(handle src, bool convert) {
// Do a first pass without conversions to improve constructor resolution.
// E.g. `py::int_(1).cast<variant<double, int>>()` needs to fill the `int`
// slot of the variant. Without two-pass loading `double` would be filled
// because it appears first and a conversion is possible.
if (convert && load_alternative(src, false, type_list<Ts...>{}))
return true;
return load_alternative(src, convert, type_list<Ts...>{});
}

View File

@ -366,6 +366,10 @@ test_initializer python_types([](py::module &m) {
return std::visit(visitor(), v);
});
m.def("load_variant_2pass", [](std::variant<double, int> 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"));

View File

@ -373,12 +373,16 @@ def test_exp_optional():
@pytest.mark.skipif(not hasattr(pybind11_tests, "load_variant"), reason='no <variant>')
def test_variant(doc):
from pybind11_tests import load_variant, cast_variant
from pybind11_tests import load_variant, load_variant_2pass, cast_variant
assert load_variant(1) == "int"
assert load_variant("1") == "std::string"
assert load_variant(1.0) == "double"
assert load_variant(None) == "std::nullptr_t"
assert load_variant_2pass(1) == "int"
assert load_variant_2pass(1.0) == "double"
assert cast_variant() == (5, "Hello")
assert doc(load_variant) == "load_variant(arg0: Union[int, str, float, None]) -> str"