Возврат и передача необработанных указателей POD (массивов) с помощью Python, C++ и pybind11

У меня есть функция C++, которая возвращает необработанный указатель float, и другая функция C++, которая принимает необработанный указатель float в качестве аргумента. Что-то типа:

float* ptr = something;
float* get_ptr(void) { return ptr; }
void use_ptr(float* ptr) { do_work(ptr); }

Я хочу иметь возможность передавать указатели с помощью Python. Что-то вроде этого:

import my_native_functions as native
ptr = native.get_ptr()
native.use_ptr(ptr)

Я использую pybind11 для создания собственного модуля Python, но я не знаю, как создать привязки для функции get_ptr(). Если я просто сделаю следующее:

PYBIND11_MODULE(my_native_functions, m)
{
    m.def("get_ptr", &get_ptr);
    m.def("use_ptr", &use_ptr);
}

функция get_ptr() возвращает объект Python Float. Я думаю, это имеет смысл, потому что в python нет типов указателей. Однако, поскольку теперь это просто Float, когда я вызываю функцию use_ptr() и перебираю указатель в C/C++, только первый элемент массива является правильным. Остальное мусор. Чтобы исправить это, в C++ мне нужно привести указатель к/от std::size_t. Делая это, все работает просто отлично.

Тем не менее, я хотел бы спросить: существует ли «правильный способ» достижения вышеизложенного без приведения к/от std::size_t с помощью pybind11?

Если вам интересно, почему я это делаю: я понимаю, что то, что я делаю, небезопасно для типов. Кроме того, я никогда не касаюсь указателя/целого числа на стороне Python. Я просто извлекаю его из одного нативного модуля и передаю в другой. Кроме того, я не могу привести указатель к какому-то пустому представлению, потому что указатель не всегда находится на ЦП. Иногда я хочу передать указатели CUDA. Создание py::array_t из указателя CUDA невозможно, если я не скопирую данные (а я не хочу этого делать).

Спасибо.


person AstrOne    schedule 26.02.2018    source источник


Ответы (1)


Оберните необработанный указатель в пользовательский класс «умных» указателей (только притворяясь действительно умным), как описано здесь. Вы можете добавить в этот класс некоторую дополнительную информацию, например, размер элемента массива и количество элементов. Это сделало бы его обобщенным дескриптором массива на стороне C++ (но не на стороне Python, потому что вы не предоставляете необработанный указатель Python).

Для более простого варианта просто оберните указатель в любой старый класс, чтобы скрыть его от Python. Нет необходимости предоставлять его Python в качестве пользовательского интеллектуального указателя. Вот пример, который делает именно это:

#include <pybind11/pybind11.h>
#include <memory>
#include <iostream>

namespace py = pybind11;

template <class T> class ptr_wrapper
{
    public:
        ptr_wrapper() : ptr(nullptr) {}
        ptr_wrapper(T* ptr) : ptr(ptr) {}
        ptr_wrapper(const ptr_wrapper& other) : ptr(other.ptr) {}
        T& operator* () const { return *ptr; }
        T* operator->() const { return  ptr; }
        T* get() const { return ptr; }
        void destroy() { delete ptr; }
        T& operator[](std::size_t idx) const { return ptr[idx]; }
    private:
        T* ptr;
};

float array[3] = { 3.14, 2.18, -1 };

ptr_wrapper<float> get_ptr(void) { return array; }
void use_ptr(ptr_wrapper<float> ptr) {
    for (int i = 0; i < 3; ++i)
        std::cout << ptr[i] << " ";
    std::cout << "\n";
}

PYBIND11_MODULE(Ptr,m)
{
    py::class_<ptr_wrapper<float>>(m,"pfloat");
    m.def("get_ptr", &get_ptr);
    m.def("use_ptr", &use_ptr);
}
person n. 1.8e9-where's-my-share m.    schedule 26.02.2018
comment
Спасибо за ваш ответ, мой друг. Это хорошая идея. К сожалению, я немного запутался в документации и не совсем уверен, как сделать то, что вы описали. Любые примеры более чем приветствуются! - person AstrOne; 27.02.2018
comment
@AstrOne я добавил пример - person n. 1.8e9-where's-my-share m.; 27.02.2018