Как использовать shared_ptr и make_shared с массивами? - PullRequest
0 голосов
/ 04 августа 2020

Я хочу использовать C ++ shared_ptr в качестве замены необработанных C указателей. В качестве простого примера, следующий код, кажется, работает так, как задумано:

from libcpp.memory cimport shared_ptr, allocator

cdef shared_ptr[double] spd 
cdef allocator[double] allo
spd.reset(allo.allocate(13))

Размер здесь выбран как 13, но, как правило, он не известен во время компиляции. Я не уверен, что это правильно, но у меня не было ошибок (пока нет утечек памяти и ошибок). Мне любопытно, есть ли более оптимальное решение с make_shared.

Но я не могу использовать массивы C ++ 11, потому что Cython не позволяет использовать литералы в качестве шаблонов, например, что-то вроде

cdef shared_ptr[array[double]] spd = make_shared[array[double,13]]()

и «обычные» массивы, которые должны работать с компилятором C ++ 20 (например, g cc 10), так или иначе вызывают проблемы:

# Cython error "Expected an identifier or literal"
cdef shared_ptr[double[]] spd = make_shared[double[]](3)    

# Can't get ptr to work
ctypedef double[] darr
cdef shared_ptr[darr] spd = make_shared[darr](13)
cdef double* ptr = spd.get()    # or spd.get()[0] or <double*> spd.get()[0] or ...

Является ли решение распределителя правильный и лучший или есть другой способ как это сделать?

1 Ответ

0 голосов
/ 08 августа 2020

Вот что я собираюсь использовать с

cdef extern from *:
    """
    template <typename T>
    struct Ptr_deleter{
        size_t nn;
        void (*free_ptr)(T*, size_t);
        Ptr_deleter(size_t nIn, void (*free_ptrIn)(T*, size_t)){
            this->nn = nIn;
            this->free_ptr = free_ptrIn;
        };
        void operator()(T* ptr){
            free_ptr(ptr, nn);
        };
    };
    template <typename T>
    std::shared_ptr<T> ptr_to_sptr (T* ptr, size_t nn, void (*free_ptr)(T*, size_t)) {
        Ptr_deleter dltr(nn,  free_ptr);
        std::shared_ptr<T> sp (ptr, dltr);
        return sp;
    };
    """
    shared_ptr[double] d_ptr_to_sptr "ptr_to_sptr"(double* ptr, size_t nn, void (*free_ptr)(double*, size_t) nogil) nogil

cdef void free_d_ptr(double* ptr, size_t nn) nogil:
    free(ptr)

cdef shared_ptr[double] sp_d_empty(size_t nn) nogil:
    return d_ptr_to_sptr(<double*> nullCheckMalloc(nn*sizeof(double)), nn, &free_d_ptr)

Насколько я понимаю, «правильный» способ обработки массивов malloc ed - это использование специального средства удаления, как это сделал я. Лично я предпочитаю использовать несколько необработанные C указатели (double* вместо double[] или что-то в этом роде), поскольку это более естественно в Cython и моих проектах.

Я думаю, довольно легко понять, как измените free_ptr для более сложных типов данных. Для простых типов данных это можно было бы сделать с меньшим количеством строк и менее запутанным, но я хотел иметь такую ​​же базу. необработанные указатели кода в shared_ptr.

При работе с C ++ (особенно с новыми стандартами, такими как C ++ 20) я думаю, что очень часто требуется дословный код. Но я намеренно определил free_d_ptr в Cython, поэтому можно легко использовать существующий код Cython для выполнения фактической работы, выполняемой для освобождения / очистки / любого массива.

Я не получил C ++ 11 std::arrays для работы, и, по всей видимости, это «неправильно» возможно в Cython в целом (см. Взаимодействие массива C ++ 11 с Cython ).

Я не получил double[] или аналогично работе (возможно в C ++ 20), но с дословным кодом C ++ я думаю, что это должно быть выполнено в Cython. Как я уже сказал, я предпочитаю больше C -подобных указателей / массивов.

...