General Information
I'm currently experimenting with the C->Haskell (C2HS) Interface Generator for Haskell. At the first glace, it was just awesome, I interfaced a rather complicated C++ library (using a small extern C
-wrapper) in just a couple of hours. (And I never did any FFI before.)
There was just one problem: How to get the memory allocated in the C/C++ library freed? I found {#pointer ... foreign #}
in the C2HS documentation and that looks exactly like what I'm after. Since my C-wrapper turns the C++ library into a library with referential transparency with a functional interface, the Haskell Storage Manager should be able to do the hard work for me :-). Unfortunately, I was not able to get this working. To better explain my problem, I set up a small demo project on GitHub that has the same properties as the C/C++ library+wrapper but hasn't the overhead. As you can see, the library is completely safe to use with pure unsafe
FFI.
Demo Project
On GitHub, I created a small demo project organized as follows:
C library
The C library is very simple and useless: You can pass it an integer and you can get as many integers (currently [0..n]
) back from the library. Remember: The library is useless, just a demo. The interface is quite simple, too: The function LTIData lti_new_data(int n)
will (after passing a integer) return you some kind of opaque object containing allocated data of the C library. The library has also two accessor functions int lti_element_count(LTIData data)
and int lti_get_element(LTIData data, int n)
, the former will return the number of elements and the latter will return you element n
. Ah, and last but not least, the user of the library should after using it free the opaque LTIData
using void lti_free_data(LTIData data)
.
Low-level Haskell Binding
The low-level Haskell Binding is set up using C2HS, you can find it in
High-level Haskell API
For fun I also set up kind of a high-level Haskell API using the low-level API binding and a simple driver program that uses the high-level API. Using the driver program and e.g. valgrind one is easily able to see the leaked memory (for every parameter p_1, p_2, ..., p_n
the library does \sum_{i = 1..n} 1 + p_i
allocations; easily observable as below):
$ 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
Current State of the Demo
You should be able to clone, compile and run the project by simply typing git clone https://github.com/weissi/c2hs-experiments.git && cd c2hs-experiments && cabal configure && cabal build && dist/build/TestHsLTI/TestHsLTI
So what's the Problem again?
The problem is that the project only uses Foreign.Ptr
and not the "managed" version Foreign.ForeignPtr
using C2HS's {#pointer ... foreign #}
and I can't get it to work. In the demo project I also added a .chs
file trying to use these foreign pointers but it does not work :-(. I tried it very hard but I didn't have any success.
And there is one thing I don't understand, too: How to tell GHC using C2HS how to free the library's data. The demo project's library provides a function void lti_free_data(LTIData data)
that should get called to free the memory. But GHC can't guess that!?! If GHC uses regular a free()
, not all of the memory will get freed :-(.