I'm wrapping a C numerical library with inline-c
; some functions can be passed callbacks to step routines, think optimization or time integration of ODEs.
In particular in the native C use the callbacks can operate on contiguous arrays, modify them via a pointer, and return them to some opaque (distributed) datastructure.
So it's a mutable data problem, and I'd like to represent it on the Haskell side: in my understanding, in the callback we should freeze the array, work on it e.g. as a Data.Vector.Storable.Vector
or with repa
, thaw the result, get the foreign pointer and pass it back.
The internals: newtype Vec = Vec (Ptr Vec) deriving Storable
, and the associated entry in an inline-c
Context, represents the type of a pointer to an opaque C data structure and vecGetArray
/vecRestoreArray
produce/request the same pointer to contiguous memory and request/produce a Vec
, respectively.
I noticed that, while the returned Vector
is correct, when I use the resulting, modified Vec
(the "side effect"), after returning from this function, it is not modified. GHC doesn't recompute it (laziness?). How do I make it recompute it? What idiomatic way is there in Haskell to work with mutable data across the FFI?
see answer
Thanks!
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))}|]
Moreover, IIUC, the code makes 2 additional copies of the vector, one at the freeze and one at thaw. , but I suspect it's inefficient. Can someone suggest improvements or simplifications?
答案 0 :(得分:0)
我犯了一个微不足道的错误。哪个指针要处理;这是修复。这个函数与错误的函数具有相同的签名,这表明我必须有一种有意义的方式来表示我们正在修改哪个指针。如果有人对此有任何建议,请随时与我们联系。
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