我目前正在试验Haskell的C-> Haskell(C2HS)接口生成器.在第一次,这真是太棒了,我extern C
在几个小时内连接了一个相当复杂的C++库(使用一个小型的包装器).(之前我从未做过任何FFI.)
只有一个问题:如何释放在C/C++库中分配的内存?我{#pointer ... foreign #}
在C2HS文档中找到了,它看起来与我追求的完全一样.由于我的C-wrapper将C++库转换为具有参考透明度和功能接口的库,Haskell Storage Manager应该能够为我做好工作:-).不幸的是,我无法让这个工作.为了更好地解释我的问题,我在GitHub上设置了一个小的演示项目,它具有与C/C++库+包装器相同的属性,但没有开销.如您所见,该库与pure unsafe
FFI 一起使用是完全安全的.
在GitHub上,我创建了一个小型演示项目,其组织如下:
C库非常简单和无用:你可以传递一个整数,你可以[0..n]
从库中获取尽可能多的整数(当前).记住:图书馆没用,只是一个演示.界面也很简单:函数LTIData lti_new_data(int n)
将(在传递一个整数之后)返回一些包含C库分配数据的不透明对象.图书馆还拥有两个存取函数int lti_element_count(LTIData data)
和int lti_get_element(LTIData data, int n)
,前者将返回元素的数量,而后者将返回元素n
.嗯,最后但并非最不重要的,该库的用户应该使用它后释放不透明的LTIData
使用void lti_free_data(LTIData data)
.
使用C2HS设置低级Haskell绑定,您可以在其中找到它
为了好玩,我还使用低级API绑定和使用高级API的简单驱动程序设置了一种高级Haskell API.使用驱动程序和例如valgrind,可以很容易地看到泄漏的内存(对于库执行分配的每个参数;可以轻松观察如下):p_1, p_2, ..., p_n
\sum_{i = 1..n} 1 + p_i
$ valgrind dist/build/TestHsLTI/TestHsLTI 100 2>&1 | grep -e allocs -e frees
==22647== total heap usage: 184 allocs, 74 frees, 148,119 bytes allocated
$ valgrind dist/build/TestHsLTI/TestHsLTI 100 100 2>&1 | grep -e allocs -e frees
==22651== total heap usage: 292 allocs, 80 frees, 181,799 bytes allocated
$ valgrind dist/build/TestHsLTI/TestHsLTI 100 100 100 2>&1 | grep -e allocs -e frees
==22655== total heap usage: 400 allocs, 86 frees, 215,479 bytes allocated
$ valgrind dist/build/TestHsLTI/TestHsLTI 100 100 100 100 2>&1 | grep -e allocs -e frees
==22659== total heap usage: 508 allocs, 92 frees, 249,159 bytes allocated
Run Code Online (Sandbox Code Playgroud)
您只需键入即可克隆,编译和运行项目git clone https://github.com/weissi/c2hs-experiments.git && cd c2hs-experiments && cabal configure && cabal build && dist/build/TestHsLTI/TestHsLTI
问题是项目只使用Foreign.Ptr
而不是Foreign.ForeignPtr
使用C2HS的"托管"版本{#pointer ... foreign #}
,我无法使用它.在演示项目中,我还添加了一个.chs
文件,试图使用这些外部指针,但它不起作用:-(.我努力尝试但我没有任何成功.
还有一件事我也不明白:如何告诉GHC使用C2HS如何释放图书馆的数据.演示项目的库提供了一个void lti_free_data(LTIData data)
应该被调用来释放内存的函数.但是GHC猜不出来了!?!如果GHC使用常规a free()
,则不会释放所有内存:-(.
问题已解决:我发现这个文件在互联网上做了类似的事情并且能够解决它:-)。
它所需要的只是一些样板编组代码:
foreign import ccall "lib_to_interface.h <i_free_data"
ltiFreeDataPtr :: FunPtr (Ptr (LTIDataHs) -> IO ())
newObjectHandle :: Ptr LTIDataHs -> IO LTIDataHs
newObjectHandle p = do
fp <- newForeignPtr ltiFreeDataPtr p
return $ LTIDataHs fp
Run Code Online (Sandbox Code Playgroud)
这是该文件的最终托管 ( ForeignPtr
) 版本.chs
。
归档时间: |
|
查看次数: |
458 次 |
最近记录: |