wiki:BlockObjects

Version 15 (modified by chak, 3 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:

void
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

do
    -- 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);

callback_t 
get_callback (void);

assume the FFI declaration

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

We might use the imported C function as follows:

do
  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.)

Background