Câu hỏi của tôi là về cách viết Giao diện Haskell thân thiện mô hình các cuộc gọi lại có thể được gọi từ mã C. Tuy nhiên, các cuộc gọi lại được giải quyết ở đây (HaskellWiki), tôi tin rằng câu hỏi này phức tạp hơn ví dụ từ liên kết đó.Gọi lại FFI Haskell với Tiểu bang
Giả sử chúng ta có mã C, đòi hỏi callbacks và header trông giống như sau:
typedef int CallbackType(char* input, char* output, int outputMaxSize, void* userData)
int execution(CallbackType* caller);
Trong trường hợp này các chức năng execution
mất một chức năng gọi lại và sẽ sử dụng để xử lý dữ liệu mới, thực chất là đóng cửa. Các cuộc gọi trở lại hy vọng một chuỗi đầu vào, một bộ đệm đầu ra đã được phân bổ với kích thước outputMaxSize
và con trỏ userData, mà có thể được đúc tuy nhiên bên trong gọi lại.
Chúng tôi làm những điều tương tự trong haskell, khi chúng tôi vượt qua xung quanh đóng cửa với MVars, vì vậy chúng tôi vẫn có thể giao tiếp. Vì vậy, khi chúng tôi viết giao diện nước ngoài, chúng tôi muốn giữ loại này.
Cụ thể ở đây là những gì Bộ luật FFI có thể trông giống như:
type Callback = CString -> CString -> CInt -> Ptr() -> IO CInt
foreign import ccall safe "wrapper"
wrap_callBack :: Callback -> IO (FunPtr Callback)
foreign import ccall safe "execution"
execute :: FunPtr Callback -> IO CInt
Người dùng sẽ có thể làm được việc này, nhưng nó cảm thấy như một giao diện nghèo từ họ cần phải viết callbacks với loại PTR(). Thay vào đó, chúng tôi muốn thay thế điều này bằng MVars mà cảm thấy tự nhiên hơn. Vì vậy, chúng tôi muốn viết một hàm:
myCallback :: String -> Int -> MVar a -> (Int, String)
myCallback input maxOutLength data = ...
Để chuyển đổi sang C, chúng tôi muốn có một chức năng như:
castCallback :: (String -> Int -> MVar a -> (Int, String))
-> (CString -> CString -> CInt -> Ptr() -> IO CInt)
main = wrap_callBack (castCallback myCallback) >>= execute
Trong trường hợp này castCallback là dành cho hầu hết các phần không khó thực hiện, chuyển đổi chuỗi -> cstring, Int -> CInt và sao chép trên chuỗi đầu ra.
Tuy nhiên, phần cứng đang giải quyết MVar thành Ptr, không nhất thiết phải lưu trữ được.
Câu hỏi của tôi là cách tốt nhất để viết về mã gọi lại trong Haskell, cái nào vẫn có thể được liên lạc với.
Tôi không phải là một chuyên gia về FFI, nhưng sự hiểu biết của tôi là những người C đã sử dụng thủ thuật 'void * 'vì họ không có đóng cửa thực sự. Trong Haskell, chúng ta có các bao đóng thực sự - vì vậy chỉ cần để đối số 'void *' ra khỏi giao diện Haskell hoàn toàn và đóng trên bất kỳ dữ liệu cục bộ nào (có lẽ là 'IORef' hoặc' MVar') thông qua một phần ứng dụng. –
Ahh! Gotcha. Tôi sẽ thử. Tôi nghĩ rằng đó là những gì ràng buộc có thể đã được thực hiện, nhưng tôi đã không bắt được điều đó. Cảm ơn vì sự trả lời! –
@tigger, tôi đã làm thủ thuật tương tự trước đó DanielWagner đề xuất cho cuộc gọi đồng bộ vào Haskell từ C - có được một chức năng một phần bằng cách áp dụng đối số MVar và để hàm C gọi lại với dữ liệu cho MVar. Nếu MVar của bạn phức tạp hơn, thì bạn có thể sử dụng vector Storable hoặc một thể hiện có thể lưu trữ để truyền dữ liệu đến MVar từ C. Chuyển một Ptr đến Storable instance tới C. Ví dụ ở đây: http://hpaste.org/63702 – Sal