Haskell中Storable的使用

2018-06-18  本文已影响12人  DarkBubble

Haskell支持指针和堆内存的分配和使用,主要使用Foreign模块里面的功能。要使用Haskell的指针类型(主要是Ptr a)需要满足(Storable a)。在GHCI中查看class Storable接口的定义:

Prelude> import Foreign
Prelude Foreign> :i Storable 
class Storable a where
  sizeOf :: a -> Int
  alignment :: a -> Int
  peekElemOff :: Ptr a -> Int -> IO a
  pokeElemOff :: Ptr a -> Int -> a -> IO ()
  peekByteOff :: Ptr b -> Int -> IO a
  pokeByteOff :: Ptr b -> Int -> a -> IO ()
  peek :: Ptr a -> IO a
  poke :: Ptr a -> a -> IO ()
  {-# MINIMAL sizeOf,
              alignment,
              (peek | peekElemOff | peekByteOff),
              (poke | pokeElemOff | pokeByteOff) #-}
    -- Defined in ‘Foreign.Storable’

我们假设要实现一个代数数据类型的Storable Instance,参考下面的例子:

  1 import Foreign
  2 
  3 data MyData = MyData Int Double deriving(Eq, Show, Read)
  4 
  5 instance Storable MyData where
  6     sizeOf _ = 16
  7     alignment _ = 8
  8     peek ptr = let ptr_x = castPtr ptr :: Ptr Int
  9                    ptr_y = castPtr (ptr_x `advancePtr` 1) :: Ptr Double
 10                in  do x <- peek ptr_x
 11                       y <- peek ptr_y
 12                       return $ MyData x y
 13     poke ptr d = let ptr_x = castPtr ptr :: Ptr Int
 14                      ptr_y = castPtr (ptr_x `advancePtr` 1) :: Ptr Double
 15                      MyData x y = d
 16                  in  do poke ptr_x x
 17                         poke ptr_y y

对于一个带参类型,实现Storable Instance还需要注意下面的问题:

  1. 自定义ADT中,要求所有被存储在堆内存的成员数据类型也是Storable Instance
  2. 自定义ADT中,sizeOfalignment两个接口尽量使用_来作为输入参数,而不要对ADT数据进行任意部分的求值,否则可能引起malloc接口的错误,因为malloc接口会使用undefined作为构造ADT的参数,一旦求值即会报错
  3. 要实现上述要求必须开启ScopedTypeVariable语法扩展,即作用域类型变量,也就是作为参数类型a可以用于undefined :: a
    例如:
{-# LANGUAGE ScopedTypeVariable #-}

data Vector a = Vector a a a deriving(Eq, Show, Read)

instance Storable a => Storable (Vector a) where
    sizeOf _ = sizeOf (undefined :: a) * 3 -- Scoped type variable 'a'
    alignment _ = sizeOf (undefined :: a) -- Scoped type variable 'a'
    {- ... -}
上一篇 下一篇

猜你喜欢

热点阅读