【问题标题】:Pointer-type mismatch with PyArray_SimpleNew指针类型与 PyArray_SimpleNew 不匹配
【发布时间】:2015-03-10 12:08:15
【问题描述】:

我正在使用 C API 为带有 Numpy 的 Python 创建一个模块,但遇到了与 PyArray_SimpleNew 的输出不兼容的奇怪问题,我想了解一下。但首先是一个最小的例子:

# include <Python.h>
# include <numpy/arrayobject.h>

void foo()
{
    Py_Initialize();
    import_array();

    npy_intp dims[1] = {42};
    PyObject * A = PyArray_SimpleNew(1,dims,NPY_DOUBLE); // Line A

    Py_Finalize();
}

int main()
{
    foo();
    return 0;
}

如果我用 gcc source.c -lpython2.7 -I/usr/include/python2.7 --pedantic 编译它,我会得到(参考 A 行):

ISO C 禁止将对象指针转换为函数指针类型

所以,显然,PyArrayObjects 出于某种原因应该是函数指针。


根据文档(例如,here),PyArray_SimpleNew 的返回类型为 PyObject *,因此上述内容应该非常好。此外,我没有收到与返回 PyObject * 的其他函数类似的警告。

现在,虽然这只是我们正在讨论的一个警告,并且我使用 PyArray_SimpleNew 的程序按预期工作,但这一切都表明 Numpy C API 没有像我认为的那样工作(或有错误)。因此,我想了解这背后的原因。


我在以下系统上制作了上述内容:

  • GCC 4.7.2 (Debian 4.7.2-5),Numpy 1.6.2
  • GCC 4.8.2 (Ubuntu 4.8.2-19ubuntu1),Numpy 1.8.2

在这两种情况下,# define NPY_NO_DEPRECATED_API NPY_1_8_API_VERSION 都会改变情况。

【问题讨论】:

  • 我无法重现这个。当您没有告诉它在哪里可以找到 numpy 头文件时,您的 gcc 命令如何成功(只有一个警告)?我希望-I /path/to/installed/numpy/core/include 形式的参数作为gcc 命令的一部分。 (另外,您使用的是什么平台和操作系统?哪个版本的 gcc?哪个版本的 numpy?)
  • @WarrenWeckesser:我认为-I/usr/include/python2.7 在我的系统上就足够了(见编辑),我很确定这与问题无关。 Numpy 的 get_include 应该为您提供系统的相关标志。我添加了可以产生警告的规范。
  • numpy 的 get_include() 在您的系统上返回什么?
  • 没关系。我看到在 Ubuntu 中,/usr/include/python2.7 中有一个符号链接到 numpy 的包含目录。

标签: c pointers numpy compiler-warnings python-c-api


【解决方案1】:

为了回答您为什么会收到有关“ISO C 禁止将对象指针转换为函数指针类型”的警告的问题,我检查了 numpy 的源代码。

PyArray_SimpleNew 是在第 125 行 numpy/ndarrayobject.h 中定义的宏:

#define PyArray_SimpleNew(nd, dims, typenum) \
        PyArray_New(&PyArray_Type, nd, dims, typenum, NULL, NULL, 0, 0, NULL)

这会将 A 行扩展为:

PyObject * A = PyArray_New(&PyArray_Type, 1, dims, typenum, NULL, NULL, 0, 0, NULL); // Line A

PyArray_New 本身就是在第 1017 行的numpy/__multiarray_api.h 中定义的宏:

#define PyArray_New \
        (*(PyObject * (*)(PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *)) \
         PyArray_API[93])

这会将 A 行扩展为:

PyObject * A = (*(PyObject * (*)(PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *))
                PyArray_API[93])(&PyArray_Type, 1, dims, typenum, NULL, NULL, 0, 0, NULL); // Line A

这个复杂的表达式可以简化为:

// PyObject * function93(PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *)
typedef PyObject * (*function93)(PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *);

// Get the PyArray API function #93, cast the function pointer to its
// signature, and call it with the arguments to `PyArray_New`.
PyObject * A = (*(function93) PyArray_API[93])(&PyArray_Type, 1, dims, typenum, NULL, NULL, 0, 0, NULL); // Line A

导致禁止转换的部分是:

*(function93) PyArray_API[93]

在第 807、810 和 812 行的 numpy/__multiarray_api.h 中,PyArray_API 是 声明为void **。所以PyArray_API[93] 是一个void *(即一个对象 指针),它被转换为函数指针。

我对 NumPy 或其 C-api 不是很熟悉,但看起来你很熟悉 正确使用它。 NumPy 恰好使用了一些非标准的、未定义的 GCC 支持但 ISO 标准不支持的内部行为(即,NumPy 不能被 ISO 标准移植)。

另见[SciPy-User] NumPy C API: where does casting pointer-to-object to pointer-to-function come from?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    相关资源
    最近更新 更多