2017-06-13 1 views
0

Je suis un débutant à Cython avec un peu de connaissances en C et une certaine expérience python. Actuellement, j'essaie de connaître les types étendus, mais je ne comprends pas ce qui se passe avec les valeurs de pointeurs dans l'exemple suivant (code ci-dessous l'explication).pointeurs comme attributs dans les types étendus cython

En tant qu'exercice, j'implémente un solveur factice. Le problème est représenté par deux nombres 'a' et 'b' et la solution par 's', où s = a * b. J'ai défini deux structures C correspondantes, problem et solution. La structure du problème a deux membres int, 'a' et 'b'; la structure de la solution a un membre 's'. Il y a une fonction pour initialiser la structure du problème, init_problem(problem *p,int a, int b). En outre, il existe une fonction qui prend des pointeurs vers les structures et renvoie une solution, solution solve(problem *p). Enfin, deux autres fonctions impriment les valeurs (void dump_problem(problem *p) et void dump_solution(solution *s)). Tout cela en utilisant les déclarations cdef.

Ensuite, j'utilisé trois façons d'exposer le code C à python: fonction defdo_things(int a,int b) qui enveloppe les fonctions C, et deux cdef class, un en utilisant struct comme attributs et l'autre à l'aide des pointeurs vers structs comme des attributs (Solver_s et Solver_p , respectivement), y compris les méthodes d'emballage pour imprimer le problème et les solutions. La classe Solver_s fonctionne comme prévu. cependant, lors de l'utilisation de Solver_p, les pointeurs ne semblent pas être initialisés, renvoyant des valeurs incorrectes (voir test_pointers et sections de sortie). Je suppose que je manque un point sur les pointeurs et leur portée, mais je ne peux pas comprendre ce qui se passe. Toute aide pour obtenir ceci est grandement appréciée. J'utilise python 3.5.3 et cython 0.25.2 dans OS X 10.11.6 (El Capitan)

P.S: Première fois demander à SO, donc si je ne suis pas clair, je serai heureux de clarifier!

pointers.pyx

from libc.stdio cimport printf 

cdef struct problem: 
    int a 
    int b 

cdef struct solution: 
    int s 

cdef void init_problem(problem *p,int a, int b): 
    p.a = a 
    p.b = b 

cdef solution solve(problem *p): 
    cdef solution s 
    s.s = p.a * p.b 
    return(s) 

cdef void dump_problem(problem *p): 
    printf("Problem dump: a = %d,b = %d\n",p.a,p.b) 

cdef void dump_solution(solution *s): 
    printf("Solution dump: s= %d\n",s.s) 

def do_things(int a,int b): 
    cdef problem p 
    init_problem(&p,a,b) 
    cdef solution s = solve(&p) 
    dump_problem(&p) 
    dump_solution(&s) 

cdef class Solver_s: #Structs as attributes of Solver 
    cdef problem p 
    cdef solution s 
    def __cinit__(self,int a,int b): 
     print("\tInside Solver_s __cinit__") 
     init_problem(&self.p,a,b) 
     dump_problem(&self.p) 
     self.s = solve(&self.p) 
     dump_solution(&self.s) 
     print("\tGetting out of Solver_s __cinit__") 

    def show_problem(self): 
     dump_problem(&self.p) 

    def show_solution(self): 
     dump_solution(&self.s) 

cdef class Solver_p: #Pointers to structs as attributes 
    cdef problem *pptr 
    cdef solution *sptr 

    def __cinit__(self,int a, int b): 
     print("\tInside Solver_p __cinit__") 
     cdef problem p 
     self.pptr = &p 
     cdef solution s 
     self.sptr = &s 
     init_problem(self.pptr,a,b) 
     dump_problem(self.pptr) #It shows right values 
     self.sptr[0] = solve(self.pptr) 
     dump_solution(self.sptr) #It shows right values 
     print("\tGetting out of Solver_p __cinit__") 


    def show_problem(self): 
     dump_problem(self.pptr) 

    def show_solution(self): 
     dump_solution(self.sptr) 

test_pointers.py

import pyximport; pyximport.install() 
import pointers 

print("\tSolving as a function") 
pointers.do_things(2,3) 

print("\tSolving as a Extended Type, structs as attributes") 
sol_s = pointers.Solver_s(4,5) 
print("\t###Problem definition unsing Solver_s methods###") 
sol_s.show_problem() 
print("\t###Solution definition using Solver_s methods###") 
sol_s.show_solution() 


print("\tSolving as a Extended Type, pointers to structs as attributes") 
sol_p = pointers.Solver_p(6,7) 
print("\t###Problem definition unsing Solver_p methods###") 
print("\t###Gives weird values###") 
sol_p.show_problem() 
print("\t###Solution definition using Solver_p methods###") 
print("\t###Gives weird values###") 
sol_p.show_solution() 

Sortie

Solving as a function 
Problem dump: a = 2,b = 3 
Solution dump: s= 6 
    Solving as a Extended Type, structs as attributes 
    Inside Solver_s __cinit__ 
Problem dump: a = 4,b = 5 
Solution dump: s= 20 
    Getting out of Solver_s __cinit__ 
    ###Problem definition unsing Solver_s methods### 
Problem dump: a = 4,b = 5 
    ###Solution definition using Solver_s methods### 
Solution dump: s= 20 
    Solving as a Extended Type, pointers to structs as attributes 
    Inside Solver_p __cinit__ 
Problem dump: a = 6,b = 7 
Solution dump: s= 42 
    Getting out of Solver_p __cinit__ 
    ###Problem definition unsing Solver_p methods### 
    ###Gives weird values### 
Problem dump: a = 1,b = 0 
    ###Solution definition using Solver_p methods### 
    ###Gives weird values### 
Solution dump: s= 185295816 

Répondre

2

En Solver_p.__cinit__, p et s sont des variables locales qui existent uniquement pour la durée de l'appel à __cinit__. L'instance Solver_p dure au-delà de l'appel et, par conséquent, pendant la plus grande partie de sa durée de vie, les pointeurs sont sur des données non valides.

La solution consiste à allouer de la mémoire de tas à la place:

# at the top 
from libc.stdlib cimport malloc, free 

cdef class Solver_p: 
    # .... 
    def __cinit__(self,...): 
     self.pptr = <problem*>malloc(sizeof(problem)) 
     self.sptr = <solution*>malloc(sizeof(solution)) 
     # ... 
    def __dealloc__(self): 
     free(self.pptr) 
     free(self.sptr) 
     # ... 

Vous devez être prudent de faire en sorte que toute la mémoire que vous allouez est libéré de façon appropriée.


Mon conseil est que si vous ne comprenez pas comment utiliser des pointeurs correctement dans C, alors vous ne devriez pas être de les utiliser dans Cython.