Мой вопрос о том, как написать дружественные интерфейсы Haskell, которые моделируют обратные вызовы, которые могут быть вызваны из кода C.Обратные вызовы рассматриваются здесь ( HaskellWiki ), однако я считаю, что этот вопрос более сложный, чем пример из этой ссылки.
Предположим, у нас есть код C, требующий обратных вызовов, а заголовок выглядит какследующее:
typedef int CallbackType(char* input, char* output, int outputMaxSize, void* userData)
int execution(CallbackType* caller);
В этом случае функция execution
принимает функцию обратного вызова и будет использовать ее для обработки новых данных, по существу, замыкания.Обратный вызов ожидает входную строку, выходной буфер, который был выделен с размером outputMaxSize
и указатель userData, который может быть преобразован, однако, внутри обратного вызова.
Мы делаем подобные вещи в haskell, когда передаемвокруг замыканий с MVars, так что мы все еще можем общаться.Поэтому, когда мы пишем внешний интерфейс, мы хотели бы сохранить этот тип типа.
В частности, вот как может выглядеть код FFI:
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
Пользователи должны иметь возможностьделать подобные вещи, но это похоже на плохой интерфейс, так как они должны писать обратные вызовы с типом Ptr ().Скорее мы хотели бы заменить это на MVars, которые кажутся более естественными.Итак, мы хотели бы написать функцию:
myCallback :: String -> Int -> MVar a -> (Int, String)
myCallback input maxOutLength data = ...
Чтобы преобразовать в C, мы хотели бы иметь такую функцию:
castCallback :: ( String -> Int -> MVar a -> (Int, String) )
-> ( CString -> CString -> CInt -> Ptr () -> IO CInt )
main = wrap_callBack (castCallback myCallback) >>= execute
В этом случае castCallback имеет видпо большей части несложно реализовать, преобразуйте строку -> cstring, Int -> CInt и скопируйте выходную строку.
Однако сложная часть заключается в преобразовании MVar в Ptr, который не обязательно сохраняется.
Мой вопрос - как лучше всего написать код обратного вызова в Haskell, с которым все еще можно связаться.