Changes between Version 3 and Version 4 of BlockObjects/FakingIt


Ignore:
Timestamp:
Aug 26, 2011 6:47:32 AM (3 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:'''