Search code examples
haskellffiout-parameters

Using out parameters of a C Function in Haskell


I have functions that do the following sort of thing.

CStructType* foo;
int result = someFunctionThatAllocsFooAsOutput(&foo);

The first thing that comes to mind after reading around about this stuff is:

-- Standard binding done elsewhere
data CStructType = CStructType {
  -- Stuff here
} deriving (Eq, Show)
foreign import ccall "some_c_header.h someFunctionThatAllocsFooAsOutput" someFunction :: CStructType Ptr -> IO CInt

-- Call function binding from somewhere
main :: IO ()
main = do
  result <- alloca $ \cstruct -> someFunction cstruct
  doOtherThings cstruct result

However, I don't know of this is valid, or if it's valid, if it's the best practice.

I'm reasonably new to Haskell, so I apologize if there's any major stupidity in this post.


Solution

  • I think your code is not quite correct. First consider what happens in the C idiom: you allocate a pointer to CStructType on the stack, then pass a pointer to that pointer as the argument to someFunctionThatAllocsFooAsOutput. That function is responsible for allocating a new CStructType (probably via malloc), and the resulting new pointer is stored into the pointer you've created on the stack.

    To replicate this in Haskell, the important point is that rather than marshaling back and forth, you need to retain a pointer to the memory allocated by someFunctionThatAllocsFooAsOutput and pass that in to C functions until you're ready to free it. So you will probably want to do something like this (I haven't tried to compile this, but I think it's close):

    newtype CStructPtr = CStructPtr (ForeignPtr CStructType)
    
    foreign import ccall "some_c_header.h someFunctionThatAllocsFooAsOutput" someFunction :: Ptr (Ptr CStructType) -> IO CInt
    
    -- we need a hook into the function to free a `CStructType`
    foreign import ccall "some_c_header.h &freeFoo" freeFoo :: FunPtr (Ptr CStructType -> IO ())
    
    allocFoo :: IO (CStructPtr, CInt)
    allocFoo = alloca $ \ptrptr -> do
        result <- someFunction ptrptr
        cstructptr <- peek ptrptr
        fPtr <- newForeignPtr freeFoo cstructptr
        return (CStructPtr fPtr, result)
    
    -- a useful unwrapper
    withCStruct :: CStructPtr -> (Ptr CStructType -> IO a) -> IO a
    withCStruct (CStructPtr fPtr) action = withForeignPtr fPtr action
    
    doOtherThings :: CStructPtr -> CInt -> IO ()
    doOtherThings ptr result = withCStruct ptr $ \cptr -> otherCFunction cptr result
    
    
    main = do
        (cstruct,result) <- allocFoo
        doOtherThings cstruct result
    

    A couple points to note:

    1. It's not necessary to use ForeignPtrs, you can just keep a raw Ptr CStructType and pass it into function directly. This gets rather inconvenient though because you'll need to manually free it, which usually involves a top-level bracket.
    2. This style is safe even when someFunctionThatAllocsFooAsOutput involves global state, pointers to other malloc'd memory, or other trickiness. If you know more about CStructType, it might not be necessary to retain the exact memory returned by someFunctionThatAllocsFooAsOutput and you could more freely marshal the data to Haskell. But there's not enough information here for me to assume that.

    As others have pointed out, the FFI chapter of Real World Haskell is quite relevant. I don't think it has an example of using output params though, which is an unfortunate omission.