2013-07-17 10 views
5

Próbuję utworzyć wrapper Pythona dla starego programu C, który pobiera dane wejściowe jako wskaźniki. W tym momencie mogę uruchomić program, ale nie wiem, jak odzyskać wartości w wyznaczonych wskaźnikach.Dane dostępu na adres pamięci z kodami typów

Jest to uproszczona C scenariusz:

#include <stdlib.h> 

void cprogram(double *wts, double *res, int *kks, int n, double *ex) { 
    int m; 
    m=n+1; 
    res[0]=1.0; 
    kks[0]=1.0;} 

A to moja uproszczony kod Python:

from ctypes import * 
import sys 

libc = CDLL("src/program.so") 

class CONTEXT(Structure): 
    _fields_ = [ 
       ("wts", POINTER(c_double)), //tried just to see if it would work 
       ("res", c_double), 
       ("kks", c_int), 
       ("n", c_int), 
       ("ex", c_double)] 

def runner(): 
    kk = (1,2,3) 
    n = 3 
    mm = n + 1 
    wts = (c_double * n)(1, 1, 1) 
    res = (c_double * mm)(0) 
    kks = (c_int * len(kk))(*kk) 
    n = c_int(n) 
    ex = c_double(0) 

    libc.cprogram.restype = POINTER(CONTEXT) 

    tmp = libc.cprogram(wts, res, kks, n, ex) 

runner() 

Próbowałem komendy jak print tmp[1].wts[1] i print tmp[2] ale to tylko drukuje adres pamięci i nie wartość (lub nieprawdopodobnie małe wartości, które są nieprawidłowe, jak 2.15880221124e-314). Chciałbym móc zwrócić listę wartości wts.

Odpowiedz

3

Twoja funkcja C zwraca wartość void, a nie CONTEXT *. Twój kod po prostu rzuca losową niezainicjowaną pamięć na numer CONTEXT *, a następnie próbuje go usunąć.

Poza tym, nawet jeśli zrobił zwrócić CONTEXT obiektu poprzez odniesienie tmp[1] próbowałby deref pamięć po że sprzeciw, więc byłoby nadal być śmieci.

Kiedy spróbujesz interpretować pamięć losową jako podwójną, otrzymasz szansę na wykroczenie lub wartości, takie jak 2.15880221124e-314, jeśli masz szczęście - jeśli masz pecha, otrzymasz poprawne, ale wciąż losowe wartości, takie jak 0.0.


Tymczasem, ponieważ funkcja C modyfikuje argumenty w tym miejscu, nie trzeba tu robić nic nadzwyczajnego. Wystarczy użyć zmiennych zdałeś w

Tak.

def runner(): 
    kk = (1,2,3) 
    n = 3 
    mm = n + 1 
    wts = (c_double * n)(1, 1, 1) 
    res = (c_double * mm)(0) 
    kks = (c_int * len(kk))(*kk) 
    n = c_int(n) 
    ex = c_double(0) 

    libc.cprogram.restype = None 

    libc.cprogram(wts, res, kks, n, ex) 

    print wts[1] 

To działa, i wypisuje 1.0.


A jeśli funkcja C zrobił zwraca tablicę CONTEXT strukturach odpowiadających swoją deklarację ctypes, byłoby to wszystko działa dobrze. Na przykład:

#include <stdlib.h> 

typedef struct { 
    double *wts; 
    double res; 
    int kks; 
    int n; 
    double ex; 
} CONTEXT; 

CONTEXT *cprogram(double *wts, double *res, int *kks, int n, double *ex) { 
    int m; 
    m=n+1; 
    res[0]=1.0; 
    kks[0]=1.0; 

    CONTEXT *contexts = malloc(sizeof(CONTEXT) * 4); 
    for (int i=0; i!=4; ++i) { 
    double *wtsses = malloc(sizeof(double) * 5); 
    for (int j=0; j!=4; ++j) { 
     wtsses[j] = i + j; 
    } 
    CONTEXT context = { wtsses, *res, *kks, m, *ex }; 
    contexts[i] = context; 
    } 
    return contexts; 
} 

skompilować to uruchomić istniejący skrypt Pythona z dodatkiem print tmp[1].wts[1] i będzie wydrukować 2.0.


Wreszcie, do obserwacji, najpierw zmieńmy kod C podjąć int *n, gdzie *n jest wejście:

void cprogram(double *wts, double *res, int *kks, int *n, double *ex) { 
    int m; 
    m=*n+1; 
    res[0]=1.0; 
    kks[0]=1.0;} 

teraz zadzwonić to od Pythona, trzeba stworzyć c_int i przekazać do niego wskaźnik.Ponieważ jesteś już robi pierwszą połowę (co nie było konieczne przed-po prostu ustawić argtypes zamiast ... ale trzeba teraz, co jest wygodne), to tylko zmiana jednego wiersza:

libc.cprogram(wts, res, kks, pointer(n), ex) 

To działa nawet jeśli n jest parametrem wejściowym.


Ale tak naprawdę, nie trzeba wogóle widzieć obiektu wskaźnika z Pythona; jedyną rzeczą, którą robisz z tym tworzeniem, jest przejście do funkcji, a następnie pozwolenie jej na zbieranie. Aby przekazać wskaźnik C bez tworzenia obiektu wskaźnik ctypes (co znowu działa nawet jeśli n jest parametrem in-out), należy byref:

libc.cprogram(wts, res, kks, byref(n), ex) 
+0

Dziękujemy za oddanie na czas i wysiłek, aby wyjaśnić i naprawić swoje błędy ! Jedyne, co odkryłem, to że "c_void" nie istnieje, ale "None" działało w jego miejsce. Nie znam zbyt wiele C, więc staram się unikać zmiany tego kodu. – brebs

+0

Szybka obserwacja: Jak powinienem zrobić, aby funkcja C przyjęła n jako wskaźnik '* n', a następnie działała na nim, wykonując' m = * n + 1'? Mój obecny kod nie działa. W szczególności, czy istnieje szybka naprawa w kodzie Pythona, aby to zrobić? – brebs

+0

@brebs: Racja, przepraszam, 'void *' to 'c_void_p', ale' void' to po prostu 'None'. W każdym razie, dla kontynuacji, będę edytować odpowiedź. – abarnert

Powiązane problemy