11

Cython を使用せずに Python C 拡張機能を作成しています。

Cでdouble配列を割り当て、それを内部関数(たまたま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);
}

このコードをデバッグしたところ、a からアルファを作成しようとするとセグメンテーション エラーが発生しました。そこまではすべて正常に動作します。関数 drecur_ が機能し、削除すると同じ問題が発生します。

では、C データの周りに PyArray を定義する標準的な方法は何ですか? ドキュメントは見つかりましたが、良い例はありません。また、メモリリークはどうですか?alpha と beta のインスタンスが保持されるように、戻る前に 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 */
}

これについて自由にコメントしてください。回答に感謝します。

4

2 に答える 2

3

したがって、最初に疑わしいと思われるのは、配列ab関数のローカル スコープ内にあることです。つまり、復帰後に不正なメモリ アクセスが発生します。

したがって、配列を次のように宣言する必要があります

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

次に、作成したオブジェクトによってメモリが後で解放されることを確認する必要があります。ドキュメントのこの引用を参照してください。

PyObject PyArray_SimpleNewFromData(int nd, npy_intp dims , int typenum, void* data)

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

返された配列が存在する間は、提供されたメモリが解放されないようにする必要があります。これを処理する最も簡単な方法は、データが別の参照カウント Python オブジェクトから取得された場合です。このオブジェクトの参照カウントは、ポインターが渡された後に増加する必要があり、返された ndarray の基本メンバーは、データを所有する Python オブジェクトを指す必要があります。次に、ndarray の割り当てが解除されると、基本メンバーは適切に DECREF されます。ndarray の割り当てが解除されたらすぐにメモリを解放したい場合は、返された ndarray に OWNDATA フラグを設定するだけです。

2 番目の質問でPy_INCREF(alpha);は、参照をグローバル変数またはクラス メンバーに保持する場合にのみ必要です。ただし、関数をラップしているだけなので、それを行う必要はありません。悲しいことに、関数PyArray_SimpleNewFromDataが参照カウンターを 1 に設定しない可能性があります。その場合は、1 に増やす必要があります。それが理解できたことを願っています ;)。

于 2012-09-25T12:55:58.103 に答える
1

1 つの問題は、配列 (a,b) が、それを含む numpy-array と少なくとも同じくらい持続する必要があることです。配列をローカル スコープで作成したので、メソッドを離れると配列が破棄されます。

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

于 2012-09-25T12:32:32.240 に答える