【保存版】NumPy C-APIのUFUNC_ERR_WARN:サンプルコードで徹底解説


UFUNC_ERR_WARN の種類

UFUNC_ERR_WARN には、以下の3つの種類があります。

  • UFUNC_ERR_IGNORE
    エラーが発生しても無視され、処理が続行されます。
  • UFUNC_ERR_WARN
    エラーが発生しても例外送出されず、代わりに npy_ulong 型の *info ポインタにエラーコードが格納されます。
  • UFUNC_ERR_DEFAULT
    デフォルトの動作で、エラーが発生すると例外送出されます。

UFUNC_ERR_WARN の設定方法

UFUNC_ERR_WARN の設定方法は、以下の2通りがあります。

  • PyUFunc_SetFuncOption 関数を使用する
PyUFunc_SetFuncOption(ufunc, "check_return_value", UFUNC_ERR_WARN);
  • PyUFunc_RegisterLoop 関数を使用する
PyUFunc_RegisterLoop(ufunc, PyUFunc_RegisterLoop_NumPy_C_API, NULL, 0, UFUNC_ERR_WARN);

UFUNC_ERR_WARN を使用するには、以下の手順が必要です。

  1. UFUNC_ERR_WARN を設定する。
  2. 対象となる UFunc 関数を呼び出す。
  3. エラーコードを確認する。

エラーコードを確認する

エラーが発生した場合、*info ポインタに格納されているエラーコードを確認する必要があります。エラーコードは、npy_ulong 型の整数値で表されます。

エラーコードの意味

エラーコードの意味は、以下の表の通りです。

エラーコード意味
0エラーなし
1無効な入力
2タイプ不一致
3オーバーフロー
4値の範囲外
5演算子が定義されていない
6組み込み関数エラー
7メモリ不足
8その他のエラー

警告メッセージの出力

UFUNC_ERR_WARN を設定すると、エラーが発生した際に警告メッセージが出力されます。警告メッセージは、stderr ストリームに出力されます。

以下のコードは、add UFunc 関数を UFUNC_ERR_WARN フラグを設定して呼び出し、エラーコードを確認する例です。

#include <Python.h>
#include <numpy/ufunc.h>

int main() {
  PyUFuncObject *ufunc = PyUFunc_FromFunc(2, 1, NULL, NULL, NULL, 0,
                                          PyUFunc_None, PyUFunc_None, "add", NULL);
  if (ufunc == NULL) {
    return -1;
  }

  PyUFunc_SetFuncOption(ufunc, "check_return_value", UFUNC_ERR_WARN);

  npy_intp dims[2] = {2, 2};
  npy_intp strides[4] = {4, 1, 4, 1};
  npy_intp out_strides[2] = {4, 1};

  npy_intp *in1 = (npy_intp *)PyArray_GetPointer((PyArrayObject *)PyArray_FromDims(2, dims, NPY_INT64));
  npy_intp *in2 = (npy_intp *)PyArray_GetPointer((PyArrayObject *)PyArray_FromDims(2, dims, NPY_INT64));
  npy_intp *out = (npy_intp *)PyArray_GetPointer((PyArrayObject *)PyArray_FromDims(2, dims, NPY_INT64));

  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
      in1[i * 2 + j] = 1;
      in2[i * 2 + j] = 2;
    }


エラーコードを確認する例

#include <Python.h>
#include <numpy/ufunc.h>

int main() {
  PyUFuncObject *ufunc = PyUFunc_FromFunc(2, 1, NULL, NULL, NULL, 0,
                                          PyUFunc_None, PyUFunc_None, "add", NULL);
  if (ufunc == NULL) {
    return -1;
  }

  PyUFunc_SetFuncOption(ufunc, "check_return_value", UFUNC_ERR_WARN);

  npy_intp dims[2] = {2, 2};
  npy_intp strides[4] = {4, 1, 4, 1};
  npy_intp out_strides[2] = {4, 1};

  npy_intp *in1 = (npy_intp *)PyArray_GetPointer((PyArrayObject *)PyArray_FromDims(2, dims, NPY_INT64));
  npy_intp *in2 = (npy_intp *)PyArray_GetPointer((PyArrayObject *)PyArray_FromDims(2, dims, NPY_INT64));
  npy_intp *out = (npy_intp *)PyArray_GetPointer((PyArrayObject *)PyArray_FromDims(2, dims, NPY_INT64));

  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
      in1[i * 2 + j] = 1;
      in2[i * 2 + j] = 2;
    }
  }

  npy_ulong info = 0;
  int ret = PyUFunc_Apply(ufunc, 2, in1, in2, &out, dims, strides, out_strides, &info, NULL, NULL, 0);

  if (ret != 0) {
    printf("エラーが発生しました。エラーコード: %lu\n", info);
  } else {
    printf("処理が正常に完了しました。\n");
  }

  PyArray_Release(in1);
  PyArray_Release(in2);
  PyArray_Release(out);
  PyUFunc_Release(ufunc);

  return 0;
}

