hdirect-0.21.0: comlib/EnumInterface.lhs
%
% (c) 1999, sof
%
Generic implementation of Com-style enumeration
interfaces - give it the list to enumerate & you're there!
\begin{code}
module EnumInterface
(
mkEnumInterface -- :: [a]
-- -> Int
-- -> (Ptr a -> a -> IO ())
-- -> IO (ComVTable iid objState)
) where
import Word
import Int
import ComServ
import ComException
import Com ( HRESULT, writeIUnknown, IUnknown )
import HDirect
import Foreign.Ptr
import IOExts
import Monad ( when )
type ThisPtr = Ptr (IUnknown ())
\end{code}
The state kept by each IEnum* instance:
\begin{code}
data EnumState a
= EnumState
{ elt :: IORef ([a], Int)
, origElts :: [a]
, writeElt :: (Ptr (Ptr a) -> a -> IO ())
, sizeof :: Int
}
\end{code}
The constructor doesn't have to do much, initialise the state
shared by the different methods and create a method table
containing them:
\begin{code}
mkEnumInterface :: [a]
-> Int
-> (Ptr (Ptr a) -> a -> IO ())
-> IO (ComVTable iid objState)
mkEnumInterface ls sizeof write = do
ref <- newIORef (ls, length ls)
let st = EnumState ref ls write sizeof
m_enumNext <- export_enumNext (enumNext st)
m_enumSkip <- export_enumSkip (enumSkip st)
m_enumReset <- export_enumReset (enumReset st)
m_enumClone <- export_enumClone (enumClone st)
createComVTable [m_enumNext, m_enumSkip, m_enumReset, m_enumClone]
\end{code}
An IEnum* interface allows you to iterate over a sequence *once*,
i.e., it doesn't wrap around (but you can rewind the enumeration
back to the beginning with IEnum::Reset()):
\begin{code}
enumNext :: EnumState a
-> ThisPtr
-> Word32
-> Ptr (Ptr a)
-> Ptr Word32
-> IO HRESULT
enumNext st this c pFetched pcFetched
| pcFetched == nullPtr && c /= 1 = return e_INVALIDARG
| pFetched == nullPtr = return e_POINTER
| otherwise = do
let ref = elt st
(elts, eltsLeft) <- readIORef ref
let
c_int = fromIntegral c
(hr, elts_to_fetch)
| c_int > eltsLeft = (s_FALSE, eltsLeft)
| otherwise = (s_OK, c_int)
elts_left = eltsLeft - elts_to_fetch
elts' <- fillIn pFetched elts_to_fetch elts
when (pcFetched /= nullPtr)
(writeWord32 pcFetched (fromIntegral elts_to_fetch))
writeIORef ref (elts', elts_left)
return hr
where
wr_marshall = writeElt st
fillIn ptr 0 ls = return ls
fillIn ptr n (x:xs) = do
wr_marshall ptr x
let ptr' = ptr `plusPtr` (fromIntegral (sizeof st))
fillIn ptr' (n-1) xs
foreign export stdcall dynamic
export_enumNext :: (ThisPtr -> Word32 -> Ptr a -> Ptr Word32 -> IO HRESULT) -> IO (Ptr ())
enumSkip :: EnumState a
-> ThisPtr
-> Word32
-> IO HRESULT
enumSkip st this c
| c == 0 = return e_INVALIDARG
| otherwise = do
(elts, eltsLeft) <- readIORef (elt st)
let
c_int = fromIntegral c
(hr, elts_to_fetch)
| c_int > eltsLeft = (s_FALSE, eltsLeft)
| otherwise = (s_OK, c_int)
elts_left = take elts_to_fetch elts
x = eltsLeft - elts_to_fetch
writeIORef (elt st) (elts_left, x)
return hr
foreign export stdcall dynamic
export_enumSkip :: (ThisPtr -> Word32 -> IO HRESULT) -> IO (Ptr ())
enumReset :: EnumState a
-> ThisPtr
-> IO HRESULT
enumReset st _ = do
let ls = origElts st
writeIORef (elt st) (ls, length ls)
return s_OK
foreign export stdcall dynamic
export_enumReset :: (ThisPtr -> IO HRESULT) -> IO (Ptr ())
enumClone :: EnumState a
-> ThisPtr
-> Ptr (Ptr (IUnknown b))
-> IO HRESULT
enumClone st this out = do
vtbl <- mkEnumInterface (origElts st) (sizeof st) (writeElt st)
ip <- cloneIPointer_prim this vtbl
writeIUnknown False out ip
return s_OK
foreign export stdcall dynamic
export_enumClone :: (ThisPtr -> Ptr (Ptr (IUnknown b)) -> IO HRESULT) -> IO (Ptr ())
\end{code}