Как поймать исключение 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, создает исключение или возвращает результат по мере необходимости.

Я не знаю ни одного примера программы, которая делает это.