Search code examples
pythonc++enumsdefaultpybind11

How to provide default enum value with pybind11?


I want to have functions accepting enums default params.

But I couldn't found what is the proper way to provide enum defaults in the PyBind11 enum example and documentation, for example:

struct Pet
{
    enum Kind
    {
        Dog = 0,
        Cat
    };

    Pet(const std::string &name) : name(name)
    {
    }

    void setName(const std::string &name_)
    {
        name = name_;
    }

    const std::string &getName() const
    {
        return name;
    }

    Kind test(Kind kind = Dog)
    {
        if(kind == Dog)
           std::cout << "Dog" << std::endl;

        if(kind == Cat)
           std::cout << "Cat" << std::endl;

        return kind;
    }

    std::string name;
};

PYBIND11_MODULE(pet,m)
{
    py::class_<Pet> pet(m, "Pet");
    pet.def(py::init<const std::string &>())
       .def("setName", &Pet::setName)
       .def("getName", &Pet::getName)
       .def("test", &Pet::test, py::arg("kind") = Pet::Kind::Dog)
       .def("__repr__", [](const Pet &a) { return "<example.Pet named '" + a.name + "'>"; }
    );

    py::enum_<Pet::Kind>(pet, "Kind")
        .value("Dog", Pet::Kind::Dog)
        .value("Cat", Pet::Kind::Cat)
        .export_values();

But it does not works:

py::arg("kind") = Pet::Kind::Dog 

When I run it in Python I am getting errors.

from pet import Pet as p
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: arg(): could not convert default argument into a Python object (type not registered yet?). Compile in debug mode for more information.

I am getting errors and when try to initialize it with a string value, i.e "Dog" or to 1.


Solution

  • It's a simple ordering problem: on load, the statements in the module definition are executed to create the Python classes, functions, etc. So, define Kind first (on the Python-side, that is) to allow the interpreter to find it during setting of the defaults when it's defining test later on. I.e., use this order:

    PYBIND11_MODULE(pet,m)
    {
        py::class_<Pet> pet(m, "Pet");
    
        py::enum_<Pet::Kind>(pet, "Kind")
            .value("Dog", Pet::Kind::Dog)
            .value("Cat", Pet::Kind::Cat)
            .export_values();
    
        pet.def(py::init<const std::string &>())
           .def("setName", &Pet::setName)
           .def("getName", &Pet::getName)
           .def("test", &Pet::test, py::arg("kind") = Pet::Kind::Dog)
           .def("__repr__", [](const Pet &a) { return "<example.Pet named '" + a.name + "'>"; }
        );
    
    }