web-dev-qa-db-fra.com

Passer des tableaux Numpy à une fonction C pour l'entrée et la sortie

Oh mon mot, je suis un imbécile. J'avais simplement omis les deuxième et troisième arguments lors de l'appel de la fonction. Comme un fou. Parce que c'est ce que je suis. La question idiote originale suit:

Cela semble être une chose très courante à faire, mais je ne trouve pas de tutoriel pertinent, et j'ignore trop Numpy et ctypes pour le découvrir moi-même.

J'ai une fonction C dans le fichier 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!");
}

(Comme vous pouvez le deviner, j'avais à l'origine les arguments comme double * plutôt que void *, mais je ne savais pas quoi faire du côté Python. J'adorerais certainement les changer) en arrière, mais je ne suis pas difficile tant que ça fonctionne.)

J'en fais une bibliothèque partagée. gcc -fPIC -shared -o ctest.so ctest.c

Ensuite, en Python, j'ai quelques tableaux numpy, et je voudrais les passer à la fonction C, une en entrée et une en sortie.

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

Cela ne signale aucune erreur, mais s'imprime

>>> 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.]]

Le tableau de données supplémentaires n'est pas modifié. Et en fait, si j'appelle à nouveau la fonction, j'obtiens un défaut de segmentation. Ce qui ne me surprend pas - je ne sais vraiment pas ce que je fais ici. Est-ce que quelqu'un peut-il me montrer la bonne direction?

54
Tom Future

Passez simplement les quatre arguments à la fonction C. Changez votre code Python à partir de:

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

À:

fun(ctypes.c_void_p(indata.ctypes.data), ctypes.c_int(5), ctypes.c_int(6),
    ctypes.c_void_p(outdata.ctypes.data))
17
stderr

Bien qu'il ne s'agisse pas d'une réponse directe à votre question d'origine, voici un moyen beaucoup plus pratique d'appeler votre fonction. Commencez par créer le prototype de votre fonction C exactement comme vous le feriez en simple C. Puisque vous n'avez pas besoin de rowcount et colcount séparément, je vais les réduire en un seul size paramètre:

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;
}

Définissez maintenant le prototype ctypes de la manière suivante:

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")]

Maintenant, les appels à votre fonction seront vraiment pratiques:

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

Vous pouvez également définir un wrapper pour rendre cela encore plus pratique:

def wrap_fun(indata, outdata):
    assert indata.size == outdata.size
    fun(indata, indata.size, outdata)
72
Sven Marnach