string to enum conversion regression after enum rewrite?
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 18k
- Forks
- 2.3k
- Avg merge
- 5d 17h
- Merged PRs (30d)
- 10
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: https://github.com/pybind/pybind11/issues/483 and https://github.com/pybind/pybind11/pull/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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the enum_ rewrite and the PYBIND11_ENUM_OP_CONV and PYBIND11_ENUM_OP_CONV_LHS macros, then run the reproducible example in example.cc with test.py against the reported versions. Compare string conversion through implicitly_convertible with the v2.2.4 and v2.3.0+ outputs; done means the intended string-enum behavior is established and the regression or supported limitation is covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100