在 PyObject_NEW 创建时初始化非平凡的 class 成员

Initialize nontrivial class members when created by PyObject_NEW

我有一个 Python object 引用 C++ object。

Header:

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>

typedef struct {
    PyObject_HEAD
    FrameBuffer frame_buffer;
} PyFrameBuffer;

extern PyTypeObject PyFrameBuffer_type;

extern PyObject* PyFrameBuffer_NEW(size_t width, size_t height);

C++ FrameBuffer class 如下:

class FrameBuffer {
public:
    explicit FrameBuffer(const size_t width, const size_t height);

    // ...

private:
    size_t m_width;
    size_t m_height;
    std::vector<Pixel> m_pixels;
};

Pixel 是几个普通类型元素的简单 class。

PyFrameBuffer_NEW()函数如下:

PyObject* PyFrameBuffer_NEW(size_t width, size_t height)
{
    auto* fb = (PyFrameBuffer*) PyObject_NEW(PyFrameBuffer, &PyFrameBuffer_type);

    if (fb != nullptr)
    {
        fb->frame_buffer = FrameBuffer(width, height);
    }

    return (PyObject*) fb;
}

此代码崩溃:

fb->frame_buffer 分配了未定义的值(fb->frame_buffer.m_pixels.size() 未定义)。

fb->frame_buffer 赋值期间,m_pixels.

中有未定义数量的 Pixel 的重新分配

所以,它崩溃了。

此代码似乎 有效:

/// ...
    if (fb != nullptr)
    {
        FrameBuffer fb_tmp(width, height);
        std::memcpy(&fb->frame_buffer, &fb_tmp, sizeof(FrameBuffer));
    }
// ...

但我怀疑这不是解决这个问题的正确方法(我怀疑 fb_tmp 中的 Pixel 被分配到范围之外。

我已经成功使用了类似于

的代码
typedef struct {
    PyObject_HEAD
    FrameBuffer *frame_buffer;
} PyFrameBuffer;

然后

if (fb != nullptr)
{
    fb->frame_buffer = new FrameBuffer( ... );
}

但是您必须考虑明确删除该对象。您需要在删除 C++ FrameBuffer 对象的 PyFrameBuffer 初始化中添加 tp_dealloc() function

delete fb->frame_buffer;

还有:

当您使用 C++ 创建 Python 扩展模块时,您正在链接代码的 C++ 运行时库。如果多个模块使用 C++,则它们 所有 都必须使用完全相同的 C++ 运行时库。