Как сделать обертку для обычной функции доступной для выбора с помощью pybind11? - PullRequest
0 голосов
/ 06 апреля 2020

У меня есть класс в c ++, содержащий среди прочего std::function. Через pybind11 я создал привязку для этого класса, которая работала нормально. Теперь я хотел бы сделать класс маринованным. Однако, при попытке выбрать экземпляр класса в python, я получаю TypeError: can't pickle PyCapsule objects.

Чтобы быть точным, вот минимальный пример: класс c ++ выглядит как

#include<functional>

using Function = std::function<double(double)>;

struct Test {
    Function func;
};

Код для генерации python привязки читает

#include "pybind11/pybind11.h"
#include "pybind11/functional.h"


namespace py = pybind11;


PYBIND11_MODULE(my_module, m) {
    py::class_<Test>(m, "Test")
        .def(py::init<Function>())
        .def(py::pickle(
                    [](const Test& t)
                    {
                        return py::make_tuple(t.func);
                    },
                    [](py::tuple tup)
                    {
                        return Test{tup[0].cast<Function>()};
                    }
                    ));
}

Теперь (после того, как выше было построено), если кто-то пытается выбрать экземпляр класса в python

import pickle

from my_module import Test


def identity(entity):
    return entity


if __name__ == "__main__":
    test = Test(identity)
    with open('test.pickle', 'wb') as out_file:
        pickle.dump(test, out_file)

каждый получает ошибку, упомянутую выше:

TypeError: can't pickle PyCapsule objects

, которая поднимается линией pickle.dump(test, out_file).

Есть ли способ заставить травление работать без потери общности? То есть мне бы очень хотелось иметь возможность вызывать класс с произвольным python вызовом с подписью float -> float.

Получается такая же ошибка, если func заключен в py::cpp_function, то есть, если заменить return py::make_tuple(t.func) на return py::make_tuple(py::cpp_function{t.func}).

...