FFI Ptr question

Alastair Reid alastair@reid-hoffmann.net
Thu, 3 Jul 2003 11:42:30 +0100


paul@theV.net wrote:
> If a C function returns a (void*), let's say:
>
> data MyObj = MyObj
>
> foreign import ccall "static myobj_ffi.h new_obj" 
>    newMyObject :: IO (Ptr MyObj)
>
> ptr <- newMyObject
>
> should I free the ptr afterwards or not?

There's no single right answer to this - it all depends on the function.  
Here's three functions which return very different kinds of pointer.
(They're somewhat bogus in other ways but are representative of real, useful 
functions.)

Statically allocated - do not free

   T* newMyObject(void) { static T x; return &x; }

Allocated with malloc - use free to release it

   T* newMyObject(void) { return (T*)malloc(sizeof T); }

Reference counted - do not free

   T* newMyObject(void) { T* x = malloc(sizeof T); x->ref_count=1; return x; }

There's several further variants like allocating from a separate memory pool.

Basically, you have to carefully read the documentation of every function to 
find out how to release objects.  Since documentation is often poor, you 
might also study the implementation or uses and you should get in the habit 
of always checking error codes (even on functions like printf which most C 
programmers have never checked in their life) because it will help find those 
cases where the documentation is wrong or incomplete.

> If I have another imported C function:
>
> foreign import ccall "static myobj_ffi.h print_obj" 
>      printMyObject :: Ptr MyObj -> IO ()
>
> Can I safely assume the C function print_obj(void *) always get the
> same pointer even if I call (printMyObject ptr) multiple times?

If you have:

do
   printMyObject ptr
   ...
   printMyObject ptr
   ...
   printMyObject ptr

Then all three uses see the same pointer no matter what happens between the 
calls (assuming you dont use 'let ptr = ...' in the dots, of course).

To put it another way, anything of type 'Ptr a' is an abstract type as far as 
Haskell is concerned.  It is just a 32-bit object (or 64 or whatever) which 
Haskell doesn't directly understand.  All it can do with such a value is copy 
it, pass it to/from functions, store it, etc.  It certainly isn't going to 
try to treat it as a garbage collection root, or consider moving the object 
it points at.

--
Alastair Reid