packages feed

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 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