Связывание массива с помощью pybind11

У меня есть структура в c следующим образом

typedef struct _person{
         int id[10];
         int number[10];
}person;

Как можно связать это с помощью pybind11?


person tom    schedule 05.11.2019    source источник


Ответы (1)


Кажется, нет хорошего способа AFAICT, когда вы хотите, чтобы данные были доступны для записи (это несколько менее надумано, когда данные будут доступны только для чтения). В любом случае, следующее помогает, если у вас установлен numpy:

#include <pybind11/pybind11.h>
#include <pybind11/pytypes.h>
#include <pybind11/numpy.h>


typedef struct _person{
    int id[10];
    int number[10];
} person;

PYBIND11_MODULE(person, m)
{   
    pybind11::class_<person>(m, "person", pybind11::buffer_protocol())
        .def(pybind11::init<>())
        .def_property("id", [](person &p) -> pybind11::array {
            auto dtype = pybind11::dtype(pybind11::format_descriptor<int>::format());
            auto base = pybind11::array(dtype, {10}, {sizeof(int)});
            return pybind11::array(
                dtype, {10}, {sizeof(int)}, p.id, base);
        }, [](person& p) {})
        .def_property("number", [](person &p) -> pybind11::array {
            auto dtype = pybind11::dtype(pybind11::format_descriptor<int>::format());
            auto base = pybind11::array(dtype, {10}, {sizeof(int)});
            return pybind11::array(dtype, {10}, {sizeof(int)}, p.number, base);
        }, [](person& p) {});
}

Билет состоит в том, чтобы предоставить пустой базовый объект, который заставляет массив вести себя как объект представления. Без базы делает копию. Вам не нужен установщик свойств (который, если бы он был реализован, устанавливал бы массив, а не элементы массива) и мог бы вызвать ошибку вместо предоставления отсутствия операции, как это сделал я. Кроме того, если у вас действительно есть два массива одинакового размера, вы можете использовать вспомогательную функцию вместо лямбда-выражений.

Основная проблема для привязки встроенных массивов C заключается в том, что python не имеет правильного типа массива (есть базовое представление памяти и массив модулей, но нет истинного типа массива), поэтому вам нужно получить его откуда-то, а pybind11 предпочитает брать тот, что от numpy, так как это лучшая игра в городе.

Просто чтобы показать вам альтернативу, в cppyy (http://cppyy.org) я выбрал другой подход: имеет представление низкоуровневого массива, которое впоследствии может быть передано numpy для просмотра или копирования по желанию, поскольку оно реализует протокол полного буфера. Преимущество здесь в том, что пользователь Python может принять решение об окончательном использовании. Недостатком является то, что если вы все равно собираетесь использовать numpy, это дополнительный шаг. Но его также можно использовать напрямую без установки numpy:

import cppyy

cppyy.cppdef("""
typedef struct _person{
    int id[10];
    int number[10];
} person;
""")

p = cppyy.gbl.person()
print(len(p.id), p.id)
print(list(p.id))

который производит:

(10, <cppyy.LowLevelView object at 0x105ab33b0>)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
person Wim Lavrijsen    schedule 06.11.2019