Python/C APIのPyObject_CallObjectの引数として動的配列を渡す

okwaves2024-01-25  11

C コードから作成したいくつかの Python 関数にアクセスしたいと考えています。私の Python 関数の 1 つは、入力として numpy 配列を受け取ります。私の C コードでは、この配列は動的メモリに割り当てられます。 Python 関数が呼び出されるたびにセグメンテーション違反が発生しますが、何が間違っているのかわかりません。 N、O、O& を通過しようとしました。 Py_BuildValue 関数に追加しましたが、同じ結果が得られ続けます。

これが Python の簡略化されたコードです。

#Python simple function that receives pointer to array
#!/usr/bin/python3

import numpy as np
def getNumpyArrayPointer(pointer):
    data = np.ctypeslib.as_array(pointer, shape=(1000,1)).astype(np.int32) #Doesn't matter if I comment out this line. Fault continues
    print('Python function getNumpyArrayPointer() called')
    return data

これは C の簡略化されたコードです

#include <iostream>
#include <Python.h>
#include "pyhelper.h"


int capitest()
{
    Py_Initialize();
    PyObject* sysPath = PySys_GetObject((char*)"path");
    PyObject* programName = PyUnicode_FromString("path-to-python-script");
    PyList_Append(sysPath, programName);
    Py_DECREF(programName);
    PyObject* pName = PyUnicode_FromString("python-script-name");
    PyObject* pModule = PyImport_Import(pName);
    PyObject* pValue;
    CPyObject pFunc;
    PyObject* args;

    if(pModule)
    {
        pFunc = PyObject_GetAttrString(pModule, "getNumpyArrayPointer");
        uint32_t *array = (uint32_t *)malloc (1000);
        args = Py_BuildValue("(N)", array );
        
        if(pFunc && PyCallable_Check(pFunc))
        {
            printf("Calling getNumpyArrayPointer\n");
            pValue = PyObject_CallObject(pFunc, args);
            Py_DECREF(args);
            printf("Called getNumpyArrayPointer\n");
            if (PyErr_Occurred()) 
            { 
                PyErr_Print(); 
                return 0; 
            }   
        }
        else
        {
            printf("ERROR: function getNumpyArrayPointer()\n");
        }
    }
    else
    {
        printf("ERROR: Module not imported\n");
    }
    Py_Finalize();
    return 0;
}

int main()
{
    capitest();
}

出力:

Calling getNumpyArrayPointer
Segmentation fault (core dumped)


------------------------

 uint32_t *array = (uint32_t *)malloc (1000);
 args = Py_BuildValue("(N)", array );

それはナンセンスです。配列がへのポインタであることを伝えていますPython オブジェクトなので、「参照カウント」を管理しようとします。

次のようなことを提案します。

PyObject* pointer_as_int = PyLong_FromVoidPtr(array);

次に、これを Python オブジェクトの ctypes.cast(pointer_as_int, ctypes.POINTER(ctypes.c_uint32)) に渡して、ctypes ポインターを取得します。例を参照してください。 ctypes: 任意の整数からポインタを構築する

あるいは、C ポインタを Numpy C API 関数 PyArray_SimpleNewFromData に直接渡してみることもできます。Stack Overflow や他の場所でたくさんの例が見つかります。

配列の使用が完了したら、C コードで配列を解放する必要があることに注意してください。

4

ありがとう! C 側に PyObject* number = PyLong_FromVoidPtr(array); という行を追加しました。 args = Py_BuildValue("(O)", 数値 );。 Python スクリプトでは、これを pyptr = ctypes.POINTER(ctypes.c_uint32).from_address(pointer) data = np.ctypeslib.as_array(pyptr,shape=(3072,3072)) に変更しましたが、NULL ポインター アクセス値エラーが発生します。 。理由はわかりますか?

– ダウッド

2020 年 9 月 5 日 9:49

int から ctypes 値への変換が間違っていたと思います。テストされ動作しているものについては編集を参照してください

– デビッドW

2020 年 9 月 5 日 10:58

ありがとう@DavidW。参考までに、ポインタをキャストせずに np 配列に変換したときにクラッシュしたのは Python コードです。現在私がそれを処理している方法は次のとおりです pyPointerObject = ctypes.cast(pointerFromC, ctypes.POINTER(ctypes.c_uint32)) npMatrix = np.ctypeslib.as_array(pyPointerObject, shade=(3072,3072))。ありがとう!

– ダウッド

2020 年 9 月 6 日 10:00

クラッシュの原因は、C 配列を Python オブジェクトとして誤って解釈したためであると私は今でもかなり確信しています (たとえキャストを行った瞬間ではなく、オブジェクトを使用しようとしたときにのみクラッシュが発生したとしても)。現在は機能しているようで嬉しいです。

– デビッドW

2020 年 9 月 6 日 10:08

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。