inline-c-cpp 0.2.0.2 → 0.2.1.0
raw patch · 3 files changed
+130/−7 lines, 3 filesdep +hspecdep +safe-exceptionsdep ~inline-cPVP ok
version bump matches the API change (PVP)
Dependencies added: hspec, safe-exceptions
Dependency ranges changed: inline-c
API changes (from Hackage documentation)
+ Language.C.Inline.Cpp.Exceptions: CppOtherException :: CppException
+ Language.C.Inline.Cpp.Exceptions: CppStdException :: String -> CppException
+ Language.C.Inline.Cpp.Exceptions: catchBlock :: QuasiQuoter
+ Language.C.Inline.Cpp.Exceptions: data CppException
+ Language.C.Inline.Cpp.Exceptions: instance GHC.Classes.Eq Language.C.Inline.Cpp.Exceptions.CppException
+ Language.C.Inline.Cpp.Exceptions: instance GHC.Classes.Ord Language.C.Inline.Cpp.Exceptions.CppException
+ Language.C.Inline.Cpp.Exceptions: instance GHC.Exception.Exception Language.C.Inline.Cpp.Exceptions.CppException
+ Language.C.Inline.Cpp.Exceptions: instance GHC.Show.Show Language.C.Inline.Cpp.Exceptions.CppException
Files
- inline-c-cpp.cabal +6/−1
- src/Language/C/Inline/Cpp/Exceptions.hs +88/−0
- test/tests.hs +36/−6
inline-c-cpp.cabal view
@@ -1,5 +1,5 @@ name: inline-c-cpp-version: 0.2.0.2+version: 0.2.1.0 synopsis: Lets you embed C++ code into Haskell. description: Utilities to inline C++ code into Haskell using inline-c. See tests for example on how to build.@@ -19,10 +19,12 @@ library exposed-modules: Language.C.Inline.Cpp+ Language.C.Inline.Cpp.Exceptions ghc-options: -Wall build-depends: base >=4.7 && <5 , inline-c >= 0.6.0.0 , template-haskell+ , safe-exceptions hs-source-dirs: src default-language: Haskell2010 @@ -31,6 +33,9 @@ hs-source-dirs: test main-is: tests.hs build-depends: base >=4 && <5+ , inline-c , inline-c-cpp+ , safe-exceptions+ , hspec default-language: Haskell2010 extra-libraries: stdc++
+ src/Language/C/Inline/Cpp/Exceptions.hs view
@@ -0,0 +1,88 @@+-- | A module that contains exception-safe equivalents of @inline-c@ QuasiQuoters.++{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE PatternSynonyms #-}++module Language.C.Inline.Cpp.Exceptions+ ( CppException(..)+ , catchBlock+ ) where++import Control.Exception.Safe+import qualified Language.C.Inline as C+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Foreign+import Foreign.C++-- | An exception thrown in C++ code.+data CppException+ = CppStdException String+ | CppOtherException+ deriving (Eq, Ord, Show)++instance Exception CppException++-- NOTE: Other C++ exception types (std::runtime_error etc) could be distinguished like this in the future.+pattern ExTypeNoException :: CInt+pattern ExTypeNoException = 0++pattern ExTypeStdException :: CInt+pattern ExTypeStdException = 1++pattern ExTypeOtherException :: CInt+pattern ExTypeOtherException = 2++handleForeign :: (Ptr CInt -> Ptr CString -> IO ()) -> IO ()+handleForeign cont =+ alloca $ \exTypePtr ->+ alloca $ \msgPtrPtr -> do+ poke exTypePtr ExTypeNoException+ cont exTypePtr msgPtrPtr `finally` do+ exType <- peek exTypePtr+ case exType of+ ExTypeNoException -> return ()+ ExTypeStdException -> do+ msgPtr <- peek msgPtrPtr+ errMsg <- peekCString msgPtr+ free msgPtr+ throwM $ CppStdException errMsg+ ExTypeOtherException ->+ throwM CppOtherException+ _ -> error "Unexpected C++ exception type."++-- | Similar to `C.block`, but C++ exceptions will be caught and rethrown as `ForeignException`s.+-- Unlike `C.block`, the return type can only be @void@ (and doesn't need to be specified), but you can use `C.withPtr_` to extract a result yourself.+--+-- Using this will automatically include @exception@, @cstring@ and @cstdlib@.+catchBlock :: QuasiQuoter+catchBlock = QuasiQuoter+ { quoteExp = \blockStr -> do+ _ <- C.include "<exception>"+ _ <- C.include "<cstring>"+ _ <- C.include "<cstdlib>"+ typePtrVarName <- newName "exTypePtr"+ msgPtrVarName <- newName "msgPtr"+ let inlineCStr = unlines+ [ "void {"+ , " int* __inline_c_cpp_exception_type__ = $(int* " ++ nameBase typePtrVarName ++ ");"+ , " char** __inline_c_cpp_error_message__ = $(char** " ++ nameBase msgPtrVarName ++ ");"+ , " try {"+ , blockStr+ , " } catch (std::exception &e) {"+ , " *__inline_c_cpp_exception_type__ = " ++ show ExTypeStdException ++ ";"+ , " size_t whatLen = std::strlen(e.what()) + 1;"+ , " *__inline_c_cpp_error_message__ = static_cast<char*>(std::malloc(whatLen));"+ , " std::memcpy(*__inline_c_cpp_error_message__, e.what(), whatLen);"+ , " } catch (...) {"+ , " *__inline_c_cpp_exception_type__ = " ++ show ExTypeOtherException ++ ";"+ , " }"+ , "}"+ ]+ [e| handleForeign $ \ $(varP typePtrVarName) $(varP msgPtrVarName) -> $(quoteExp C.block inlineCStr) |]++ , quotePat = unsupported+ , quoteType = unsupported+ , quoteDec = unsupported+ } where+ unsupported _ = fail "Unsupported quasiquotation."
test/tests.hs view
@@ -1,15 +1,45 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE ScopedTypeVariables #-}++import Control.Exception.Safe+import Control.Monad import qualified Language.C.Inline.Cpp as C+import qualified Language.C.Inline.Cpp.Exceptions as C+import qualified Test.Hspec as Hspec C.context C.cppCtx C.include "<iostream>"+C.include "<stdexcept>" main :: IO ()-main = do- let x = 3- [C.block| void {- std::cout << "Hello, world!" << $(int x) << std::endl;- } |]+main = Hspec.hspec $ do+ Hspec.describe "Basic C++" $ do+ Hspec.it "Hello World" $ do+ let x = 3+ [C.block| void {+ std::cout << "Hello, world!" << $(int x) << std::endl;+ } |]++ Hspec.describe "Exception handling" $ do+ Hspec.it "std::exceptions are caught" $ do+ result <- try [C.catchBlock|+ throw std::runtime_error("C++ error message");+ |]++ result `Hspec.shouldBe` Left (C.CppStdException "C++ error message")++ Hspec.it "non-exceptions are caught" $ do+ result <- try [C.catchBlock|+ throw 0xDEADBEEF;+ |]++ result `Hspec.shouldBe` Left C.CppOtherException++ Hspec.it "code without exceptions works normally" $ do+ result :: Either C.CppException C.CInt <- try $ C.withPtr_ $ \resPtr -> [C.catchBlock|+ *$(int* resPtr) = 0xDEADBEEF;+ |]++ result `Hspec.shouldBe` Right 0xDEADBEEF