Может ли FFI работать с массивами? Если да, то как?
Я уверен, что можно отправлять массивы через FFI, но я не могу найти примеров. Например, у меня есть массив Haskell, который я отправляю в функцию int foo(int*)
, или у меня есть массив C int bar[64];
, который я отправляю в Haskell.
В идеале я бы хотел наиболее эффективный способ - я не хочу, чтобы было выделено кучу или ненужное копирование. Кроме того, было бы неплохо, если бы я мог использовать Haskell unboxed массивы как в Haskell, так и в C. Так какой же способ?
Ответы
Ответ 1
Если вы используете библиотеку 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
таким же образом.
Ответ 2
Спецификация FFI вполне читаема, поэтому вы можете просто сесть и проработать все это. Однако для этого конкретного вопроса вы можете перейти в раздел "Маршаллинг", в частности, Ptr и Сохраняемые подразделы, в которых описывается, что доступно для этого.
Ответ 3
Чтобы преобразовать 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
Ответ 4
Как и в C, массив в основном является указателем на первый элемент массива. Вы получаете другие элементы, делая арифметику на указателе. Ptr
является членом Num
, поэтому вы можете использовать обычные арифметические операции.