PyCapsule_New失败时指针所有权

时间:2017-03-15 19:09:14

标签: python python-c-api

PyCapsule_New接受一个析构函数,当胶囊被破坏时调用该函数:

PyObject* PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)

我正在尝试使用此机制将C ++代码创建的对象的所有权传递给Python。具体来说,析构函数只需调用" delete"对象。

auto ptr = make_unique<ObjType>(arg);
PyObject * ret = PyCapsule_New(ptr.release(), nullptr, Destroyer);

void Destroyer(PyObject *capsule)
{
    auto rawPtr = static_cast<ObjType*>(PyCapsule_GetPointer(capsule, nullptr));
    delete rawPtr;
}

在我看来,这里存在潜在的内存泄漏:如果PyCapsule_New失败,释放的原始指针将变为悬空状态。我试图从Python C API文档中获得确认。但是,它只提到失败时设置了异常,并返回NULL。它没有谈论所有权。

假设指针悬空是合理的,因为如果首先没有生成包,那么就没有处理程序可以传递给析构函数。

但是,我不确定PyCapsule_New是否在内部调用析构函数,具体来说:

  • 在PyCapsule_New内部,胶囊结构几乎完成。
  • 失败发生在它返回之前。
  • PyCapsule_New设置异常,在调用析构函数后返回NULL, (???)

如果突出显示的部分永远不会发生,我觉得上面的代码必须重写为

auto ptr = make_unique<ObjType>(arg);
PyObject * ret = PyCapsule_New(ptr.get(), nullptr, Destroyer);
if (ret != nullptr)
    ptr.release();

有人可以帮助确认是否属实?

1 个答案:

答案 0 :(得分:0)

根据建议更改评论以回答。

简短回答:不,当PyCapsule_New失败时,它不会召唤毁灭者。

请参阅https://github.com/python/cpython/blob/master/Objects/capsule.c#L44

中的实施
PyObject *
PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
{
    PyCapsule *capsule;

    if (!pointer) {
        PyErr_SetString(PyExc_ValueError, "PyCapsule_New called with null pointer");
        return NULL;
    }

    capsule = PyObject_NEW(PyCapsule, &PyCapsule_Type);
    if (capsule == NULL) {
        return NULL;
    }

    capsule->pointer = pointer;
    capsule->name = name;
    capsule->context = NULL;
    capsule->destructor = destructor;

    return (PyObject *)capsule;
}

因此,第一个实现确实包含潜在的内存泄漏。只有在PyCapsule_New成功时才会调用“release()”。