0

かなり面倒な Microchip デバイス用の API を持っているので、コードCをインクルードするさまざまな方法を学んでいます。これにより、テストをはるかに高速に行うことができます。それを行う1つの方法は、基本的にコンパイラーを呼び出して、提供されたものが正しいかどうかをチェックすることをユーザーに提供するモジュールを使用することです。PythonPythoncffiverify()Ccdef(...)

最初に適切に使用する方法を学ぶことができるように、小さなプロジェクトを作成しましたcffi。2つの部分で構成されています

  1. ライブラリ- C で書かれています。私はそれに応じてそのコードをコンパイルするために使用cmakemakeます。

    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. Python でのテスト コードstruct- コードは、上記のライブラリの 3 つの関数と共にの使用法を示しています。

            #!/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)
    

Python問題は、コード内のそれぞれの場所にデータを渡すために文字列を変換する必要があるバイト配列に起因しCます。

上記のコードは機能していますが、 に似た他の文字列を使用したいと思いますb'hello'。そのため、format()(短い形式の とともに%) inを使用Pythonして、一連の文字と数字を組み合わせようとしましたが、. うまくいきませんでした。my のパラメーターの""値として取得するか、奇妙な交互のガベージ データ (文字でも数字でもないほとんどの奇妙な文字) を取得します。labelpoint_t struct

encode()関数を間違って使用していると思いましたが、Pythonインタラクティブシェル内でテストしたところ、 を使用した場合と同じ出力が得られましたb'...'

ここで何が起こっているのか分かりますか?


知っておくと便利な質問:これまで読んだことからcffi、ガベージ コレクションを使用してPython、C コードで動的に割り当てられたメモリの割り当てを解除しているようです。たくさんのポイントでテストしましたが、これが実際に常に当てはまることを確認したいと思います。


更新: わかりました。new(...)動作しないように見えますが、その場合、すべての値はループ内の最後の値と同じです。たとえば、ループが 10 に達すると、すべてのstructPython オブジェクトのラベルに 10 が含まれます。これは参照の問題のようです。使用するnew(...)と、ガベージデータが取得されます。

4

1 に答える 1

1

C コードでは、point_t構造体は に保持さlabelchar *ます。つまり、メモリ内の別の場所へのポインタです。10 個の構造体を作成するpoint_tと、メモリ内の別の場所にある 10 個の文字列へのポインターが保持されます。構造を使用している限り、これらの 10 個の文字列が存続していることを確認する必要がありますpoint_t。CFFI は、そのような関係があるとは推測できません。呼び出しを行うとCLibTC.createPoint(..., some_string)、CFFIは呼び出しのchar[]周りに配列を割り当ててコピーsome_stringしますが、このchar[]メモリは呼び出し後に解放されます。

代わりにそのようなコードを使用してください。

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

whereは別のリストであり、有効な を含めるkeepalive必要がある限り、このリストが存続していることを確認する必要があります。point_tlabel

于 2016-07-10T14:49:53.860 に答える