| | 81 | === Turning a comparison function into a block literal === |
| | 82 | |
| | 83 | The 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 | {{{ |
| | 85 | type CmpFun a = Ptr (StablePtr a) -> Ptr (StablePtr a) -> IO Int |
| | 86 | }}} |
| | 87 | We use a foreign import wrapper to perform the actual marshalling of the Haskell function |
| | 88 | {{{ |
| | 89 | foreign import ccall "wrapper" mkCmpWrapper |
| | 90 | :: (Block (CmpFun a) -> CmpFun a) -> IO (FunPtr (Block (CmpFun a) -> CmpFun a)) |
| | 91 | }}} |
| | 92 | and pass that wrapper to the `mkBlock` function when creating a block: |
| | 93 | {{{ |
| | 94 | mkCmpBlock :: CmpFun a -> IO (Block (CmpFun a)) |
| | 95 | mkCmpBlock = mkBlock mkCmpWrapper |
| | 96 | }}} |
| | 97 | |
| | 98 | === Calling quicksort === |
| | 99 | |
| | 100 | With 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 | {{{ |
| | 102 | foreign import ccall "stdlib.h" qsort_b |
| | 103 | :: Ptr (StablePtr a) -> CSize -> CSize -> Block (CmpFun a) -> IO () |
| | 104 | }}} |
| | 105 | Then, we use `mkCmpBlock` to turn the Haskell comparison into a block literal that we pass as the last argument to `qsort_b`: |
| | 106 | {{{ |
| | 107 | do { -- 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 | }}} |
| | 131 | The complete code is in the attachment `QSortB_wrapper.hs`. |