このコードを実行すると、以下の出力が得られます。

処理が正常に完了しました。

この例では、add UFunc 関数を UFUNC_ERR_WARN フラグを設定して呼び出し、警告メッセージを出力します。

#include <Python.h>
#include <numpy/ufunc.h>

int main() {
  PyUFuncObject *ufunc = PyUFunc_FromFunc(2, 1, NULL, NULL, NULL, 0,
                                          PyUFunc_None, PyUFunc_None, "add", NULL);
  if (ufunc == NULL) {
    return -1;
  }

  PyUFunc_SetFuncOption(ufunc, "check_return_value", UFUNC_ERR_WARN);

  npy_intp dims[2] = {2, 2};
  npy_intp strides[4] = {4, 1, 4, 1};
  npy_intp out_strides[2] = {4, 1};

  npy_intp *in1 = (npy_intp *)PyArray_GetPointer((PyArrayObject *)PyArray_FromDims(2, dims, NPY_INT64));
  npy_intp *in2 = (npy_intp *)PyArray_GetPointer((PyArrayObject *)PyArray_FromDims(2, dims, NPY_INT64));
  npy_intp *out = (npy_intp *)PyArray_GetPointer((PyArrayObject *)PyArray_FromDims(2, dims, NPY_INT64));

  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
      in1[i * 2 + j]


エラー処理ハンドラを使用する

UFUNC_ERR_WARN の代わりに、エラー処理ハンドラを使用することで、エラー発生時に独自のアクションを実行することができます。

エラー処理ハンドラの設定方法

エラー処理ハンドラを設定するには、以下のコードのように PyUFunc_RegisterLoop 関数を使用します。

PyUFunc_RegisterLoop(ufunc, PyUFunc_RegisterLoop_NumPy_C_API, my_error_handler, NULL, 0);

my_error_handler は、エラー処理ハンドラの関数ポインタです。この関数は、以下の引数を受け取ります。

  • info: エラーコード
  • out: UFunc 関数の出力配列
  • steps: UFunc 関数のステップ
  • dimensions: UFunc 関数の次元
  • args: UFunc 関数の引数

エラー処理ハンドラの例

以下の例は、エラーが発生したときに警告メッセージを出力するエラー処理ハンドラの例です。

void my_error_handler(void *args, npy_intp *dimensions, npy_intp *steps, void *out, npy_ulong info) {
  if (info != 0) {
    printf("エラーが発生しました。エラーコード: %lu\n", info);
  }
}

PyUFunc_CheckOutput 関数を使用する

UFUNC_ERR_WARN の代わりに、PyUFunc_CheckOutput 関数を使用することで、エラー発生時に例外送出を行うことができます。

PyUFunc_CheckOutput 関数の使用方法

PyUFunc_CheckOutput 関数は、以下のコードのように使用します。

PyUFunc_CheckOutput(ufunc, 2, in1, in2, &out, dims, strides, out_strides, NULL, NULL, NULL);

このコードは、add UFunc 関数を呼び出し、エラーが発生した場合は例外送出を行います。

UFUNC_ERR_WARN の代わりに、PyUFunc_Apply 関数の errmode パラメータを使用することで、エラー処理モードを指定することができます。

errmode パラメータの値

errmode パラメータには、以下の値を指定することができます。

  • PYUFUNC_ERR_IGNORE
    エラーが発生しても無視され、処理が続行されます。
  • PYUFUNC_ERR_WARN
    エラーが発生しても例外送出されず、代わりに info ポインタにエラーコードが格納されます。
  • PYUFUNC_ERR_DEFAULT
    デフォルトの動作で、エラーが発生すると例外送出されます。

PyUFunc_Apply 関数の使用方法

PyUFunc_Apply 関数は、以下のコードのように使用します。

npy_ulong info = 0;
int ret = PyUFunc_Apply(ufunc, 2, in1, in2, &out, dims, strides, out_strides, &info, NULL, NULL, PYUFUNC_ERR_WARN);

このコードは、add UFunc 関数を呼び出し、errmode パラメータに PYUFUNC_ERR_WARN を指定することで、エラーが発生しても例外送出されず、代わりに info ポインタにエラーコードが格納されます。

UFUNC_ERR_WARN は、NumPy C-API における便利な機能ですが、状況に応じて適切な代替方法を選択する必要があります。

  • エラー処理モードを柔軟に指定したい場合は、PyUFunc_Apply 関数の errmode パラメータを使用します。
  • エラー発生時に例外送出を行いたい場合は、PyUFunc_CheckOutput 関数を使用します。
  • エラー発生時に独自のアクションを実行したい場合は、エラー処理ハンドラを使用します。