包装 pybind11 重载方法的 python 参数
Wrap python argument for pybind11 overloaded methods
我正在尝试为一些遗留 C++ 代码创建 python 绑定,这些代码实现了它自己的 'extended' 字符串 class aString
,它被广泛使用。这适用于 .def(py::init<const std::string &, int>())
,但一旦我开始尝试包装重载方法,pybind11 似乎不会自动将 std::string
转换为 aString
。 MWE 改编自 pybind11 文档:
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)
在 python 中,以下工作正常:
import example
p = example.Pet("Penny", 1)
p.set(3)
a = example.aString("Anne")
p.set(a)
但是,以下操作失败并显示消息:
#> 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'
我尝试将我的 pybinding 更改为 .def("set", static_cast<void (Pet::*)(const std::string &)>(&Pet::set), "Set the pet's name")
而不是 aString
,但这不会编译,因为 std::string
不是重载实例之一。我无法在 C++ 库中为 std:string
添加额外的重载实例。
我希望能够调用 p.set("Jenny")
并让 pybind11 将 "Jenny"
转换为 aString("Jenny")
,而不是 p.set(example.aString("Jenny"))
。这是我使用 pybind11 的第一个项目,如有任何帮助,我们将不胜感激,谢谢!
您可以通过添加以下语句在类型之间启用 implicit conversion:
py::implicitly_convertible<std::string, aString>();
一般来说,py::implicitly_convertible<A, B>()
只有在(如您的情况)B
有一个以 A
作为唯一参数的构造函数时才有效。
我已将 @lr1985 的答案标记为正确,因为它对我来说是最简单的解决方案 - 我不需要为每个方法调用手动将每个 std::string
转换为 aString
。
但是,如果有人绝对必须包装或添加另一种类型的重载,我确实找到了解决方案:
py::class_<Pet>(m, "Pet")
// ...
.def("set",
[](Pet &self, const std::string &s)
{
aString as = aString(s);
self.set(as);
});
我正在尝试为一些遗留 C++ 代码创建 python 绑定,这些代码实现了它自己的 'extended' 字符串 class aString
,它被广泛使用。这适用于 .def(py::init<const std::string &, int>())
,但一旦我开始尝试包装重载方法,pybind11 似乎不会自动将 std::string
转换为 aString
。 MWE 改编自 pybind11 文档:
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)
在 python 中,以下工作正常:
import example
p = example.Pet("Penny", 1)
p.set(3)
a = example.aString("Anne")
p.set(a)
但是,以下操作失败并显示消息:
#> 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'
我尝试将我的 pybinding 更改为 .def("set", static_cast<void (Pet::*)(const std::string &)>(&Pet::set), "Set the pet's name")
而不是 aString
,但这不会编译,因为 std::string
不是重载实例之一。我无法在 C++ 库中为 std:string
添加额外的重载实例。
我希望能够调用 p.set("Jenny")
并让 pybind11 将 "Jenny"
转换为 aString("Jenny")
,而不是 p.set(example.aString("Jenny"))
。这是我使用 pybind11 的第一个项目,如有任何帮助,我们将不胜感激,谢谢!
您可以通过添加以下语句在类型之间启用 implicit conversion:
py::implicitly_convertible<std::string, aString>();
一般来说,py::implicitly_convertible<A, B>()
只有在(如您的情况)B
有一个以 A
作为唯一参数的构造函数时才有效。
我已将 @lr1985 的答案标记为正确,因为它对我来说是最简单的解决方案 - 我不需要为每个方法调用手动将每个 std::string
转换为 aString
。
但是,如果有人绝对必须包装或添加另一种类型的重载,我确实找到了解决方案:
py::class_<Pet>(m, "Pet")
// ...
.def("set",
[](Pet &self, const std::string &s)
{
aString as = aString(s);
self.set(as);
});