I'm trying to create python bindings for some legacy C++ code which implemented its own 'extended' string class aString
which it uses extensively. This works fine for .def(py::init<const std::string &, int>())
, but once I start trying to wrap overloaded methods pybind11 does not appear to automatically cast std::string
to aString
. MWE adapted from the pybind11 docs:
src/example.cpp
:
#include <pybind11/pybind11.h>
// Extended string class and struct in legacy library
class aString : public std::string
{
public:
aString() : std::string() {}
aString(const char *str) : std::string(str) {}
aString(const std::string &str) : std::string(str) {}
};
struct Pet
{
Pet(const aString &name, int age) : name(name), age(age) {}
void set(int age_) { age = age_; }
void set(const aString &name_) { name = name_; }
aString name;
int age;
};
// Python bindings
namespace py = pybind11;
PYBIND11_MODULE(example, m)
{
py::class_<aString>(m, "aString")
.def(py::init<const std::string &>())
.def("__repr__",
[](const aString &a)
{
return "<aString ('" + a + "')>";
});
py::class_<Pet>(m, "Pet")
.def(py::init<const std::string &, int>())
.def("set", static_cast<void (Pet::*)(int)>(&Pet::set), "Set the pet's age")
.def("set", static_cast<void (Pet::*)(const aString &)>(&Pet::set), "Set the pet's name")
.def("__repr__",
[](const Pet &a)
{
return "<Pet ('" + a.name + "')>";
});
;
}
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.4)
project(example)
add_subdirectory(pybind11)
pybind11_add_module(example src/example.cpp)
In python, the following works fine:
import example
p = example.Pet("Penny", 1)
p.set(3)
a = example.aString("Anne")
p.set(a)
However, the following fails with the message:
#> p.set("Jenny")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-9-c5b0ff7c5315> in <module>
----> 1 p.set("Jenny")
TypeError: set(): incompatible function arguments. The following argument types are supported:
1. (self: example.Pet, arg0: int) -> None
2. (self: example.Pet, arg0: example.aString) -> None
Invoked with: <Pet ('Anne')>, 'Jenny'
I've tried changing my pybinding to be .def("set", static_cast<void (Pet::*)(const std::string &)>(&Pet::set), "Set the pet's name")
instead of aString
, but this will not compile as std::string
is not one of the overload instances. I cannot add an additional overload instance for std:string
in the C++ library.
I would like to be able to call p.set("Jenny")
and let pybind11 convert "Jenny"
into aString("Jenny")
, rather than p.set(example.aString("Jenny"))
. This is my first project with pybind11 so any help would be appreciated, thanks!
You can enable an implicit conversion between types by adding the following statement:
py::implicitly_convertible<std::string, aString>();
In general py::implicitly_convertible<A, B>()
works only if (as in your case) B
has a constructor that takes A
as its only argument.