2016-07-09 11 views
0

Ich lerne verschiedene Möglichkeiten, wie Code in C zu Python geschrieben werden, weil ich eine API für ein Mikrochip-Gerät, die ziemlich ... mühsam zu arbeiten, und ich würde gerne mein Leben in der Zukunft leichter machen, indem ich einen Python Wrapper dafür hinzufüge, der mir erlauben wird, Sachen viel schneller zu prüfen. Eine Möglichkeit, das zu tun, ist die Verwendung des Moduls, das seinen Benutzer sogar mit verify() versorgt, das im Grunde den C Compiler anruft, um zu überprüfen, ob das bereitgestellte cdef(...) korrekt ist.Python CFFI - nicht formatierte Python-String als Byte-Array in Funktionsaufruf verwenden

Ich habe ein kleines Projekt geschrieben, so dass ich zuerst lernen kann, wie man richtig verwendet . Es besteht aus zwei Teilen

  1. Bibliothek - geschrieben in C. Ich benutze cmake und make entsprechend seinen Code zu kompilieren:

    CMakeLists.txt

    project(testlib_for_cffi) 
    cmake_minimum_required(VERSION 2.8) 
    
    set(CMAKE_BUILD_TYPE Release) 
    set(CMAKE_CXX_FLAGS "-fPIC ${CMAKE_C_FLAGS}") 
    # Debug build 
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -g -O0") 
    # Release build 
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os") 
    
    aux_source_directory(. SRC_LIST) 
    add_library(testcffi SHARED ${SRC_LIST}) 
    
    # Not required for the library but needed if I want to check for memory leaks with Valgrind 
    set(SRC main.c) 
    add_executable(${PROJECT_NAME} ${SRC}) 
    target_link_libraries(${PROJECT_NAME} PUBLIC testcffi) 
    

    testcffi.h

    typedef struct 
    { 
        double x; 
        double y; 
        double z; 
        char *label; 
    } point_t; 
    
    // Creation, printing and deletion 
    point_t* createPoint(double x, double y, double z, char *label); 
    void printPoint(point_t *point); 
    void deletePoint(point_t *point); 
    

    testcffi.c

    #include "testcffi.h" 
    #include <stdio.h> 
    #include <malloc.h> 
    
    point_t* createPoint(double x, double y, double z, char *label) { 
        point_t *p = malloc(sizeof(point_t)); 
        p->x = x; 
        p->y = y; 
        p->z = z; 
        p->label = label; 
    
        return p; 
    } 
    
    void printPoint(point_t *point) { 
        if(point == NULL) return; 
        printf("Data:\n\tx : %f\n\ty : %f\n\tz : %f\n\tmsg : \"%s\"\n", point->x, point->y, point->z, point->label); 
    } 
    
    void deletePoint(point_t *point) { 
        if(point == NULL) return; 
        free(point); 
        point = NULL; 
    } 
    
  2. Prüfregeln in Python - der Code zeigt die Verwendung der struct zusammen mit den drei Funktionen aus der Bibliothek oben:

     #!/usr/bin/python3 
    
         from cffi import FFI 
         import random 
    
         ffi = FFI() 
    
         # Add library's header 
         ffi.cdef(''' 
          typedef struct 
          { 
           double x; 
           double y; 
           double z; 
           char * label; 
          } point_t; 
    
          // Creation, printing and deletion 
          point_t * createPoint(double x=0., double y=0., double z=0., char *label="my_label"); 
          void printPoint(point_t *point); 
          void deletePoint(point_t *point); 
         ''') 
    
         # Load shared object from subdirectory `build` 
         CLibTC = ffi.dlopen('build/libtestcffi.so') 
    
         def createList(length=5): 
          if len: 
           lst = [] 
           for i in range(0, length): 
            lst.append(CLibTC.createPoint(
             float(random.random()*(i+1)*10), 
             float(random.random()*(i+1)*10), 
             float(random.random()*(i+1)*10), 
             b'hello' # FIXME Why does ONLY this work? 
             # ('point_%d' % i).encode('utf-8') # NOT WORKING 
             # 'point_{0}'.format(str(i)).encode('utf-8') # NOT WORKING 
             # ffi.new('char[]', 'point_{0}'.format(str(i)).encode('utf-8')) # NOT WORKING 
            )) 
    
           return lst 
          return None 
    
    
         def printList(lst): 
          if lst and len(lst): 
           for l in lst: 
            CLibTC.printPoint(l) 
    
         list_of_dstruct_ptr = createList(10) 
         printList(list_of_dstruct_ptr) 
    

