2017-10-15 131 views
0

我想在C中使用PyPy和cffi嵌入Python函數。我正在關注PyPy文檔中的this guide如何嵌入一個使用cffi在C中返回字符串的Python函數?

問題是,我找到的所有例子都是在int上進行操作的,我的函數接受一個字符串並返回一個字符串。我似乎無法弄清楚如何在C中嵌入這個函數,因爲C似乎並沒有真正的字符串,而是使用了字符數組。

這是我已經試過:

# interface.py 

import cffi 

ffi = cffi.FFI() 
ffi.cdef(''' 
struct API { 
    char (*generate_cool_page)(char url[]); 
}; 
''') 

... 


@ffi.callback("char[] (char[])") 
def generate_cool_page(url): 
    # do some processing with BS4 
    return str(soup) 

def fill_api(ptr): 
    global api 
    api = ffi.cast("struct API*", ptr) 
    api.generate_cool_page = generate_cool_page 

-

// c_tests.c 

#include "PyPy.h" 
#include <stdio.h> 
#include <stdlib.h> 

struct API { 
    char (*generate_cool_page)(char url[]); 
}; 

struct API api; /* global var */ 

int initialize_api(void) 
{ 
    static char source[] = 
     "import sys; sys.path.insert(0, '.'); " 
     "import interface; interface.fill_api(c_argument)"; 
    int res; 

    rpython_startup_code(); 
    res = pypy_setup_home(NULL, 1); 
    if (res) { 
     fprintf(stderr, "Error setting pypy home!\n"); 
     return -1; 
    } 
    res = pypy_execute_source_ptr(source, &api); 
    if (res) { 
     fprintf(stderr, "Error calling pypy_execute_source_ptr!\n"); 
     return -1; 
    } 
    return 0; 
} 

int main(void) 
{ 
    if (initialize_api() < 0) 
     return 1; 

    printf(api.generate_cool_page("https://example.com")); 

    return 0; 
} 

當我運行gcc -I/opt/pypy3/include -Wno-write-strings c_tests.c -L/opt/pypy3/bin -lpypy3-c -g -o c_tests然後運行./c_tests,我得到這個錯誤:

debug: OperationError: 
debug: operror-type: CDefError 
debug: operror-value: cannot render the type <char()(char *)>: it is a function type, not a pointer-to-function type 
Error calling pypy_execute_source_ptr! 

我不在C語言方面沒有太多的經驗,我覺得我誤表了字符串參數/返回值UE。我如何正確地做到這一點?

感謝您的幫助!

回答

2

請注意,您不應該使用pypy的棄用接口來嵌入;相反,請參閱http://cffi.readthedocs.io/en/latest/embedding.html

C語言沒有「字符串」,但只有字符數組。在C中,想要返回「字符串」的函數通常以不同的方式寫入 :它接受指向預先存在的緩衝區(類型爲char[])的指針作爲第一個參數,並將該緩衝區的長度作爲第二個參數;並在被調用時填充緩衝區。這可能很麻煩,因爲你理想上需要處理調用者中緩衝區太小的情況,例如,分配一個更大的數組並再次調用該函數。

或者,某些功能放棄並返回新的malloc() -ed char *。那麼來電者必須記得free()它,否則會發生泄漏。在這種情況下,我會推薦這種方法,因爲在調用之前猜測字符串的最大長度可能會很困難。

所以,類似的東西。假設你開始 http://cffi.readthedocs.io/en/latest/embedding.html,改變 plugin.h含有::

// return type is "char *" 
extern char *generate_cool_page(char url[]); 

,並更改該位的plugin_build.py ::

ffibuilder.embedding_init_code(""" 
    from my_plugin import ffi, lib 

    @ffi.def_extern() 
    def generate_cool_page(url): 
     url = ffi.string(url) 
     # do some processing 
     return lib.strdup(str(soup)) # calls malloc() 
""") 
ffibuilder.cdef(""" 
    #include <string.h> 
    char *strdup(const char *); 
""") 

從C代碼,你不需要在initialize_api()在所有 新的嵌入模式;相反,你剛纔說#include "plugin.h" 並直接調用該函數::

char *data = generate_cool_page("https://example.com"); 
if (data == NULL) { handle_errors... } 
printf("Got this: '%s'\n", data); 
free(data); // important! 
+0

感謝您指出我正確的文檔 - 我會嘗試了這一點! – TheInitializer

+0

你確定我不需要'initialize_api()'嗎? GCC給我'未定義的引用'generate_cool_page'。 (我已經包含'plugin.h') – TheInitializer

+0

仔細閱讀如何編譯,在我鏈接的頁面上。對於兩種不同的用例有兩種選擇。看起來你並沒有遵循這些選項...... –

相關問題