3

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, PyArrayObjects 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.

Wrzlprmft
  • 4,234
  • 1
  • 28
  • 54
  • I can't reproduce this. How does your `gcc` command succeed (with just a warning), when you haven't told it where to find the numpy header files? I would expect an argument of the form `-I /path/to/installed/numpy/core/include` as part of that `gcc` comand. (Also, what platform and OS are you using? Which version of gcc? Which version of numpy?) – Warren Weckesser Mar 10 '15 at 13:42
  • @WarrenWeckesser: I think `-I/usr/include/python2.7` suffices on my systems (see edit) and I am pretty sure that this is unrelated to the problem. Numpy’s `get_include` should give you the relevant flags for your system. I added the specifications for which I could produce the warning. – Wrzlprmft Mar 10 '15 at 14:26
  • What does numpy's `get_include()` return on your system(s)? – Warren Weckesser Mar 10 '15 at 18:52
  • Nevermind. I see that in Ubuntu, there is a symlink in /usr/include/python2.7 to numpy's include directory. – Warren Weckesser Mar 10 '15 at 20:36

1 Answers1

4

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?

Uyghur Lives Matter
  • 18,820
  • 42
  • 108
  • 144