Przekazywanie tablic Numpy do funkcji C dla wejścia i wyjścia

O rany, jestem głupcem. Po prostu pomijałem drugi i trzeci argument podczas wywoływania funkcji. Jak głupiec. Bo tym właśnie jestem. Oryginalne głupie pytanie:

Wydaje się, że to musi być bardzo powszechna rzecz do zrobienia, ale nie mogę znaleźć odpowiedniego tutoriala, i jestem zbyt nieświadomy o Numpy i ctypes, aby sam to rozgryźć.

Mam funkcję C w pliku ctest.c.

#include <stdio.h>

void cfun(const void * indatav, int rowcount, int colcount, void * outdatav) {
    //void cfun(const double * indata, int rowcount, int colcount, double * outdata) {
    const double * indata = (double *) indatav;
    double * outdata = (double *) outdatav;
    int i;
    puts("Here we go!");
    for (i = 0; i < rowcount * colcount; ++i) {
        outdata[i] = indata[i] * 2;
    }
    puts("Done!");
}

(Jak można się domyślać, początkowo miałem argumenty jako podwójne * raczej niż void *, ale nie mogłem wymyślić, co zrobić po stronie Pythona. Z pewnością chciałbym je zmienić, ale nie jestem wybredny, dopóki to działa.)

Robię z tego wspólną bibliotekę. gcc-fPIC-shared-o ctest.so ctest.c

Następnie w Pythonie mam kilka tablic numpy i chciałbym przekazać je do funkcji C, jedna jako wejście, a druga jako wyjście.

indata = numpy.ones((5,6), dtype=numpy.double)
outdata = numpy.zeros((5,6), dtype=numpy.double)
lib = ctypes.cdll.LoadLibrary('./ctest.so')
fun = lib.cfun
# Here comes the fool part.
fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

print 'indata: %s' % indata
print 'outdata: %s' % outdata

To nie zgłasza żadnych błędów, ale wypisuje

>>> Here we go!
Done!
indata: [[ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.]]
outdata: [[ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.]]

Tablica outdata nie jest modyfikowana. I w fakt, jeśli ponownie wywołam tę funkcję, dostaję segfault. Co mnie nie dziwi ... naprawdę Nie wiem, co tu robię. Czy ktoś może wskazać mi właściwy kierunek?

Author: ppperry, 2011-05-03

2 answers

Wystarczy przekazać wszystkie cztery argumenty do funkcji C. Zmień kod Pythona z:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_void_p(outdata.ctypes.data))

Do:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_int(5), ctypes.c_int(6),
    ctypes.c_void_p(outdata.ctypes.data))
 16
Author: stderr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-02 23:21:17

Chociaż nie jest to bezpośrednia odpowiedź na oryginalne pytanie, oto o wiele wygodniejszy sposób na wywołanie funkcji. Po pierwsze, zrób prototyp swojej funkcji C dokładnie tak, jak byś to zrobił w zwykłym C. Ponieważ nie potrzebujesz rowcount i colcount osobno, zwinę je w jeden parametr size:

void cfun(const double *indatav, size_t size, double *outdatav) 
{
    size_t i;
    for (i = 0; i < size; ++i)
        outdatav[i] = indatav[i] * 2.0;
}

Teraz zdefiniuj prototyp ctypes w następujący sposób:

import ctypes
from numpy.ctypeslib import ndpointer
lib = ctypes.cdll.LoadLibrary("./ctest.so")
fun = lib.cfun
fun.restype = None
fun.argtypes = [ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),
                ctypes.c_size_t,
                ndpointer(ctypes.c_double, flags="C_CONTIGUOUS")]

Teraz wywołania twojej funkcji będą naprawdę wygodne:

indata = numpy.ones((5,6))
outdata = numpy.empty((5,6))
fun(indata, indata.size, outdata)

Można również zdefiniować wrapper aby było to jeszcze wygodniejsze:

def wrap_fun(indata, outdata):
    assert indata.size == outdata.size
    fun(indata, indata.size, outdata)
 65
Author: Sven Marnach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-12 11:13:10