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


person tmizmd    schedule 19.04.2015    source источник
comment
В данный момент я задаю полусвязанный вопрос: stackoverflow.com/questions/29725324/ - это не сработает для меня, но я думаю, что это может сработать для вас... посмотрите на пакет enclosed-exceptions и посмотрите, принесет ли это вам удачу: hackage.haskell.org/package/enclosed-exceptions   -  person TheCriticalImperitive    schedule 19.04.2015


Ответы (1)


Вы должны сделать это вручную, что будет выглядеть так:

  • Оберните свою функцию обратного вызова в код Haskell, который вызывает try, а затем сериализует полученный Either SomeException () в формат, который вы можете обрабатывать из C (вы можете использовать StablePtr для SomeException, но дело в том, что вам нужно как-то обрабатывать Either).

  • Когда ваш код C вызывает обратный вызов, проверьте, был ли результат Left exn, и если да, распространите ошибку на верхний уровень вашего кода C, освобождая ресурсы по мере необходимости. Этот шаг не механический, так как в C нет исключений.

  • На верхнем уровне вашего кода C повторно сериализуйте исключение или результат и прочитайте его в функции Haskell, которая обертывает ваш вызов кода C, вызывая исключение или возвращая результат в зависимости от ситуации.

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

person Reid Barton    schedule 19.04.2015