How to catch a Haskell exception that is thrown in

2019-04-19 17:53发布

问题:

Is there any good way to catch a haskell exception, which is thrown in a haskell callback function called by a c function?

For example, let me have a simple c function which just calls a given callback,

void callmeback ( void (*callback) () ) {
  callback ();
}

and a haskell code which uses this function via 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."

The exection result (compiling with GHC 7.8.2) is as follows:

% ./Catch
Catch: AnError "Catch me."

So it seems like the exception thrown in callback can not be caught in main. How can I make this kind code work well?

回答1:

You have to do this manually, which would look like this:

  • Wrap your callback function in Haskell code that calls try, and then serializes the resulting Either SomeException () to a format you can process from C (you can use a StablePtr for the SomeException, but the point is that you need to handle the Either somehow).

  • Where your C code calls the callback, check whether the result was a Left exn and if so, propagate the error to the top level of your C code, freeing resources as appropriate along the way. This step is non-mechanical, since C does not have exceptions.

  • At the top level of your C code, re-serialize the exception or result and read it in a Haskell function that wraps your call to the C code, raising an exception or returning the result as appropriate.

I'm not aware of any example of a program that does this.