2012-01-10 34 views
5

Tôi đang cố gắng viết trình bao bọc xung quanh chương trình C để tôi có thể gọi nó từ Python. Tôi đang sử dụng Cython để làm điều này. Hàm C nhận hàm gọi lại làm đối số, nhưng hàm gọi lại này sẽ chỉ được biết khi chạy chương trình python. Tôi đã tìm kiếm cách thực hiện điều này và dường như không có giải pháp đơn giản nhưng có vẻ như sau đây hoạt động:Cách chuyển con trỏ hàm tới chương trình bên ngoài trong Cython

#python.py 

libc = cdll.LoadLibrary("myfunc.so") #Callback function is defined in myfunc.so 
.... 
c_wrapper(libc.fun, ...) 

.

#c_wrapper.pyx 

cdef extern void mainfunction(void *F, ...) #The intial C function we are wrapping 
ctypedef void (*myfuncptr)() 

def c_wrapper(f, ...) # our function pointer is passed to the wrapper as a Python object 
    cdef myfuncptr thisfunc 
    thisfunc = (<myfuncptr*><size_t>addressof(f))[0] 
    mainfunction(thisfunc, ...) 

Phương pháp này làm việc cho các chức năng C và FORTRAN (tôi đang giả định nó sẽ làm việc cho languges biên soạn nhất) và chức năng Python (sử dụng các loại C), nhưng có vẻ như một chút vụng về. Có cách nào đơn giản hơn để làm điều này trong Cython?

Cảm ơn

EDIT: Tôi không thể thay đổi thư viện C Tôi cố gắng để quấn

+0

Nó w sẽ tốt đẹp nếu có. Nhưng nếu bạn không nhận được bất kỳ, giải pháp đề xuất của bạn có thể hữu ích cho người khác. Trong trường hợp đó, có lẽ bạn có thể thuật lại nó thành một câu hỏi/giải pháp ghép đôi? – dsign

+0

Ok, một điều tôi đã không nhận được lần đầu tiên, và do đó câu trả lời của tôi: chức năng gọi lại có thể đến từ một thư viện C/FORTRAN hoặc nó có thể là một hàm gốc Python. Đó là trường hợp? –

+0

@Ricardo, Đúng vậy. Xin lỗi tôi có thể không nói rõ điều này. Basilcaly, nếu hàm xuất phát từ thư viện C/FORTRAN, nó được lưu trữ dưới dạng đối tượng _FuncPtr, nếu hàm này được viết bằng Python nguyên gốc, nó có thể được lưu dưới dạng CFunctionType (bằng cách sử dụng kiểu C). Tôi cho rằng những gì tôi đang thực sự yêu cầu là nếu có bất kỳ cách nào để đưa bất kỳ/tất cả những điều này vào một con trỏ hàm C trong wrapper? – SimpleSimon

Trả lời

5

Tôi đoán bạn đang nhận thức được this?

Có thể gọi mã Python của tôi từ C không?

Trả lời: Có, dễ dàng. Thực hiện theo các ví dụ trong Demo/callback/trong việc phân phối nguồn Cython

Vì vậy, khi biết rằng bạn có thể dload chức năng chính của bạn, chúng ta hãy xem cách tiếp cận khác. Tôi đã viết một vài chức năng ngu ngốc để kiểm tra này:

/* lib.c -> lib.so */ 
#include <stdio.h> 

void fn1(void) { 
     puts("Called function 1"); 
} 

void fn2(void) { 
     puts("Called function 2"); 
} 

sau đó, chức năng mà mất callback

/* main.c -> main.so */ 

typedef void (*callback)(); 

void mainfunction(void *F) { 
     ((callback)F)(); 
} 

Mà có thể được chuyển trực tiếp từ Python:

>>> from ctypes import cdll 
>>> lib = cdll.LoadLibrary('./lib.so') 
>>> main = cdll.LoadLibrary('./main.so') 
>>> main.mainfunction(lib.fn1) 
Called function 1 
>>> main.mainfunction(lib.fn2) 
Called function 2 

Và bây giờ , hãy bọc hàm Python:

>>> from ctypes import CFUNCTYPE 
>>> def pyfn(): 
...  print "Called the Python function" 
... 
>>> CWRAPPER = CFUNCTYPE(None) 

>>> wrapped_py_func = CWRAPPER(pyfn) 
>>> main.mainfunction(wrapped_py_func) 
Called the Python function 
+0

Cảm ơn @Ricardo Cárdenes, Tôi đã thấy ví dụ này, nó cho thấy cách bọc một API C có giao diện gọi lại, để bạn có thể chuyển các hàm Python vào nó như gọi lại. Đây là một ví dụ tốt về cách vấn đề này có thể được giải quyết nếu tôi có quyền truy cập vào thư viện C mà tôi đang cố gắng bọc, không may là tôi không thể chạm vào nó. Cảm ơn bạn – SimpleSimon

+0

Mh ... Tôi nghĩ rằng tôi hiểu sai ý định của bạn, sau đó ... –

+0

@Dan: câu trả lời mới, dựa trên các giả định khác. –

Các vấn đề liên quan