Das Problem com es aus dem Byte-Array, das ich meinen Python String umwandeln muss, um die Daten an die entsprechende Stelle in meinem C Code zu übergeben.

Der obige Code funktioniert, aber ich möchte andere als ähnliche Zeichenfolgen wie b'hello' verwenden. Deshalb habe ich versucht, die format() (zusammen mit ihrer Kurzform %) in Python zu kombinieren, eine Reihe von Buchstaben und eine Zahl aber. Es hat nicht geklappt. Ich bekomme entweder "" als Wert für die label Parameter meiner point_tstruct oder ich bekomme eine seltsame wechselnde Müll-Daten (meist seltsame Zeichen, die weder Buchstaben noch Ziffern sind).

Ich dachte, dass ich die encode() Funktion falsch benutze, aber wenn ich es innerhalb meiner interaktiven Shell Python getestet habe, bekam ich die gleiche Ausgabe wie mit b'...'.

Irgendeine Idee, was hier vor sich geht?


A nice-to-know Frage: Von dem, was ich bisher gelesen habe scheint es, dass cffi die Garbage-Collection in Python verwendet die dynamisch zugewiesenen Speicher in Ihrem C-Code freigegeben.Ich habe es mit einer Reihe von Punkten getestet, aber ich möchte sicherstellen, dass dies tatsächlich immer der Fall ist.


Update: Okay, so scheint es, dass sich die Dinge ohne new(...) in diesem Fall jedoch funktionieren alle Werte die gleiche wie die letzte in der Schleife sind. Zum Beispiel, wenn die Schleife bis 10 geht, dann haben alle struct Python-Objekte die 10 in ihren Etiketten. Dies scheint ein Bezugsproblem zu sein. Wenn ich new(...) verwende, bekomme ich Mülldaten.

Antwort

1

In Ihrem C-Code hält die point_t Struktur in label eine char *, d. H. Einen Zeiger auf eine andere Stelle im Speicher. Wenn Sie 10 point_t Strukturen erstellen, enthalten sie Zeiger auf 10 Zeichenfolgen, die sich irgendwo im Speicher befinden. Sie müssen sicherstellen, dass diese 10 Strings so lange am Leben erhalten werden, wie Sie die point_t Strukturen verwenden. CFFI kann nicht vermuten, dass es eine solche Beziehung gibt. Wenn Sie den Anruf CLibTC.createPoint(..., some_string) ausführen, reserviert CFFI ein char[] Array um den Aufruf und kopiert some_string darin, aber dieser char[] Speicher wird nach dem Aufruf freigegeben. Statt

diese Art von Code verwenden:

c_string = ffi.new("char[]", some_string) 
lst.append(createPoint(..., c_string)) 
keepalive.append(c_string) 

wo keepalive eine andere Liste, die Sie am Leben sicher bleibt so lange machen müssen, wie Sie die point_t benötigen gültige label ‚s enthalten.

+0

: O Absolut richtig und ich habe es total vermisst, obwohl ich es 1000 mal in der Dokumentation gelesen habe. LOL Wenn man also einen Wrapper einer C-basierten API erstellt, muss man spezielle Objekte erstellen, die eine bessere Verwaltung solcher Referenzen ermöglichen. Ich danke dir sehr. Ich habe einige weiße Haare bekommen, die während meines Kampfes wachsen, um das zu lösen. – rbaleksandar