{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-unused-matches #-} -- | @std::set@ module Hercules.CNix.Std.Set ( stdSetCtx, instanceStdSet, instanceStdSetCopyable, CStdSet, StdSet (StdSet), Hercules.CNix.Std.Set.new, size, toSet, fromList, fromListP, fromListFP, Hercules.CNix.Std.Set.toList, insert, insertP, insertFP, toListFP, ) where import Control.Exception (mask_) import Data.Coerce (Coercible, coerce) import Data.Foldable (for_) import qualified Data.Set as S import Data.Traversable (for) import qualified Data.Vector.Storable as VS import qualified Data.Vector.Storable.Mutable as VSM import Foreign ( ForeignPtr, FunPtr, Ptr, Storable, newForeignPtr, withForeignPtr, ) import Foreign.C (CSize) import Hercules.CNix.Encapsulation (HasEncapsulation (..)) import qualified Language.C.Inline as C import qualified Language.C.Inline.Cpp as C import qualified Language.C.Inline.Unsafe as CU import Language.Haskell.TH (DecsQ) import Language.Haskell.TH.Syntax (Dec, Q) import Prelude data CStdSet a stdSetCtx :: C.Context stdSetCtx = C.cppCtx `mappend` C.cppTypePairs [("std::set", [t|CStdSet|])] newtype StdSet a = StdSet (ForeignPtr (CStdSet a)) instance HasStdSet a => HasEncapsulation (CStdSet a) (StdSet a) where moveToForeignPtrWrapper = fmap StdSet . newForeignPtr cDelete class HasStdSet a where cNew :: IO (Ptr (CStdSet a)) cDelete :: FunPtr (Ptr (CStdSet a) -> IO ()) cSize :: Ptr (CStdSet a) -> IO CSize cInsertByPtr :: Ptr a -> Ptr (CStdSet a) -> IO () cCopies :: Ptr (CStdSet a) -> Ptr (Ptr a) -> IO () class HasStdSet a => HasStdSetCopyable a where cCopyTo :: Ptr (CStdSet a) -> Ptr a -> IO () cInsert :: a -> Ptr (CStdSet a) -> IO () -- | Helper for defining templated instances roll :: String -> Q [Dec] -> Q [Dec] roll cType d = concat <$> sequence [ C.include "", C.include "", C.substitute [ ("T", const cType), ("SET", \var -> "$(std::set<" ++ cType ++ ">* " ++ var ++ ")") ] d ] instanceStdSet :: String -> DecsQ instanceStdSet cType = roll cType [d| instance HasStdSet $(C.getHaskellType False cType) where cNew = [CU.exp| std::set<@T()>* { new std::set<@T()>() } |] cDelete = [C.funPtr| void deleteStdSet(std::set<@T()>* set) { delete set; } |] cSize set = [CU.exp| size_t { @SET(set)->size() } |] cInsertByPtr ptr set = [CU.exp| void { @SET(set)->insert(*$(@T() *ptr)) } |] cCopies set dstPtr = [CU.block| void { const std::set<@T()>& set = *@SET(set); @T()** aim = $(@T()** dstPtr); for (auto item : set) { *aim = new @T()(item); aim++; } }|] |] instanceStdSetCopyable :: String -> DecsQ instanceStdSetCopyable cType = roll cType [d| instance HasStdSetCopyable $(C.getHaskellType False cType) where cCopyTo set dstPtr = [CU.block| void { const std::set<@T()>* set = @SET(set); std::copy(set->begin(), set->end(), $(@T()* dstPtr)); } |] cInsert value set = [CU.exp| void { @SET(set)->insert($(@T() value)) } |] |] new :: forall a. HasStdSet a => IO (StdSet a) new = mask_ $ do moveToForeignPtrWrapper =<< cNew @a size :: HasStdSet a => StdSet a -> IO Int size (StdSet fptr) = fromIntegral <$> withForeignPtr fptr cSize fromList :: HasStdSetCopyable a => [a] -> IO (StdSet a) fromList as = do set <- new for_ as $ insert set pure set fromListP :: HasStdSet a => [Ptr a] -> IO (StdSet a) fromListP as = do set <- new for_ as $ insertP set pure set fromListFP :: (Coercible a' (ForeignPtr a), HasStdSet a) => [a'] -> IO (StdSet a) fromListFP as = do set <- new for_ as $ insertFP set pure set toSet :: (HasStdSetCopyable a, Storable a, Ord a) => StdSet a -> IO (S.Set a) toSet stdSet = do S.fromList . VS.toList <$> toVector stdSet toVector :: (HasStdSetCopyable a, Storable a) => StdSet a -> IO (VS.Vector a) toVector stdSet@(StdSet stdSetFPtr) = do vecSize <- size stdSet hsVec <- VSM.new vecSize withForeignPtr stdSetFPtr $ \stdSetPtr -> VSM.unsafeWith hsVec $ \hsVecPtr -> cCopyTo stdSetPtr hsVecPtr VS.unsafeFreeze hsVec toList :: (HasStdSetCopyable a, Storable a) => StdSet a -> IO [a] toList vec = VS.toList <$> toVector vec toVectorP :: (HasStdSet a) => StdSet a -> IO (VS.Vector (Ptr a)) toVectorP stdSet@(StdSet stdSetFPtr) = do vecSize <- size stdSet hsVec <- VSM.new vecSize withForeignPtr stdSetFPtr $ \stdSetPtr -> VSM.unsafeWith hsVec $ \hsVecPtr -> cCopies stdSetPtr hsVecPtr VS.unsafeFreeze hsVec toListP :: (HasStdSet a) => StdSet a -> IO [Ptr a] toListP vec = VS.toList <$> toVectorP vec toListFP :: (HasStdSet a, HasEncapsulation a b) => StdSet a -> IO [b] toListFP vec = mask_ $ do ptrs <- toListP vec for ptrs moveToForeignPtrWrapper insert :: HasStdSetCopyable a => StdSet a -> a -> IO () insert (StdSet fptr) value = withForeignPtr fptr (cInsert value) insertP :: HasStdSet a => StdSet a -> Ptr a -> IO () insertP (StdSet fptr) ptr = withForeignPtr fptr (cInsertByPtr ptr) insertFP :: (Coercible a' (ForeignPtr a), HasStdSet a) => StdSet a -> a' -> IO () insertFP (StdSet fptr) vfptr = withForeignPtr fptr $ \setPtr -> withForeignPtr (coerce vfptr) (\valPtr -> cInsertByPtr valPtr setPtr)