2017-10-15 18 views
0

PyPy와 cffi를 사용하여 C에서 Python 함수를 임베드하려고합니다. 나는 PyPy 문서에서 this guide을 팔로우하고 있습니다.cffi를 사용하여 C에 문자열을 반환하는 Python 함수를 포함하려면 어떻게해야합니까?

문제는 내가 찾은 모든 예제가 int에서 작동하고, 함수가 문자열을 받아 문자열을 반환한다는 것입니다. C에서이 함수를 포함하는 방법을 알아낼 수 없습니다. C가 실제로 문자열을 갖고있는 것처럼 보이지 않으므로 char 배열을 사용하는 것이 좋습니다. 여기

내가 무엇을 시도했다입니다 : - :

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! 

I 돈

# 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을 실행하면이 오류를 얻을 수 C에 대한 많은 경험이 있지만 문자열 인수/리턴 값을 잘못 전달하고있는 것처럼 느껴집니다. ue. 이 작업을 올바르게 수행하려면 어떻게해야합니까?

도움 주셔서 감사합니다.

답변

2

pypy의 더 이상 사용되지 않는 인터페이스를 사용해서는 안됩니다. 대신 http://cffi.readthedocs.io/en/latest/embedding.html을 참조하십시오.

C 언어에는 "문자열"이 없으며 char 배열 만 있습니다. C에서 "문자열"을 반환하고자하는 함수는 대개 으로 다르게 작성됩니다. 첫 번째 인수로 기존 버퍼 (유형 : char[])에 대한 포인터를 받고 두 번째 인수는 해당 버퍼의 길이입니다. 호출되면 버퍼를 채 웁니다. 호출자에서 버퍼가 너무 작은 상황을 처리해야하므로 이상적 일 수 있습니다 (예 : 더 큰 배열을 할당하고 함수를 다시 호출하십시오.

또는 일부 기능은 포기하고 새로 malloc()-을 반환합니다. 그런 다음 호출자는 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

내가 링크 된 페이지에서 컴파일하는 법을 아주주의 깊게 읽으십시오. 두 가지 유스 케이스에 대해 두 가지 옵션이 있습니다. 이 옵션을 따르지 않는 것 같습니다 ... –