Adds automatic casting on assignment of non-pyobject types (#551)
This adds automatic casting when assigning to python types like dict,
list, and attributes. Instead of:
dict["key"] = py::cast(val);
m.attr("foo") = py::cast(true);
list.append(py::cast(42));
you can now simply write:
dict["key"] = val;
m.attr("foo") = true;
list.append(42);
Casts needing extra parameters (e.g. for a non-default rvp) still
require the py::cast() call. set::add() is also supported.
All usage is channeled through a SFINAE implementation which either just returns or casts.
Combined non-converting handle and autocasting template methods via a
helper method that either just returns (handle) or casts (C++ type).
diff --git a/tests/pybind11_tests.cpp b/tests/pybind11_tests.cpp
index 35981a0..9c593ee 100644
--- a/tests/pybind11_tests.cpp
+++ b/tests/pybind11_tests.cpp
@@ -39,7 +39,7 @@
for (const auto &initializer : initializers())
initializer(m);
- if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = py::cast(false);
+ if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = false;
return m.ptr();
}
diff --git a/tests/test_eigen.cpp b/tests/test_eigen.cpp
index a9cb9f2..588cdce 100644
--- a/tests/test_eigen.cpp
+++ b/tests/test_eigen.cpp
@@ -40,7 +40,7 @@
typedef Eigen::SparseMatrix<float, Eigen::RowMajor> SparseMatrixR;
typedef Eigen::SparseMatrix<float> SparseMatrixC;
- m.attr("have_eigen") = py::cast(true);
+ m.attr("have_eigen") = true;
// Non-symmetric matrix with zero elements
Eigen::MatrixXf mat(5, 6);
diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp
index ca2afa6..706b500 100644
--- a/tests/test_exceptions.cpp
+++ b/tests/test_exceptions.cpp
@@ -88,7 +88,7 @@
struct PythonCallInDestructor {
PythonCallInDestructor(const py::dict &d) : d(d) {}
- ~PythonCallInDestructor() { d["good"] = py::cast(true); }
+ ~PythonCallInDestructor() { d["good"] = true; }
py::dict d;
};
diff --git a/tests/test_issues.cpp b/tests/test_issues.cpp
index 378da52..4c59a1b 100644
--- a/tests/test_issues.cpp
+++ b/tests/test_issues.cpp
@@ -381,6 +381,11 @@
.def_static("make", &MyDerived::make)
.def_static("make2", &MyDerived::make);
+ py::dict d;
+ std::string bar = "bar";
+ d["str"] = bar;
+ d["num"] = 3.7;
+
/// Issue #528: templated constructor
m2.def("tpl_constr_vector", [](std::vector<TplConstrClass> &) {});
m2.def("tpl_constr_map", [](std::unordered_map<TplConstrClass, TplConstrClass> &) {});
diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp
index 68e07ad..33c655b 100644
--- a/tests/test_python_types.cpp
+++ b/tests/test_python_types.cpp
@@ -37,7 +37,8 @@
py::set get_set() {
py::set set;
set.add(py::str("key1"));
- set.add(py::str("key2"));
+ set.add("key2");
+ set.add(std::string("key3"));
return set;
}
@@ -59,7 +60,7 @@
/* Create, manipulate, and return a Python list */
py::list get_list() {
py::list list;
- list.append(py::str("value"));
+ list.append("value");
py::print("Entry at position 0:", list[0]);
list[0] = py::str("overwritten");
return list;
@@ -269,7 +270,7 @@
d["missing_attr_chain"] = "raised"_s;
}
- d["is_none"] = py::cast(o.attr("basic_attr").is_none());
+ d["is_none"] = o.attr("basic_attr").is_none();
d["operator()"] = o.attr("func")(1);
d["operator*"] = o.attr("func")(*o.attr("begin_end"));
@@ -279,13 +280,13 @@
m.def("test_tuple_accessor", [](py::tuple existing_t) {
try {
- existing_t[0] = py::cast(1);
+ existing_t[0] = 1;
} catch (const py::error_already_set &) {
// --> Python system error
// Only new tuples (refcount == 1) are mutable
auto new_t = py::tuple(3);
for (size_t i = 0; i < new_t.size(); ++i) {
- new_t[i] = py::cast(i);
+ new_t[i] = i;
}
return new_t;
}
@@ -294,15 +295,15 @@
m.def("test_accessor_assignment", []() {
auto l = py::list(1);
- l[0] = py::cast(0);
+ l[0] = 0;
auto d = py::dict();
d["get"] = l[0];
auto var = l[0];
d["deferred_get"] = var;
- l[0] = py::cast(1);
+ l[0] = 1;
d["set"] = l[0];
- var = py::cast(99); // this assignment should not overwrite l[0]
+ var = 99; // this assignment should not overwrite l[0]
d["deferred_set"] = l[0];
d["var"] = var;
@@ -338,8 +339,8 @@
}, py::arg_v("x", std::experimental::nullopt, "None"));
#endif
- m.attr("has_optional") = py::cast(has_optional);
- m.attr("has_exp_optional") = py::cast(has_exp_optional);
+ m.attr("has_optional") = has_optional;
+ m.attr("has_exp_optional") = has_exp_optional;
m.def("test_default_constructors", []() {
return py::dict(
@@ -389,4 +390,41 @@
py::class_<MoveOutContainer>(m, "MoveOutContainer")
.def(py::init<>())
.def_property_readonly("move_list", &MoveOutContainer::move_list);
+
+ m.def("get_implicit_casting", []() {
+ py::dict d;
+ d["char*_i1"] = "abc";
+ const char *c2 = "abc";
+ d["char*_i2"] = c2;
+ d["char*_e"] = py::cast(c2);
+ d["char*_p"] = py::str(c2);
+
+ d["int_i1"] = 42;
+ int i = 42;
+ d["int_i2"] = i;
+ i++;
+ d["int_e"] = py::cast(i);
+ i++;
+ d["int_p"] = py::int_(i);
+
+ d["str_i1"] = std::string("str");
+ std::string s2("str1");
+ d["str_i2"] = s2;
+ s2[3] = '2';
+ d["str_e"] = py::cast(s2);
+ s2[3] = '3';
+ d["str_p"] = py::str(s2);
+
+ py::list l(2);
+ l[0] = 3;
+ l[1] = py::cast(6);
+ l.append(9);
+ l.append(py::cast(12));
+ l.append(py::int_(15));
+
+ return py::dict(
+ "d"_a=d,
+ "l"_a=l
+ );
+ });
});
diff --git a/tests/test_python_types.py b/tests/test_python_types.py
index 50a707e..b90b44c 100644
--- a/tests/test_python_types.py
+++ b/tests/test_python_types.py
@@ -38,12 +38,13 @@
"""
with capture:
set_result = instance.get_set()
- set_result.add('key3')
+ set_result.add('key4')
instance.print_set(set_result)
assert capture.unordered == """
key: key1
key: key2
key: key3
+ key: key4
"""
with capture:
set_result = instance.get_set2()
@@ -386,3 +387,16 @@
c = MoveOutContainer()
moved_out_list = c.move_list
assert [x.value for x in moved_out_list] == [0, 1, 2]
+
+
+def test_implicit_casting():
+ """Tests implicit casting when assigning or appending to dicts and lists."""
+ from pybind11_tests import get_implicit_casting
+
+ z = get_implicit_casting()
+ assert z['d'] == {
+ 'char*_i1': 'abc', 'char*_i2': 'abc', 'char*_e': 'abc', 'char*_p': 'abc',
+ 'str_i1': 'str', 'str_i2': 'str1', 'str_e': 'str2', 'str_p': 'str3',
+ 'int_i1': 42, 'int_i2': 42, 'int_e': 43, 'int_p': 44
+ }
+ assert z['l'] == [3, 6, 9, 12, 15]