Vector (Vector Foo) -> (Ptr (Ptr Foo) -> IO a) -> IO a? - PullRequest
6 голосов
/ 29 июня 2011

Я делаю простую оболочку для библиотеки c, которой нужно передать список векторов. Требуется массив указателей на массивы. Чтобы сделать хороший интерфейс, я бы хотел иметь Vector (или список) Vector, но я не могу понять, как это сделать в идиоматическом haskell. (Или любым другим способом, кроме как переписывать вещи вокруг).

Я ищу что-то вроде

Vector (Vector Foo) -> (Ptr (Ptr Foo) -> IO a) -> IO a  

Ответы [ 2 ]

4 голосов
/ 29 июня 2011

Поскольку вы переходите к функции C, вы должны использовать Data.Vector.Storable. Вы не можете просто передать вектор векторов Storable, потому что это будет не просто вектор указателей на массивы, а также информация о размере и смещении.

если аргумент функции C, скажем, int myCFunc(foo** arrays, int sz), тогда должен работать следующий код:

import Data.Vector.Storable
import Foreign.Storable
import Foreign.ForeignPtr

withCFunction :: Storable a =>         -- ^ Storable so compatible with C
                Vector (Vector a)      -- ^ vector of vectors
              -> (Ptr (Ptr a) -> IO b) -- ^ C function wrapped by FFI
              -> IO b
withCFunction v f = do
  vs <- mapVectorM (\x -> let (fp,_,_) = unsafeToForeignPtr x 
                          in unsafeForeignPtrToPtr fp) v
  mapVectorM_ (\x -> let (tfp,_,_) = unsafeToForeignPtr x
                     in touchForeignPtr tfp) vs
  let (vfp,_,_) =  unsafeToForeignPtr vs
  withForeignPtr vfp $ \p -> f p
2 голосов
/ 29 июня 2011

Редактировать: hCsound не имеет дело с этим конкретным случаем, поэтому я добавил полный пример ниже.

Возможно, вы захотите посмотреть на мой пакет hCsound (darcs repo ), который имеет дело с очень похожим случаем.

Обратите внимание, что очень важно, чтобы библиотека C не изменяла массивы, используемые Data.Vector.Storable.Vector,Если вам нужно изменить данные, вы должны сначала скопировать старые данные, изменить массив через ffi и, наконец, обернуть указатели в новый вектор.

Вот код.Как было отмечено в комментарии, Data.Vector.Storable.Vector не имеет самого экземпляра Storable, поэтому внешний вектор должен быть Data.Vector.Vector.

import Foreign.Storable
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.Marshal.Array

import qualified Data.Vector as V
import qualified Data.Vector.Storable as S
import Data.Vector.Storable.Internal

withPtrArray v f = do
  let vs = V.map S.unsafeToForeignPtr v   -- (ForeignPtr, Offset, Length)
  ptrV = V.toList $ V.map (\(fp,off,_) -> offsetToPtr fp off) vs
  res <- withArray ptrV f
  V.mapM_ (\(fp,_,_) -> touchForeignPtr fp) vs
  return res

Обратите внимание, что массив выделяетсяwithArray, поэтому он автоматически gc'd после того, как функция вернется.

Эти массивы не заканчиваются нулем, поэтому вам нужно убедиться, что длина передана функции C некоторым другимМетод.

withForeignPtr не используется.Вместо этого вызывается touchForeignPtr, чтобы гарантировать, что ForeignPtr не был освобожден до завершения функции C.Чтобы использовать withForeignPtr, вам нужно вложить вызовы для каждого внутреннего вектора.Вот что делает функция nest в коде hCsound.Это гораздо сложнее, чем просто позвонить touchForeignPtr.

...