2012-09-03 38 views
8

Tôi đang sử dụng API C có nhiều chức năng để viết một số hàm để tính toán ma trận. Hôm nay tôi muốn di chuyển một số phần của hàm vào một tệp .c riêng biệt và sử dụng tiêu đề để khai báo chúng. Bây giờ tôi có một vấn đề lạ mà phải làm với chức năng import_array của numpy. Tôi đã cố gắng đơn giản hóa vấn đề càng nhiều càng tốt. Lúc đầu có chương trình công tác:API Numpy C: Liên kết một số tệp đối tượng

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 

Mọi thứ hoạt động như mong đợi. Tôi có thể gọi make và sau đó tải các module và gọi hàm:

test.py

import mytest 
print(mytest.my_test_function()) 

Nếu tôi sẽ loại bỏ import_array từ hàm init sẽ có một segfault, đó là hành vi mà đã được báo cáo trong nhiều danh sách gửi thư và diễn đàn.

Bây giờ tôi chỉ muốn loại bỏ toàn bộ chức năng my_sub_function từ mytest.c và di chuyển nó vào một tập tin gọi 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; 
} 

mới Makefile là:

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 

Nếu tôi cố tải mô-đun và gọi hàm ngay bây giờ, cuộc gọi hàm sẽ cho tôi một khoảng cách. Tôi có thể giải quyết vấn đề nếu tôi đặt một cuộc gọi đến import_array lên đầu my_sub_function, nhưng tôi không nghĩ rằng đây là cách mà chức năng nên được sử dụng.

Vì vậy, tôi muốn biết lý do tại sao điều này đang xảy ra và cách "sạch" để chia nhỏ mô-đun numpy thành nhiều tệp nguồn.

Trả lời

8

Theo mặc định, quy trình import_array sẽ chỉ làm cho API NumPy C khả dụng trong một tệp. Điều này là do nó hoạt động thông qua một bảng con trỏ hàm được lưu trữ trong một biến toàn cầu tĩnh (tức là không được xuất và chỉ hiển thị trong cùng một tệp).

Như mentioned in the documentation, bạn có thể thay đổi hành vi này với một vài định nghĩa preprocessor:

  1. Trong tất cả các file cho phần mở rộng của bạn, xác định PY_ARRAY_UNIQUE_SYMBOL cho một biến duy nhất là khó có khả năng xung đột với các phần mở rộng khác. Bao gồm tên mô-đun của tiện ích trong tên biến sẽ là một ý tưởng hay.

  2. Trong mỗi tập tin ngoại trừ một trong những nơi bạn gọi import_array, xác định các biểu tượng NO_IMPORT_ARRAY

Những biểu tượng cần phải được xác định trước khi bạn bao gồm arrayobject.h để cho chúng có hiệu lực.

Các vấn đề liên quan