Changes between Version 3 and Version 4 of BlockObjects/FakingIt


Ignore:
Timestamp:
Aug 26, 2011 6:47:32 AM (4 years ago)
Author:
chak
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • BlockObjects/FakingIt

    v3 v4  
    99=== Block literals ===
    1010
    11 Blocks are by default allocated on the stack in C and only promoted to the heap using an explicity `Block_copy()` function.  In Haskell, we always allocate them on the heap, which is indicated by the pointer being placed in a block literals `isa` field:
     11Blocks are by default allocated on the stack in C and only promoted to the heap using an explicity `Block_copy()` function.  In Haskell, we always allocate them on the heap, which is indicated by the pointer constant that we place in every block literal's `isa` field:
    1212{{{
    1313foreign import ccall "& _NSConcreteGlobalBlock" nsConcreteGlobalBlock :: Ptr ()
     
    4040newtype Block a = Block (Ptr (Block a))
    4141}}}
    42 
    4342
    4443When turning a Haskell function into a C function pointer to be included in a block literal as the `invoke` function, we need to take care to add a pointer to the block literal itself as a new first argument:
     
    8079}}}
    8180
     81=== Turning a comparison function into a block literal ===
     82
     83The comparison function passed to `qsort_b`, gets pointers to the array elements it is to compare.  As these array elements are marshalled Haskell thunks, they are themselves '''stable''' pointers to the actual values that ought to be compared.
     84{{{
     85type CmpFun a = Ptr (StablePtr a) -> Ptr (StablePtr a) -> IO Int
     86}}}
     87We use a foreign import wrapper to perform the actual marshalling of the Haskell function
     88{{{
     89foreign import ccall "wrapper" mkCmpWrapper
     90  :: (Block (CmpFun a) -> CmpFun a) -> IO (FunPtr (Block (CmpFun a) -> CmpFun a))
     91}}}
     92and pass that wrapper to the `mkBlock` function when creating a block:
     93{{{
     94mkCmpBlock :: CmpFun a -> IO (Block (CmpFun a))
     95mkCmpBlock = mkBlock mkCmpWrapper
     96}}}
     97
     98=== Calling quicksort ===
     99
     100With these auxiliary definitions, the actual invocation of `qsort_b` is straight forward.  We import `qsort_b` with an explicit block argument for the comparison function:
     101{{{
     102foreign import ccall "stdlib.h" qsort_b
     103  :: Ptr (StablePtr a) -> CSize -> CSize -> Block (CmpFun a) -> IO ()
     104}}}
     105Then, we use `mkCmpBlock` to turn the Haskell comparison into a block literal that we pass as the last argument to `qsort_b`:
     106{{{
     107do {   -- convert a list of strings into a C array of stable pointers to those strings in the
     108       -- Haskell heap
     109   ; ptrs <- mapM newStablePtr myCharacters
     110   ; sortedPtrs <- withArray ptrs $ \myCharactersArray -> do
     111       {
     112           -- get the size in bytes of a stable pointer to a Haskell string
     113       ; let elemSize = fromIntegral $ sizeOf (undefined :: StablePtr String)
     114
     115           -- invoke C land 'qsort_b' with a Haskell comparison function passed as a block
     116           -- object; mutates 'myCharactersArray'
     117       ; cmpBlock <- mkCmpBlock $ \lPtr rPtr -> do
     118           { l <- deRefStablePtr =<< peek lPtr
     119           ; r <- deRefStablePtr =<< peek rPtr
     120           ; return $ fromOrdering (l `compare` r)
     121           }
     122       ; qsort_b myCharactersArray (genericLength myCharacters) elemSize cmpBlock
     123
     124       ; peekArray (length ptrs) myCharactersArray
     125       }
     126
     127      -- turn the array of Haskell strings back into a list of strings
     128   ; mySortedCharacters <- mapM deRefStablePtr sortedPtrs
     129   }
     130}}}
     131The complete code is in the attachment `QSortB_wrapper.hs`.
    82132
    83133'''TODO:'''