pybind/pybind11_json

What about enumerations?

barnou-psee opened this issue · 0 comments

Hi,

I must say I really like the idea of this utility to ease my parameters structures usage in Python. I encountered an issue with bound enumerations. I use enumerations quite a lot in C++ mainly in factories. I can easily have a nlohmann converters for them, I can easily bind them, but I'm lost when trying to have the two functionalities working together.

It's quite logic since the nlohmann json bindings expect classic types and I guess enumerations fall into a special structures. I tried to get how I could handle those errors but I struggle finding how I could do that.

module.cpp

#include 

#include "nlohmann/json.hpp"
// This is where I copied pybind11_json.hpp
#include "converters/json.h"

#define MODULE_NAME footest

namespace testing_pybind11_json {

enum class Foo : uint32_t { ZERO, ONE, TWO, THREE };
NLOHMANN_JSON_SERIALIZE_ENUM(Foo,
{{Foo::ZERO, "ZERO"},
{Foo::ONE, "ONE"},
{Foo::TWO, "TWO"},
{Foo::THREE, "THREE"}})

void print_json(const nlohmann::json &j) {
std::cout << std::endl << std::setw(4) << j.dump() << std::endl;
}

void print_dict(const py::dict &d) {
nlohmann::json j;
j = d;
std::cout << std::endl << std::setw(4) << j.dump() << std::endl;
}

} // namespace testing_pybind11_json

PYBIND11_MODULE(MODULE_NAME, m) {
Py_Initialize();
PyEval_InitThreads();

py::enum_<testing_pybind11_json::Foo>(m, "Foo")
    .value("ZERO", testing_pybind11_json::Foo::ZERO)
    .value("ONE", testing_pybind11_json::Foo::ONE)
    .value("TWO", testing_pybind11_json::Foo::TWO)
    .value("THREE", testing_pybind11_json::Foo::THREE);
m.def("print_json", &testing_pybind11_json::print_json);
m.def("print_dict", &testing_pybind11_json::print_dict);

}

footest_pytest.cpp

import footest
import pytest

@pytest.mark.module("binding.utils")
def pytestcase_testing_pybind11_print_json():
footest.print_json({'a': 1, 'b': 2})
footest.print_json({'a': 1, 'b': 2, 'c': footest.Foo.ONE})

@pytest.mark.module("binding.utils")
def pytestcase_testing_pybind11_print_dict():
footest.print_dict({'a': 1, 'b': 2})
footest.print_dict({'a': 1, 'b': 2, 'c': footest.Foo.ONE})

It returns me different type of error depending on the function I'm using.

With print_json: it tries to use directly type_caster but seems to fall in a strange case.

    @pytest.mark.module("binding.utils")
    def pytestcase_testing_pybind11_print_json():
        mci.print_json({'a': 1, 'b': 2})
>       mci.print_json({'a': 1, 'b': 2, 'c': mci.Foo.ONE})
E       TypeError: print_json(): incompatible function arguments. The following argument types are supported:
E           1. (arg0: json) -> None
E       
E       Invoked with: {'a': 1, 'b': 2, 'c': <Foo.ONE: 1>}

sdk/modules/computational_imaging/python/tests/bindings/calibration_pytest.py:148: TypeError

With print_dict: I avoid using type_caster but calls directly the to_json function which goes well until the incompatible enum.

    @pytest.mark.module("binding.utils")
    def pytestcase_testing_pybind11_print_dict():
        mci.print_dict({'a': 1, 'b': 2})
>       mci.print_dict({'a': 1, 'b': 2, 'c': mci.Foo.ONE})
E       RuntimeError: to_json not implemented for this type of object: <Foo.ONE: 1>

sdk/modules/computational_imaging/python/tests/bindings/calibration_pytest.py:154: RuntimeError

I don't know if it's even possible to catch the enumeration case and call the to_json / from_json of the underlying enumeration.