22

I'm pretty sure it's possible to send arrays through the FFI, but I can't find any examples. For instance, I have a Haskell array that I send to a int foo(int*) function, or I have a C array int bar[64]; that I send to Haskell.

Ideally I'd want the most efficient way - I don't want any heap allocation or unnecessary copying. Also, it would be nice if I could use Haskell's unboxed arrays in both Haskell and C. So what's the method of doing so?

Pubby
  • 51,882
  • 13
  • 139
  • 180

4 Answers4

17

If you use the Data.Vector library you could use Data.Vector.Storable for your needs. Then you can use functions such as unsafeToForeignPtr or unsafeWith to access the underlying foreign pointer. This allows you to call C-code without any copying or marshaling taking place.

If you want to create a vector from a C-array you can use unsafeFromForeignPtr.

For your examples you can use (assuming c_foo does not modify it's arguments)

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)

This can be be golfed to:

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

Note that if your C-function modifies the data, then you shouldn't do this, instead you should make a copy of the data to not break referential transparency.

If you want to use the standard Array-type you could use withStorableArray from Data.Array.Storable in the same way.

ehird
  • 40,602
  • 3
  • 180
  • 182
dnaq
  • 2,104
  • 1
  • 14
  • 24
  • Your golfed version doesn't use the `sv` parameter, I'm guessing you just accidentally omitted it (I tried to edit your answer but SO insisted my edit be ≥ 6 characters :( ) – Ben Millwood Mar 29 '12 at 10:18
  • 1
    @benmachine: Fixed that for you :) – ehird Mar 29 '12 at 13:45
11

The FFI specification is quite readable, so you might want to just sit down and work through the whole thing. However, for this specific question, you can jump to the "Marshalling" section, particularly the Ptr and Storable subsections, which outline what's available for this.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
4

To convert a FFI Ptr into a Haskell list, you can use:

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

Flexo
  • 87,323
  • 22
  • 191
  • 272
3

As in C, an array is basically a pointer to the array's first member. You get the other elements by doing arithmetic on the pointer. The Ptr is a member of Num, so you can use usual arithmetic operations.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • 6
    True, but Haskell pointer arithmetic works in *bytes*. So in C, where you would write `aPtr += 1` to move to the next element of an array, in Haskell you need to write `next = aPtr + 1*sizeOf element`. Or you can use `Foreign.Marshal.Array.advancePtr`. – John L Mar 25 '12 at 10:09
  • I don't see an instance of `Num` for `Ptr` when I import `Foreign` or `Foreign.Ptr`, where are you getting that from? – Ben Millwood Apr 06 '12 at 00:27