问题
I am creating a module for Python with Numpy using the C API and encounter weird incompatibilities with the output of PyArray_SimpleNew
, which I would like to understand. But first a minimal example:
# 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;
}
If I compile this with gcc source.c -lpython2.7 -I/usr/include/python2.7 --pedantic
, I get (with a reference to Line A):
ISO C forbids conversion of object pointer to function pointer type
So, apparently, PyArrayObject
s are expected to be function pointers for some reason.
According to the documentation (e.g., here), PyArray_SimpleNew
has a return of type PyObject *
and thus the above should be perfectly fine. Moreover, I do not get similar warnings with other functions returning PyObject *
.
Now, while this is only a warning we are talking about and my programs using PyArray_SimpleNew
work as intended, all this indicates that the Numpy C API is not working as I think it is (or has a bug). Therefore I would like to understand the reason behind this.
I produced the above on the following systems:
- 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
In neither case, the situation changes with # define NPY_NO_DEPRECATED_API NPY_1_8_API_VERSION
.
回答1:
To answer your question about why you're receiving a warning about "ISO C forbids conversion of object pointer to function pointer type", I examined the source code for numpy.
PyArray_SimpleNew
is a macro defined in numpy/ndarrayobject.h
on line 125:
#define PyArray_SimpleNew(nd, dims, typenum) \
PyArray_New(&PyArray_Type, nd, dims, typenum, NULL, NULL, 0, 0, NULL)
Which will expand line A as:
PyObject * A = PyArray_New(&PyArray_Type, 1, dims, typenum, NULL, NULL, 0, 0, NULL); // Line A
PyArray_New
is itself a macro defined in numpy/__multiarray_api.h
on line 1017:
#define PyArray_New \
(*(PyObject * (*)(PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *)) \
PyArray_API[93])
Which will expand line A as:
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
This complex expression can be simplified to:
// 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
The part causing the forbidden conversion is:
*(function93) PyArray_API[93]
In numpy/__multiarray_api.h
on lines 807, 810, and 812 PyArray_API
is
declared as void **
. So PyArray_API[93]
is a void *
(i.e., an object
pointer) which is being cast as a function pointer.
I'm not really familiar with NumPy or its C-api, but it looks like you are using it properly. NumPy just happens to be using some non-standard, undefined behavior internally that GCC supports but the ISO standard does not (i.e., NumPy is not portable by the ISO standard).
See also [SciPy-User] NumPy C API: where does casting pointer-to-object to pointer-to-function come from?
来源:https://stackoverflow.com/questions/28963334/pointer-type-mismatch-with-pyarray-simplenew