Pybind11: init‹› с лямбдой

Я использую pybind11 в качестве оболочки моего кода C++ в библиотеке Python.

Бывает, что есть аргументы, которые я не могу предоставить, или иногда я хочу сделать преобразование/инициализацию, которую я знаю на стороне C++. Например, это может быть потому, что класс не известен в python. Как это можно было сделать? Единственное «решение», которое я вижу до сих пор, — это создать унаследованный прокси-класс в C++.

Пример: я хочу определить/связать класс python A:

class A:
  def __init__(self, B b):
    ...

С эквивалентным классом С++:

class A {
  A(C c, D d);
}

Есть ли какая-то лямбда или эквивалент, который я мог бы создать для pybind11::init‹>?


person Emile D.    schedule 10.06.2020    source источник
comment
Как бы вы получили экземпляр c и d из b? Ваш вопрос не совсем ясен... Не могли бы вы привести пример того, как вы хотите использовать класс на стороне Python и что вы хотели бы сделать на стороне C++, даже если это недействительный C++?   -  person Holt    schedule 10.06.2020
comment
Действительная точка. Я пытался быть универсальным, но это может быть преобразование std::string, известного в pybind11, например, в QString с информацией о используемой локали. Среди всех возможных примеров. Или другой случай, когда у меня было B = C, но D была локальной переменной (в моем случае указатель был инициализирован nullptr, таким образом, не такой локальный, но вы получаете духи желаемых вариантов использования).   -  person Emile D.    schedule 10.06.2020


Ответы (1)


pybind11 позволяет привязывать фабричные функции как методы инициализации. Таким образом, вам нужно было бы предоставить функцию на С++, которая принимала B и возвращала A, а затем вы могли бы связать это как метод инициализации для A.

Пример из документации pybind11

class Example {
private:
    Example(int); // private constructor
public:
    // Factory function:
    static Example create(int a) { return Example(a); }
};

py::class_<Example>(m, "Example")
    .def(py::init(&Example::create));

Вы также должны иметь возможность связываться со свободной функцией (а не только со статической функцией), если вы не хотите (или не можете) изменить класс A в С++.

Таким образом, это может выглядеть примерно так (изменено, чтобы возвращать unique_ptr, который pybind может просто взять на себя, а не необработанный экземпляр. Но любой из них должен работать)

std::unique_ptr<A> createA(const B& arg)
{
  // returns an instance of A that you made using B
}

py::class_<A>(m, "A")
    .def(py::init(&createA));

Очевидно, что тогда вы также должны предоставить привязку для B в python.

Документы находятся здесь и содержат еще больше примеров, в том числе о том, как выполнить лямбду инициализации: https://pybind11.readthedocs.io/en/stable/advanced/classes.html#custom-constructors

person Jesse C    schedule 10.06.2020