FFI中的可变数据和懒惰



简介

我正在用inline-c包装一个C数字库;一些函数可以通过回调传递到步骤例程,比如ODE的优化或时间集成。

特别是在本机C使用中,回调可以对连续数组进行操作,通过指针修改它们,并将它们返回到一些不透明(分布式)数据结构。

因此,这是一个可变的数据问题,我想在Haskell方面表示它:根据我的理解,在回调中,我们应该冻结数组,处理它,例如作为Data.Vector.Storable.Vector或使用repa,解冻结果,获取外部指针并将其传递回。

内部:newtype Vec = Vec (Ptr Vec) deriving Storableinline-c上下文中的相关条目表示指向不透明C数据结构的指针类型,vecGetArray/vecRestoreArray分别产生/请求指向连续内存的相同指针和请求/产生Vec

问:

我注意到,虽然返回的Vector是正确的,但当我使用生成的、修改过的Vec("副作用")时,从该函数返回后,它没有被修改。GHC不会补偿它(懒惰?)。我该如何让它重新计算它?Haskell中有什么惯用的方法来处理整个FFI中的可变数据?


*已修复*

见答案


谢谢!

import qualified Data.Vector.Storable as V
import qualified Data.Vector.Storable.Mutable as VM
withVecGetVectorM ::
   Vec ->                                               
   (V.Vector PetscScalar_ -> IO (V.Vector PetscScalar_)) ->   
   IO (V.Vector PetscScalar_)                           
 withVecGetVectorM v f = do 
   p <- vecGetArrayPtr v
   pf <- newForeignPtr_ p
   vImm <- V.freeze (VM.unsafeFromForeignPtr0 pf len)
   vImmOut <- f vImm        
   vMutOut <- V.thaw vImmOut
   let (fpOut, _, _) = VM.unsafeToForeignPtr vMutOut
       pOut = unsafeForeignPtrToPtr fpOut
   vecRestoreArrayPtr v pOut
   return vImmOut
where len = vecSize v

Vec.hs:

vecGetArrayPtr :: Vec -> IO (Ptr PetscScalar_)
vecGetArrayPtr v = chk1 (vecGetArrayPtr' v)
vecRestoreArrayPtr :: Vec -> Ptr PetscScalar_ -> IO ()
vecRestoreArrayPtr v ar = chk0 (vecRestoreArrayPtr' v ar)

InlineC.hs

-- PETSC_EXTERN PetscErrorCode VecGetArray(Vec,PetscScalar**);
vecGetArrayPtr' :: Vec -> IO (Ptr PetscScalar_, CInt)
vecGetArrayPtr' v = withPtr $ p -> vga v p where
  vga v p = [C.exp|int{VecGetArray($(Vec v), $(PetscScalar** p))}|]

-- PETSC_EXTERN PetscErrorCode VecRestoreArray(Vec,PetscScalar**);
vecRestoreArrayPtr' :: Vec -> Ptr PetscScalar_ -> IO CInt
vecRestoreArrayPtr' v c = with c $ pc -> vra v pc
  where
    vra w pc = [C.exp|int{VecRestoreArray($(Vec w), $(PetscScalar** pc))}|]

此外,IIUC,该代码制作了载体的两个额外副本,一个在冷冻时,一个解冻时,但我怀疑这是低效的。有人能提出改进或简化的建议吗?

已修复:

我犯了一个小错误,该处理哪些指针;这是解决办法。这个函数和bug函数有相同的签名,这表明必须有一种类型化的方式来表示我们正在修改哪个指针。如果有人对此有任何建议,请随时联系。

vecRestoreVector :: Vec -> V.Vector PetscScalar_ -> IO ()
vecRestoreVector v w = do
  p <- vecGetArrayPtr v
  pf <- newForeignPtr_ p
  V.copy (VM.unsafeFromForeignPtr0 pf len) w
  vecRestoreArrayPtr v p
    where
     len = vecSize v

最新更新