Version 15 (modified by chak, 6 years ago) (diff)


FFI Support for C Block Objects

Apple recently proposed the inclusion of lambda abstractions (closures) into C/C++/Objective-C and facilitated an implementation in the clang compiler framework. They called this language extension blocks (or block objects). It is widely used in the APIs of OS X 10.6 (Snow Leopard) and 10.7 (Lion). This page is about extending the Haskell 2010 FFI to directly support blocks — i.e., to enable Haskell functions to be marshalled as blocks to C and to enable C blocks to be marshalled as Haskell functions to Haskell land. This extension will be enabled by the language option BlockObjects.

Example: passing a Haskell functions as an argument

As an example, consider the library function qsort_b:

qsort_b(void *base, size_t nel, size_t width, int (^compar)(const void *, const void *));

In C, we might use this function as described in Apple's introduction to blocks: Using a Block Directly. We would like to be able to do the same in Haskell by declaring:

foreign import ccall qsort_b "stdlib.h" :: Ptr a -> CSize -> CSize -> (Ptr a -> Ptr a -> Int) -> IO ()

myCharacters = ["TomJohn", "George", "Charles Condomine"]

and then executing

    -- convert a list of strings into a C array of stable pointers to those strings in the Haskell heap
  myCharactersArray <- newArray $ mapM newStablePtr myCharacters
   -- get the size in bytes of a stable pointer to a Haskell string
  let elemSize = fromInteger $ sizeof (undefined :: StablePtr String)

    -- invoke C land 'qsort_b' with a Haskell comparison function passed as a block object; mutates 'myCharactersArray'
  qsort_b myCharactersArray (length myCharacters) elemSize (\l r -> fromOrdering (l `compare` r))

    -- turn the array of Haskell strings back into a list of strings
  mySortedCharacters <- mapM deRefStablePtr myCharactersArray

Here we compare entire strings and not just the first characters as in the C implementation. The marshalling function fromOrdering is defined as follows:

fromOrdering :: Ordering -> Int
fromOrdering LT = -1
fromOrdering EQ = 0
fromOrdering GT = 1

Example: returning a C block

Conversely, a C block object can be used as a function in Haskell. Given the following C prototype

typedef void (^callback_t)(int);

get_callback (void);

assume the FFI declaration

foreign import ccall get_callback :: IO (CInt -> IO ())

We might use the imported C function as follows:

  callback <- get_callback
  callback 42

TODO Is there a better example? Something from an official API?

Storage management

TODO How do we recover a Haskell function's storage once the function has been turned into a block object and passed to a C function? (NB: the environment of the function may hold on to large data structures, which will only be freed once the function is freed.)

When we marshal a C block object into a Haskell function, we need to ensure that the Haskell storage manager releases the block object (with Block_release()) once the Haskell land function becomes unreachable in the Haskell heap.

The gory details

The following subpages provide details on implementing this functionality. (The following three subpages are still stubs.)