我的功能可以完成以下任务。
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来说相当新,所以如果这篇帖子中有任何重大愚蠢,我会道歉。
答案 0 :(得分:1)
我认为您的代码不太正确。首先考虑C语言中发生的事情:在栈上分配一个指向CStructType的指针,然后将指针作为参数传递给someFunctionThatAllocsFooAsOutput
。该函数负责分配新的CStructType
(可能通过malloc
),并且生成的新指针存储在您在堆栈上创建的指针中。
要在Haskell中复制它,重要的一点是,不是来回编组,而是需要保留指向由someFunctionThatAllocsFooAsOutput
分配的内存的指针,并将其传递给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
有几点需要注意:
ForeignPtr
s,您可以保留原始Ptr CStructType
并直接将其传递给函数。这会变得相当不方便,因为您需要手动释放它,这通常涉及顶级bracket
。someFunctionThatAllocsFooAsOutput
涉及全局状态,指向其他malloc内存的指针或其他特技,此样式也是安全的。如果您对CStructType
有更多了解,可能没有必要保留someFunctionThatAllocsFooAsOutput
返回的确切内存,您可以更自由地将数据封送到Haskell。但是我没有足够的信息来假设这一点。正如其他人所指出的那样,真实世界Haskell的FFI
章节非常重要。我不认为它有一个使用输出参数的例子,这是一个遗憾的遗漏。