У меня есть набор модулей, которые я написал на C++ и экспортировал в Python с помощью pybind11. Все эти модули можно использовать независимо, но они используют общий набор настраиваемых типов, определенных в служебной библиотеке.
В каждом модуле есть код вроде того, что ниже. Заголовок Color.hpp определяет типы, которые используются в служебной библиотеке.
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <string>
#include "Color.hpp"
std::vector<Color> buncha_colors(int n, std::string &color) {
std::vector<Color> out;
for (;n-- > 0;) {
out.push_back(Color(color));
}
return out;
}
PYBIND11_MODULE(pb11_example_module, m) {
m.def("buncha_colors", &buncha_colors);
}
Конечно, это не работает. Pybind не знает, как выполнить преобразование типа для объекта Color. Ответ (или, надеюсь, нет) - определить класс Color как часть модуля. После этого pybind может выполнять автоматическое преобразование типов.
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <string>
#include "Colors.hpp"
std::vector<Color> buncha_colors(int n, std::string &color) {
std::vector<Color> out;
for (;n-- > 0;) {
out.push_back(Color(color));
}
return out;
}
PYBIND11_MODULE(pb11_example_module, m) {
pybind11::class_<Color>(m, "Color")
.def(pybind11::init<std::string&>())
.def("name", &Color::name);
m.def("buncha_colors", &buncha_colors);
}
В идеале я хотел бы сохранить все эти пользовательские типы утилит и связанные с ними функции в отдельном модуле от всех модулей, которые их используют. Но мне нужно было бы определить преобразования типов или иным образом ссылаться на них в каждом модуле, который его использует. Как мне это сделать? Мне не нужны pb11_example_module.Color, utils.Color и так далее. Я понятия не имею об их совместимости с одним, и это кажется неправильным.






Это началось как редактирование, но оказалось моим ответом.
Это интересно. Используя первый пример, где Color не экспортируется в модуле pybind ...
$ python
>>> import pb11_example_module
>>> pb11_example_module.buncha_colors(10, "red")[0].name()
TypeError: Unable to convert function return value to a Python type! The signature was
(arg0: int, arg1: str) -> List[Color]
>>> import utils # defines Color
>>> pb11_example_module.buncha_colors(10, "red")[0].name()
'red'
Также работает импорт служебной библиотеки перед импортом модуля-примера. Изменение имени класса "Color" на другое также не нарушает использования, поэтому он должен получать преобразование типа из другого модуля с использованием сигнатуры типа.
Если преобразование типа для этого типа C++ определено перед использованием, автоматическое преобразование типа работает. Утилиты преобразования типов, которые pybind использует для Python, являются глобальными и просматриваются во время выполнения. Вы можете прочитать все об этом здесь. Вышеупомянутое решение загрузки преобразования типа для настраиваемого типа в любой момент перед использованием является поддерживаемым идиоматическим решением.
@EricCousineau Отличный совет!
В качестве продолжения, я думаю, что более идеальное решение (в ваших привязках) - убедиться, что вы импортировали модуль с помощью
py::module::import("my_package.my_depdendency"). В конечном итоге, поскольку вы выполняете привязку к тому глобальному реестру, который вы упомянули, импорт модулей имеет побочный эффект - регистрацию типа через RTTI. Если вы импортируете зависимость модуля в свои привязки, все готово. Вот пример теста PR +, который быстро завершается неудачно, если он забыт (для нашего кода): github.com/RobotLocomotion/drake/pull/14072/files