Pybind11: выполнить привязку функции с помощью std::initializer_list

Я знаю, что здесь есть аналогичный вопрос: Привязка функции с Аргумент std::initializer_list с использованием pybind11, но поскольку я не могу комментировать (недостаточно репутации), я задаю свой вопрос здесь: применимы ли результаты из приведенного выше вопроса также к конструкторам: т.е. если у меня есть конструктор, который принимает std::initializer_list<T>, нет ли способа его связать?


person Quasar    schedule 14.03.2018    source источник


Ответы (1)


По крайней мере, нет простого способа связать его. По сути, как упоминалось в другом посте (и мой оригинальный ответ в системе отслеживания проблем pybind11), мы не можем динамически создавать std::initializer_list: это конструкция времени компиляции. Конструктор против метода против функции здесь не имеет значения: мы не можем преобразовать набор динамических аргументов в конструкцию initializer_list времени компиляции.

Но позвольте мне дать вам способ, которым вы могли бы частично обернуть его, если вы действительно застряли в дизайне C++, который требует этого. Сначала вы должны решить, сколько аргументов вы собираетесь поддерживать. Например, предположим, что вы хотите поддерживать 1, 2 или 3 аргумента int, передаваемых через initializer_list<int> в связанном конструкторе для MyType. Вы можете написать:

#include <stl.h>

py::class_<MyType>(m, "MyClass")
    .def(py::init([](int a) { return new MyClass({ a }); }))
    .def(py::init([](int a, int b) { return new MyClass({ a, b }); }))
    .def(py::init([](int a, int b, int c) { return new MyClass({ a, b, c }); }))
    .def(py::init([](std::vector<int> v) {
        if (vals.size() == 1) return new MyClass({ v[0] });
        elsif (vals.size() == 2) return new MyClass({ v[0], v[1] });
        elsif (vals.size() == 3) return new MyClass({ v[0], v[1], v[2] });
        else throw std::runtime_error("Wrong number of ints for a MyClass");
    });

где первые три перегрузки принимают целочисленные значения в качестве аргументов, а последняя принимает список. (Нет причин, по которым вы должны использовать оба подхода — я делаю это просто для примера).

Оба они довольно грубы и плохо масштабируются, но они демонстрируют фундаментальную проблему: каждый размер initializer_list должен быть скомпилирован из другого фрагмента кода C++. И именно поэтому pybind11 не может его поддерживать: нам пришлось бы компилировать разные версии кода преобразования для каждой возможной длины аргумента initializer_list — и поэтому либо размер двоичного файла взрывается для любого числа аргументов, которое может, или существует произвольное ограничение размера аргумента, за пределами которого вы начинаете получать фатальную ошибку. Ни один из этих вариантов не является хорошим.

Изменить: Что касается вашего вопроса конкретно о конструкторах: здесь нет никакой разницы. Проблема в том, что мы не можем преобразовать аргументы в требуемый тип, а преобразование аргументов идентично для конструктора, метода или функции.

person Jason Rhinelander    schedule 24.03.2018