我目前正在试验Haskell的C-> Haskell(C2HS)接口生成器。在第一个glace,它真棒,我在几个小时内连接了一个相当复杂的C ++库(使用一个小的extern C
- 包装器)。 (我之前从未做过任何FFI。)
只有一个问题:如何释放在C / C ++库中分配的内存?我在C2HS documentation中找到{#pointer ... foreign #}
,看起来就像我追求的那样。由于我的C-wrapper将C ++库转换为具有参考透明度和功能接口的库,Haskell Storage Manager应该能够为我做好工作:-)。不幸的是,我无法让这个工作。为了更好地解释我的问题,我设置了一个小的demo project on GitHub,它具有与C / C ++库+包装器相同的属性,但没有开销。如您所见,该库与pure unsafe
FFI一起使用是完全安全的。
在GitHub上,我创建了一个small demo project,其组织如下:
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的high-level Haskell API设置了simple driver program种。使用驱动程序和例如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
只需输入git clone https://github.com/weissi/c2hs-experiments.git && cd c2hs-experiments && cabal configure && cabal build && dist/build/TestHsLTI/TestHsLTI
问题是该项目仅使用Foreign.Ptr
而不是使用C2HS的Foreign.ForeignPtr
的“托管”版本{#pointer ... foreign #}
,我无法使其工作。在演示项目中,我还添加了一个.chs
file trying to use these foreign pointers,但它不起作用:-(。我努力尝试但是没有任何成功。
还有一件事我也不明白:如何告诉GHC使用C2HS如何释放图书馆的数据。演示项目的库提供了一个函数void lti_free_data(LTIData data)
,应该调用该函数来释放内存。但是GHC猜不出来了!?!如果GHC使用常规free()
,则不会释放所有内存: - (。
答案 0 :(得分:3)
问题解决了:我在互联网上找到this file doing something similar并且能够解决它: - )。
它需要的是一些样板编组码:
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