在Python扩展中操作来自SWIG的共享指针

1 投票
1 回答
776 浏览
提问于 2025-04-18 07:55

我正在尝试通过一个 Python 3.4 的扩展来访问一个已经存在的 C++ 对象。这个对象是通过 SWIG 绑定从 C++ 传过来的。现在我想建立一个 C++ 的 Python 扩展,以便能够访问这个对象。

我这样做的原因是,因为创建这个对象的库是一个外部库。所以我使用 Python 的绑定来生成对象,现在我需要在 C++ 中访问这个对象。

更具体一点,我使用的对象是 Magick::Image(来自 ImageMagick++),我想把这个对象传递给一个 Python 扩展,然后把图像转换成一个 numpy 的 ndarray。

这里的难点在于,我是通过一个共享指针来访问 Magick::Image 对象。

在 Python 中,我有这样的代码:

img = get_my_magick_image()
print(img)

<Swig Object of type 'std::tr1::shared_ptr< Magick::Image > *' at 0x7f32f6ecdd80>

这是我写的 C++ 扩展,目的是能够访问 Magick::Image 的方法,但我只能访问 shared_ptr 的方法……当我尝试访问 Magick::Image 的方法时,程序崩溃了。我在 Python 中使用 readimg(img) 来调用它。

PyObject* readimg(PyObject* self, PyObject* args) {
    std::tr1::shared_ptr< Magick::Image > * img;

    if (!PyArg_ParseTuple(args, "O", &img)) {
         PyErr_SetString(PyExc_TypeError, "error with parameters");
         return 0;
    }

    std::cout << "img : " << img << std::endl;
    std::cout << "&img : " << &img << std::endl;
    std::cout << "typeid(img).name() : " << typeid(img).name() << std::endl;
    std::cout << "typeid((*img)).name() : " << typeid((*img)).name() << std::endl;
    std::cout << "img->use_count()" << img->use_count() << std::endl;
    std::cout << "img->unique()" << img->unique() << std::endl;
    std::cout << "img->get()" << img->get() << std::endl;
    std::cout << "(*img).use_count()" << (*img).use_count() << std::endl;
    std::cout << "(*img)" << (*img) << std::endl;

    Magick::Geometry size = (*img)->size(); // SEGFAULT HERE
    unsigned h = size.height();
    std::cout << h << std::endl;
}

相关的输出是:

img : 0x7f32f6ecdd80
&img : 0x7fff280fbad8
typeid(img).name() : PNSt3tr110shared_ptrIN6Magick5ImageEEE
typeid((*img)).name() : NSt3tr110shared_ptrIN6Magick5ImageEEE
img->use_count() : 10296384
img->unique() : 0
img->get() : 0x2
(*img).use_count() : 10296384
(*img) : 0x2
Segmentation fault

有没有人知道为什么我无法访问 Magick::Image 的方法?

1 个回答

1

我不是专家,但看起来img没有正确初始化。你有没有看到文档中的以下内容?:

其他对象

O (object) [PyObject *] 在C语言的对象指针中存储一个Python对象(不进行任何转换)。这样,C程序就能接收到实际传入的对象。对象的引用计数不会增加。存储的指针不是NULL。

这个函数存储的是原始C指针,所以我猜它和共享指针不兼容。如果你需要一个共享指针,我建议你可以尝试这样的方式:

Magick::Image* _img = NULL;
if (!PyArg_ParseTuple(args, "O", &_img)) {
     PyErr_SetString(PyExc_TypeError, "error with parameters");
     return 0;
}
std::tr1::shared_ptr< Magick::Image > img(_img);

撰写回答