-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Open
Description
Issue description
I have a string-enum converter that works using v2.2.4. This breaks starting from v2.3.0 due to the enum_ rewrite.
In v2.2.4, it was possible to support this conversion via implicitly_convertible
. It looks like other people are taking advantage of this feature as well: #483 and #1122.
From v2.3.0+, it appears that this codepath is not being covered by the new PYBIND11_ENUM_OP_CONV
and PYBIND11_ENUM_OP_CONV_LHS
implementations. These macros appear to only support integer conversion and fails to invoke the existing pathways enabled through implicitly_convertible
.
I am wondering if this is a regression or if only integer conversions will be allowed going forward?
Tested using v2.2.4, v2.3.0, v.2.4.3.
Reproducible example code
// example.cc
#include <pybind11/pybind11.h>
namespace py = pybind11;
template <typename T>
T pyStringToEnum(const py::enum_<T>& enm, const std::string& value) {
auto values = enm.attr("__members__").template cast<py::dict>();
auto strVal = py::str(value);
if (values.contains(strVal)) {
return T(values[strVal].template cast<T>());
}
throw "Invalid string value " + value + " for enum " + std::string(typeid(T).name());
}
template <typename T>
py::str enumToPyString(const py::enum_<T>& enm, const T& value) {
auto values = enm.attr("__members__").template cast<py::dict>();
for (auto val : values) {
if (T(val.second.template cast<T>()) == value) {
return py::str(val.first);
}
}
throw "Invalid value for enum " + std::string(typeid(T).name());
}
enum class Kind { X = 0, Y = 1, Z = 2 };
struct A {
A() : kind(Kind::X) {}
Kind kind;
};
PYBIND11_MODULE(example, m) {
py::enum_<Kind> enm(m, "Kind");
enm
.value("X", Kind::X)
.value("Y", Kind::Y)
.value("Z", Kind::Z)
.def(py::init([enm](const std::string& value) -> Kind {
return pyStringToEnum(enm, py::str(value));
}))
.def("__str__", [enm](Kind e) { return enumToPyString(enm, e); });
py::implicitly_convertible<std::string, Kind>();
py::implicitly_convertible<int, Kind>();
py::class_<A>(m, "A")
.def(py::init<>())
.def_readwrite("kind", &A::kind);
}
$ g++ -std=c++11 -fPIC -shared $(python-config --includes) $(python-config --libs) -Ipybind11-${VERSION}/include/ example.cc -o example.so
# test.py
from example import A
a = A()
kind = a.kind
s = 'kind == "X"'
print '{:15} -> expected: {:<5} result: {:<5}'.format(s, True, eval(s))
s = 'kind == None'
print '{:15} -> expected: {:<5} result: {:<5}'.format(s, False, eval(s))
s = 'kind == 0'
print '{:15} -> expected: {:<5} result: {:<5}'.format(s, True, eval(s))
s = 'kind == 1'
print '{:15} -> expected: {:<5} result: {:<5}'.format(s, False, eval(s))
# output v2.3.0+
kind == "X" -> expected: 1 result: 0
kind == None -> expected: 0 result: 0
kind == 0 -> expected: 1 result: 0
kind == 1 -> expected: 0 result: 0
# output v2.2.4
kind == "X" -> expected: 1 result: 1
kind == None -> expected: 0 result: 0
kind == 0 -> expected: 1 result: 1
kind == 1 -> expected: 0 result: 0
Skylion007 and gsmecher
Metadata
Metadata
Assignees
Labels
No labels