2012-09-03 10 views
8

Estoy usando la API C de numpy para escribir algunas funciones para el cálculo de matrices. Hoy quería mover algunas partes de mis funciones en un archivo .c separado y usar un encabezado para declararlas. Ahora tengo un problema extraño que tiene que ver con la función import_array de numpy. Intenté simplificar el problema tanto como sea posible. En primer lugar está el programa de trabajo:Numpy C API: Vincular varios archivos de objetos

mytest.c

#include "mytest.h" 

PyObject* my_sub_function() { 
    npy_intp dims[2] = {2, 2}; 
    double data[] = {0.1, 0.2, 0.3, 0.4}; 

    PyArrayObject* matrix = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_FLOAT64); 
    memcpy(PyArray_DATA(matrix), data, sizeof(double) * dims[0] * dims[1]); 

    return (PyObject*)matrix; 
} 

static PyObject* my_test_function(PyObject* self, PyObject* args) { 
    return my_sub_function(); 
} 

static PyMethodDef methods[] = { 
    {"my_test_function", my_test_function, METH_VARARGS, ""}, 
    {0, 0, 0, 0} 
}; 

static struct PyModuleDef module = { 
    PyModuleDef_HEAD_INIT, "mytest", 0, -1, methods 
}; 

PyMODINIT_FUNC PyInit_mytest() { 
    import_array(); 
    return PyModule_Create(&module); 
} 

mytest.h

#ifndef mytest_h 
#define mytest_h 

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

PyObject* my_sub_function(); 

#endif 

Makefile

all: mytest.o sub.o 
    gcc -shared -Wl,-soname,mytest.so -o mytest.so mytest.o 

mytest.o: sub.o 
    gcc -fPIC -c mytest.c `pkg-config --cflags python3` 

clean: 
    rm -rf *.so 
    rm -rf *.o 

Todo funciona como se esperaba. Puedo llamar make y luego cargar el módulo y llamar a la función:

test.py

import mytest 
print(mytest.my_test_function()) 

Si me retire import_array de la función init habría una violación de segmento, que es el comportamiento que ha sido reportado en muchas listas de correo y foros.

Ahora sólo desea eliminar toda la función de my_sub_functionmytest.c y moverlo en un archivo llamado sub.c:

#include "mytest.h" 

PyObject* my_sub_function() { 
    npy_intp dims[2] = {2, 2}; 
    double data[] = {0.1, 0.2, 0.3, 0.4}; 

    PyArrayObject* matrix = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_FLOAT64); 
    memcpy(PyArray_DATA(matrix), data, sizeof(double) * dims[0] * dims[1]); 

    return (PyObject*)matrix; 
} 

El nuevo Makefile es:

all: mytest.o sub.o 
    gcc -shared -Wl,-soname,mytest.so -o mytest.so mytest.o sub.o 

mytest.o: 
    gcc -fPIC -c mytest.c `pkg-config --cflags python3` 

sub.o: 
    gcc -fPIC -c sub.c `pkg-config --cflags python3` 

clean: 
    rm -rf *.so 
    rm -rf *.o 

Si intento cargar el módulo y llamar a la función ahora, la llamada a la función me da una segfault. Puedo resolver el problema si llamo al import_array en la parte superior de my_sub_function, pero no creo que esta sea la forma en que se debe usar esa función.

Así que me gustaría saber por qué sucede esto y cuál es la forma "limpia" de dividir un módulo numpy en varios archivos fuente.

Respuesta

8

De forma predeterminada, la rutina import_array solo hará que la API NumPy C esté disponible en un único archivo. Esto se debe a que funciona a través de una tabla de punteros de función almacenados en una variable global estática (es decir, no exportada, y solo visible dentro del mismo archivo).

Como mentioned in the documentation, puede cambiar este comportamiento con algunas definiciones del preprocesador:

  1. En todos los archivos para su extensión, defina PY_ARRAY_UNIQUE_SYMBOL a una variable única que es poco probable que entren en conflicto con otras extensiones. Incluir el nombre del módulo de su extensión en el nombre de la variable sería una buena idea.

  2. En cada archivo a excepción de aquel en el que se llama a import_array, defina el símbolo NO_IMPORT_ARRAY

Estos símbolos deben ser definidos antes de incluir arrayobject.h a fin de que entren en vigor.

Cuestiones relacionadas