如何捕获在C函数调用的Haskell回调函数中抛出的Haskell异常?

时间:2015-04-19 08:35:01

标签: haskell exception ffi

有没有什么好方法可以捕获一个haskell异常,它被c函数调用的haskell回调函数抛出?

例如,让我有一个简单的c函数,它只调用给定的回调函数,

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

和通过ffi使用此功能的haskell代码。

{-# 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 个答案:

答案 0 :(得分:4)

您必须手动执行此操作,如下所示:

  • 在调用try的Haskell代码中包装回调函数,然后将生成的Either SomeException ()序列化为可以从C处理的格式(可以使用StablePtr SomeException,但重点是你需要以某种方式处理Either

  • 在C代码调用回调的地方,检查结果是否为Left exn,如果是,则将错误传播到C代码的顶层,并在适当的时候释放资源。此步骤是非机械的,因为C没有例外。

  • 在C代码的顶层,重新序列化异常或结果,并在Haskell函数中读取它,该函数包装您对C代码的调用,引发异常或返回结果。

我不知道任何一个程序的例子。