Может ли FFI иметь дело с массивами?Если так, то как? - PullRequest
21 голосов
/ 24 марта 2012

Я почти уверен, что можно отправлять массивы через FFI, но я не могу найти никаких примеров. Например, у меня есть массив Haskell, который я отправляю в функцию int foo(int*), или у меня есть массив C int bar[64];, который я отправляю в Haskell.

В идеале я бы хотел наиболее эффективный способ - я не хочу никакого выделения кучи или ненужного копирования. Кроме того, было бы неплохо, если бы я мог использовать распакованные массивы Haskell как в Haskell, так и в C. Итак, каков способ сделать это?

Ответы [ 4 ]

17 голосов
/ 25 марта 2012

Если вы используете библиотеку Data.Vector, вы можете использовать Data.Vector.Storable для своих нужд. Затем вы можете использовать такие функции, как unsafeToForeignPtr или unsafeWith, для доступа к базовому внешнему указателю. Это позволяет вам вызывать C-код без какого-либо копирования или маршалинга.

Если вы хотите создать вектор из массива C, вы можете использовать unsafeFromForeignPtr.

Для ваших примеров вы можете использовать (при условии, что c_foo не изменяет свои аргументы)

import Foreign.Ptr
import Foreign.C.Types
import System.IO.Unsafe (unsafePerformIO)
import qualified Data.Vector.Storable as SV

foreign import ccall unsafe "foo" c_foo :: Ptr CInt -> CInt

haskellFoo :: SV.Vector CInt -> CInt
haskellFoo sv = unsafePerformIO $
    SV.unsafeWith sv $ \ptr -> return (c_foo ptr)

Это может быть в гольф до:

haskellFoo sv = unsafePerformIO $
    SV.unsafeWith sv (return . c_foo)

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

Если вы хотите использовать стандартный тип массива, вы можете использовать withStorableArray из Data.Array.Storable таким же образом.

11 голосов
/ 25 марта 2012

Спецификация FFI вполне читабельна, так что вы можете просто сесть и поработать над всем этим. Тем не менее, для этого конкретного вопроса вы можете перейти к разделу «Маршаллинг», в частности к подразделам Ptr и Storable , в которых указано, что доступно для этого.

4 голосов
/ 23 июня 2013

Чтобы преобразовать FFI Ptr в список Haskell, вы можете использовать:

peekArray0 :: (Storable a, Eq a) => a -> Ptr a -> IO [a]

http://hackage.haskell.org/packages/archive/base/4.2.0.1/doc/html/Foreign-Marshal-Array.html#v%3ApeekArray0

3 голосов
/ 25 марта 2012

Как и в C, массив в основном является указателем на первый член массива. Вы получаете другие элементы, выполняя арифметику с указателем. Ptr является членом Num, поэтому вы можете использовать обычные арифметические операции.

...