2012-09-25 29 views
11

私はCythonを使わずにPython C-Extensionを書いています。C配列からPyArray

私はC言語で二重配列を割り当て、それを内部関数(Fortran内にある)で使用して返したいと思います。私はCとFortranのインタフェースが、私はこのコードをデバッグC.

static PyObject * 
Py_drecur(PyObject *self, PyObject *args) 
{ 
    // INPUT 
    int n; 
    int ipoly; 
    double al; 
    double be; 

    if (!PyArg_ParseTuple(args, "iidd", &n, &ipoly, &al, &be)) 
    return NULL; 

    // OUTPUT 
    int nd = 1; 
    npy_intp dims[] = {n}; 
    double a[n]; 
    double b[n]; 
    int ierr; 

    drecur_(n, ipoly, al, be, a, b, ierr); 

    // Create PyArray 
    PyObject* alpha = PyArray_SimpleNewFromData(nd, dims, NPY_DOUBLE, a); 
    PyObject* beta = PyArray_SimpleNewFromData(nd, dims, NPY_DOUBLE, b); 

    Py_INCREF(alpha); 
    Py_INCREF(beta); 

    return Py_BuildValue("OO", alpha, beta); 
} 

で完璧に動作することを指摘し、私は外のアルファを作成しようとすると、私はセグメンテーションフォールトを取得します。それまではすべて正常に動作します。関数drecur_は機能し、削除されても同じ問題が発生します。

ここで、Cデータの周りにPyArrayを定義する標準的な方法は何ですか?私はドキュメンテーションが見つかりましたが、良い例はありません。また、メモリリークはどうですか?アルファとベータのインスタンスが保持されるように、戻りの前にINCREFするのは正しいですか?彼らがもはや必要でない時の割り当て解除はどうですか?

編集 最終的にNumPy cookbookにあるアプローチで正しく取得できました。

static PyObject * 
Py_drecur(PyObject *self, PyObject *args) 
{ 
    // INPUT 
    int n; 
    int ipoly; 
    double al; 
    double be; 
    double *a, *b; 
    PyArrayObject *alpha, *beta; 

    if (!PyArg_ParseTuple(args, "iidd", &n, &ipoly, &al, &be)) 
    return NULL; 

    // OUTPUT 
    int nd = 1; 
    int dims[2]; 
    dims[0] = n; 
    alpha = (PyArrayObject*) PyArray_FromDims(nd, dims, NPY_DOUBLE); 
    beta = (PyArrayObject*) PyArray_FromDims(nd, dims, NPY_DOUBLE); 
    a = pyvector_to_Carrayptrs(alpha); 
    b = pyvector_to_Carrayptrs(beta); 
    int ierr; 

    drecur_(n, ipoly, al, be, a, b, ierr); 

    return Py_BuildValue("OO", alpha, beta); 
} 

double *pyvector_to_Carrayptrs(PyArrayObject *arrayin) { 
    int n=arrayin->dimensions[0]; 
    return (double *) arrayin->data; /* pointer to arrayin data as double */ 
} 

これに感謝し、答えに感謝します。

答えて

1

1つの問題は、配列(a、b)が少なくともそれを含むnumpy配列と同じ長さでなければならないことがあります。ローカルスコープで配列を作成したので、メソッドを終了するときに破棄されます。

Pythonで配列を割り当て(たとえばPyArray_SimpleNewを使用)、コンテンツをコピーしてポインタに渡してください。また、ブーストに対するビルドがオプションである場合は、これらの詳細を処理するためにboost::pythonを使用することもできます。

3

最初に疑わしいのは、配列abが関数のローカルスコープにあることです。つまり、返品後にあなたは不正なメモリアクセスを取得します。

だからあなたは、あなたがメモリを後で作成したオブジェクトによって解放されたことを確認する必要があり

double *a = malloc(n*sizeof(double)); 

で配列を宣言する必要があります。 ドキュメントのこの引用を参照してください:

PyObject PyArray_SimpleNewFromDataを(int型ND、npy_intp暗くなり、int型のtypenum、void *型データ)

は時々、あなたはndarrayに別の場所に割り当てられたメモリをラップしたいです下流使用のためのオブジェクト。このルーチンは簡単に行うことができます。最初の3つの引数はPyArray_SimpleNewと同じです。最後の引数は、ndarrayがCスタイルの連続した方法で解釈されるデータバッファとして使用する連続したメモリブロックへのポインタです。 ndarrayへの新しい参照が返されますが、ndarrayはそのデータを所有しません。このndarrayの割り当てを解除すると、ポインタは解放されません。

返された配列が存在する間は、指定されたメモリが解放されていないことを確認する必要があります。これを処理する最も簡単な方法は、データが別の参照カウントされたPythonオブジェクトから来た場合です。このオブジェクトの参照カウントは、ポインタが渡された後に増加する必要があります。返されたndarrayの基本メンバは、データを所有するPythonオブジェクトを指す必要があります。次に、ndarrayの割り当てが解除されると、ベースメンバーは適切にDECREFされます。 ndarrayが割り当て解除されるとすぐにメモリを解放したい場合は、返されたndarrayにOWNDATAフラグを設定するだけです。あなたは、グローバル変数やクラスのメンバーで参照を保持する場合Py_INCREF(alpha);は、一般的に必要なだけであるあなたの2番目の質問については

。 しかし、関数をラップするだけなので、関数を実行する必要はありません。 悲しいことに、関数PyArray_SimpleNewFromDataが参照カウンタを1に設定していない可能性があります。その場合は、1に増やす必要があります。

関連する問題