2
2
3
3
{-# LANGUAGE TemplateHaskell #-}
4
4
{-# LANGUAGE PatternSynonyms #-}
5
+ {-# LANGUAGE QuasiQuotes #-}
5
6
6
7
module Language.C.Inline.Cpp.Exceptions
7
8
( CppException (.. )
9
+ , toSomeException
8
10
, throwBlock
9
11
, tryBlock
10
12
, catchBlock
@@ -13,16 +15,26 @@ module Language.C.Inline.Cpp.Exceptions
13
15
import Control.Exception.Safe
14
16
import qualified Language.C.Inline as C
15
17
import qualified Language.C.Inline.Internal as C
18
+ import qualified Language.C.Inline.Cpp as Cpp
16
19
import Language.Haskell.TH
17
20
import Language.Haskell.TH.Quote
18
21
import Foreign
19
22
import Foreign.C
20
23
24
+ C. context Cpp. cppCtx
25
+ C. include " HaskellException.hxx"
26
+
21
27
-- | An exception thrown in C++ code.
22
28
data CppException
23
29
= CppStdException String
24
30
| CppOtherException (Maybe String ) -- contains the exception type, if available.
25
- deriving (Eq , Ord , Show )
31
+ | CppHaskellException SomeException
32
+ deriving (Show )
33
+
34
+ -- | Like 'toException' but unwrap 'CppHaskellException'
35
+ toSomeException :: CppException -> SomeException
36
+ toSomeException (CppHaskellException e) = e
37
+ toSomeException x = toException x
26
38
27
39
instance Exception CppException
28
40
@@ -33,20 +45,24 @@ pattern ExTypeNoException = 0
33
45
pattern ExTypeStdException :: CInt
34
46
pattern ExTypeStdException = 1
35
47
48
+ pattern ExTypeHaskellException :: CInt
49
+ pattern ExTypeHaskellException = 2
50
+
36
51
pattern ExTypeOtherException :: CInt
37
- pattern ExTypeOtherException = 2
52
+ pattern ExTypeOtherException = 3
38
53
39
- handleForeignCatch :: (Ptr CInt -> Ptr CString -> IO a ) -> IO (Either CppException a )
54
+ handleForeignCatch :: (Ptr CInt -> Ptr CString -> Ptr ( Ptr () ) -> IO a ) -> IO (Either CppException a )
40
55
handleForeignCatch cont =
41
56
alloca $ \ exTypePtr ->
42
- alloca $ \ msgPtrPtr -> do
57
+ alloca $ \ msgPtrPtr ->
58
+ alloca $ \ haskellExPtrPtr -> do
43
59
poke exTypePtr ExTypeNoException
44
60
-- we need to mask this entire block because the C++ allocates the
45
61
-- string for the exception message and we need to make sure that
46
62
-- we free it (see the @free@ below). The foreign code would not be
47
63
-- preemptable anyway, so I do not think this loses us anything.
48
64
mask_ $ do
49
- res <- cont exTypePtr msgPtrPtr
65
+ res <- cont exTypePtr msgPtrPtr haskellExPtrPtr
50
66
exType <- peek exTypePtr
51
67
case exType of
52
68
ExTypeNoException -> return (Right res)
@@ -55,6 +71,16 @@ handleForeignCatch cont =
55
71
errMsg <- peekCString msgPtr
56
72
free msgPtr
57
73
return (Left (CppStdException errMsg))
74
+ ExTypeHaskellException -> do
75
+ haskellExPtr <- peek haskellExPtrPtr
76
+ stablePtr <- [C. block | void * {
77
+ return (static_cast<HaskellException *>($(void *haskellExPtr)))->haskellExceptionStablePtr->stablePtr;
78
+ } |]
79
+ someExc <- deRefStablePtr (castPtrToStablePtr stablePtr)
80
+ [C. block | void{
81
+ delete static_cast<HaskellException *>($(void *haskellExPtr));
82
+ } |]
83
+ return (Left (CppHaskellException someExc))
58
84
ExTypeOtherException -> do
59
85
msgPtr <- peek msgPtrPtr
60
86
mbExcType <- if msgPtr == nullPtr
@@ -66,12 +92,12 @@ handleForeignCatch cont =
66
92
return (Left (CppOtherException mbExcType))
67
93
_ -> error " Unexpected C++ exception type."
68
94
69
- -- | Like 'tryBlock', but will throw 'CppException's rather than returning
95
+ -- | Like 'tryBlock', but will throw unwrapped 'CppHaskellException's or other 'CppException's rather than returning
70
96
-- them in an 'Either'
71
97
throwBlock :: QuasiQuoter
72
98
throwBlock = QuasiQuoter
73
99
{ quoteExp = \ blockStr -> do
74
- [e | either throwIO return =<< $(tryBlockQuoteExp blockStr) |]
100
+ [e | either ( throwIO . toSomeException) return =<< $(tryBlockQuoteExp blockStr) |]
75
101
, quotePat = unsupported
76
102
, quoteType = unsupported
77
103
, quoteDec = unsupported
@@ -95,6 +121,7 @@ tryBlockQuoteExp blockStr = do
95
121
_ <- C. include " <exception>"
96
122
_ <- C. include " <cstring>"
97
123
_ <- C. include " <cstdlib>"
124
+ _ <- C. include " HaskellException.hxx"
98
125
-- see
99
126
-- <https://stackoverflow.com/questions/28166565/detect-gcc-as-opposed-to-msvc-clang-with-macro>
100
127
-- regarding how to detect g++ or clang.
@@ -109,15 +136,21 @@ tryBlockQuoteExp blockStr = do
109
136
]
110
137
typePtrVarName <- newName " exTypePtr"
111
138
msgPtrVarName <- newName " msgPtr"
139
+ haskellExPtrVarName <- newName " haskellExPtr"
112
140
-- see
113
141
-- <https://stackoverflow.com/questions/561997/determining-exception-type-after-the-exception-is-caught/47164539#47164539>
114
142
-- regarding how to show the type of an exception.
115
143
let inlineCStr = unlines
116
144
[ ty ++ " {"
117
145
, " int* __inline_c_cpp_exception_type__ = $(int* " ++ nameBase typePtrVarName ++ " );"
118
146
, " char** __inline_c_cpp_error_message__ = $(char** " ++ nameBase msgPtrVarName ++ " );"
147
+ , " HaskellException** __inline_c_cpp_haskellexception__ = (HaskellException**)($(void ** " ++ nameBase haskellExPtrVarName ++ " ));"
119
148
, " try {"
120
149
, body
150
+ , " } catch (HaskellException &e) {"
151
+ , " *__inline_c_cpp_exception_type__ = " ++ show ExTypeHaskellException ++ " ;"
152
+ , " *__inline_c_cpp_haskellexception__ = new HaskellException(e);"
153
+ , if ty == " void" then " return;" else " return {};"
121
154
, " } catch (std::exception &e) {"
122
155
, " *__inline_c_cpp_exception_type__ = " ++ show ExTypeStdException ++ " ;"
123
156
, " #if defined(__GNUC__) || defined(__clang__)"
@@ -146,7 +179,7 @@ tryBlockQuoteExp blockStr = do
146
179
, " }"
147
180
, " }"
148
181
]
149
- [e | handleForeignCatch $ \ $(varP typePtrVarName) $(varP msgPtrVarName) -> $(quoteExp C.block inlineCStr) |]
182
+ [e | handleForeignCatch $ \ $(varP typePtrVarName) $(varP msgPtrVarName) $(varP haskellExPtrVarName) -> $(quoteExp C.block inlineCStr) |]
150
183
151
184
-- | Similar to `C.block`, but C++ exceptions will be caught and the result is (Either CppException value). The return type must be void or constructible with @{}@.
152
185
-- Using this will automatically include @exception@, @cstring@ and @cstdlib@.
0 commit comments