【问题标题】:How to use shared_ptr and make_shared with arrays?如何将 shared_ptr 和 make_shared 与数组一起使用?
【发布时间】:2020-08-04 20:09:07
【问题描述】:

我想使用 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 编译器(例如 gcc 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 ...

分配器解决方案是正确和最好的解决方案,还是有另一种方法?

【问题讨论】:

  • 您的解决方案“有效”(可能是 UB)用于双精度,但会泄漏内存用于更复杂的类,因为 shared_ptr 只会破坏第一个对象,而不破坏其他 12 个对象。为什么不使用 std: :vector 而不是整个 shared_ptr-business?
  • Btw 数组没有包含在 cython 中,所以也许你做错了?请提供minimal reproducible example
  • 据我所知,如果在编译时不知道大小,则无法使用 std::array 类。即使这是一个相当老套的解决方案 (stackoverflow.com/questions/36357024/…)。
  • 我有一些用例,我希望在函数返回“指针”时避免复制数据(因此没有向量),但仍然需要某种引用计数(使用 C 指针我'我手动完成,shared_ptr 会简化)。

标签: cython shared-ptr make-shared


【解决方案1】:

这就是我要去的地方

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)

我的理解是处理malloced 数组的“正确”方法是像我一样使用自定义删除器。我个人更喜欢坚持使用有点原始的 C 指针(double* 而不是 double[] 或其他东西),因为它在 Cython 和我的项目中更自然。

我认为很容易看出如何将free_ptr 更改为更复杂的数据类型。对于简单的数据类型,它可以用更少的行和更少的复杂性来完成,但我希望有相同的基础。

我喜欢我的解决方案,因为我可以将现有的 Cython/C 代码原始指针“包装”在 shared_ptr 中。

在使用 C++(尤其是像 C++20 这样的新标准)时,我认为逐字代码通常是必要的。但是我特意在 Cython 中定义了free_d_ptr,因此可以很容易地使用现有的 Cython 代码来处理释放/清除/任何数组的实际工作。

我没有让 C++11 std::arrays 工作,而且在 Cython 中显然不可能“正常”运行(请参阅 Interfacing C++11 array with Cython)。

我也没有得到 double[] 或类似的工作(在 C++20 中是可能的),但是对于逐字 C++ 代码,我认为这在 Cython 中应该是可行的。正如我所说,我更喜欢更多类似 C 的指针/数组。

【讨论】:

    猜你喜欢
    • 2015-05-18
    • 2018-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多