2

我有做以下事情的功能。

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

阅读完这些东西后首先想到的是:

-- 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

但是,我不知道这是有效的,或者它是否有效,如果它是最佳实践。

我对 Haskell 相当陌生,所以如果这篇文章有任何重大的愚蠢之处,我深表歉意。

4

1 回答 1

1

我认为您的代码不太正确。首先考虑 C 习惯用法中发生的情况:在堆栈上分配一个指向 CStructType 的指针,然后将指向该指针的指针作为参数传递给someFunctionThatAllocsFooAsOutput. 该函数负责分配一个新的CStructType(可能是通过malloc),并将生成的新指针存储到您在堆栈上创建的指针中。

someFunctionThatAllocsFooAsOutput要在 Haskell 中复制这一点,重要的一点是,您需要保留一个指向由分配的内存的指针并将其传递给 C 函数,直到您准备好释放它,而不是来回编组。所以你可能想做这样的事情(我没有尝试编译这个,但我认为它很接近):

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

有几点需要注意:

  1. 不需要使用ForeignPtrs,您可以保留一个 rawPtr CStructType并直接将其传递给函数。这会变得相当不方便,因为您需要手动释放它,这通常涉及顶级bracket.
  2. 即使someFunctionThatAllocsFooAsOutput涉及全局状态、指向其他 malloc 内存的指针或其他技巧,这种风格也是安全的。如果您对 有更多了解CStructType,则可能没有必要保留返回的确切内存,someFunctionThatAllocsFooAsOutput并且您可以更自由地将数据编组到 Haskell。但是这里没有足够的信息让我假设。

正如其他人所指出的FFI,Real World Haskell 的章节非常相关。我不认为它有一个使用输出参数的例子,这是一个不幸的遗漏。

于 2014-05-26T21:26:47.823 回答