您正在寻找
default method signatures
:
{-# LANGUAGE DefaultSignatures, TypeFamilyDependencies #-}
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Storable
class ManagedValue a where
type ManagedPtr a = (r :: *) | r -> a
type ManagedPtr a = Ptr a
withManaged :: a -> (ManagedPtr a -> IO b) -> IO b
default withManaged :: (Storable a, ManagedPtr a ~ Ptr a) => a -> (ManagedPtr a -> IO b) -> IO b
withManaged v f = do
fp <- mallocForeignPtr
withForeignPtr fp $ \p -> do
poke p v
f p
getManaged :: ManagedPtr a -> IO a
default getManaged :: (Storable a, ManagedPtr a ~ Ptr a) => ManagedPtr a -> IO a
getManaged p = peek p
castManagedToPtr :: ManagedPtr a -> Ptr b
default castManagedToPtr :: ManagedPtr a ~ Ptr a => ManagedPtr a -> Ptr b
castManagedToPtr = castPtr
castPtrToManaged :: Ptr b -> ManagedPtr a
default castPtrToManaged :: ManagedPtr a ~ Ptr a => Ptr b -> ManagedPtr a
castPtrToManaged = castPtr
你还是得写
instance ManagedValue Int
,
instance ManagedValue Double
,等等,但您不必在这些实例中实现任何内容。
还有一个选项不需要您手动编写
instance
s代表的是
Storable
,但它也有自己的一些注意事项:
{-# LANGUAGE TypeFamilies #-}
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Storable
class ManagedPtr r where
type ManagedValue r
withManaged :: ManagedValue r -> (r -> IO b) -> IO b
getManaged :: r -> IO (ManagedValue r)
castManagedToPtr :: r -> Ptr b
castPtrToManaged :: Ptr b -> r
instance Storable a => ManagedPtr (Ptr a) where
type ManagedValue (Ptr a) = a
withManaged v f = do
fp <- mallocForeignPtr
withForeignPtr fp $ \p -> do
poke p v
f p
getManaged p = peek p
castManagedToPtr = castPtr
castPtrToManaged = castPtr
我基本上把typeclass从内向外翻转,把typeclass放在指针类型上,并让值成为一个关联的类型。主要的警告是,值类型与指针类型之间不再存在依赖关系,因此当您实际使用此类型类时,可能会有大量不明确的类型需要解决
TypeApplications
或者什么的。第二个警告是
ManagedPtr
只能是
Ptr
现在如果基础价值是
可存储
.