Как поймать исключение Haskell, которое бросается в функцию обратного вызова Haskell, вызываемую функцией C?
Есть ли хороший способ поймать исключение haskell, которое выбрасывается в функцию обратного вызова haskell, вызываемую функцией c?
Например, позвольте мне иметь простую функцию c, которая просто вызывает данный обратный вызов,
void callmeback ( void (*callback) () ) {
callback ();
}
и код haskell, который использует эту функцию через ffi.
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module Main (main) where
import Foreign
import Control.Exception
import Data.Typeable
foreign import ccall safe "wrapper"
mkCallback :: IO () -> IO (FunPtr (IO ()))
foreign import ccall safe "callmeback"
callmeback :: FunPtr (IO ()) -> IO ()
data AnError = AnError String deriving (Show, Eq, Typeable)
instance Exception AnError
callback :: IO ()
callback = throwIO $ AnError "Catch me."
callMeBack :: IO () -> IO ()
callMeBack f = do fp <- mkCallback f
callmeback fp
main = do callMeBack callback `catch`
(\ e -> do putStrLn $ show (e :: AnError)
putStrLn "I caught you." )
putStrLn "-- Happy end."
Результат выхода (компиляция с GHC 7.8.2) выглядит следующим образом:
% ./Catch
Catch: AnError "Catch me."
Итак, похоже, что исключение, заброшенное в callback
, не может быть найдено в main
.
Как я могу сделать этот добрый код хорошо работать?
Ответы
Ответ 1
Вам нужно сделать это вручную, что будет выглядеть так:
-
Оберните функцию обратного вызова в код Haskell, который вызывает try
, а затем сериализует полученный Either SomeException ()
формат, который вы можете обрабатывать с C (вы можете использовать StablePtr
для SomeException
, но дело в том, что вам нужно как-то обрабатывать Either
).
-
Если ваш код C вызывает обратный вызов, проверьте, был ли результат Left exn
, и если да, распространите ошибку на верхний уровень вашего кода на C, освободив ресурсы по мере необходимости. Этот шаг не механический, поскольку у C нет исключений.
-
На верхнем уровне вашего C-кода повторно сериализуйте исключение или результат и прочитайте его в функции Haskell, которая обертывает ваш вызов кодом C, создает исключение или возвращает результат по мере необходимости.
Я не знаю ни одного примера программы, которая делает это.