包装 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);
        });