Hi everyone, I was working on a hobby project to get used to haskells FFI, I wanted to create a function that allows custom defined handlers for errors and for recieved responses, so I wrote the needed c code using c function pointers
foreign import ccall "ListenTcpsocketv4" cListenTcpIP4 :: Int -> CString -> Int -> Int -> Int -> (Int -> CString -> IO ()) -> (Int -> IO ()) -> IO Int
however I got into issues to to the functions passed as arguments not being marshelled, I tried making them of type FunPtr but then couldn’t pass a haskell function to this c call, any help?
Provide a mid-level wrapper function that marshals the FFI-level arguments into an actual call into foreign code. Note the use of newForeignPtr to clean up the wrapper FunPtr as well as other stuff we had to create:
Provide functions and types so that setup is ergonomic for the library user:
type EventHandler = TelnetPtr -> Event -> IO ()
data Event = Received ByteString | Send ByteString | -- and many others
-- basically a big case-match that packs CString into ByteString etc.
convertEventT :: FFI.EventT -> IO Event
convertEventHandler :: EventHandler -> F.TelnetEventHandlerT
convertEventHandler f telnetP eventP _ =
peek eventP >>= convertEventT >>= f telnetP
-- | Main init function for the library
telnetInit :: [OptionSpec] -> [Flag] -> EventHandler -> IO Telnet
telnetInit options flags handler =
FFI.telnetInit options' (convertEventHandler handler) flags
where
options' = map f options
f (OptionSpec opt us him) =
let us' = if us then iacWill else iacWont
him' = if him then iacDo else iacDont
in T.TelnetTeloptT (fromIntegral $ unOption opt) us' him'
Provide a typeclass to generalise over both Ptr TelnetT and ForeignPtr TelnetT (does something like this exist in base?):
-- | The pointer you get back from 'telnetInit' is a 'ForeignPtr'
-- because it carries around its finalizers, but the pointer that gets
-- passed into your 'EventHandler' is a bare 'Ptr' because it's being
-- passed in from C. This class lets us generalise across both types.
class HasTelnetPtr t where
withTelnetPtr :: t -> (TelnetPtr -> IO a) -> IO a
-- | Unwrap with 'withForeignPtr'.
instance HasTelnetPtr Telnet where
withTelnetPtr = withForeignPtr
-- | No unwrapping needed.
instance HasTelnetPtr TelnetPtr where
withTelnetPtr t f = f t
Provide wrappers over FFI functions that can use either from of the telnet_t pointer:
telnetRecv :: HasTelnetPtr t => t -> ByteString -> IO ()
telnetRecv t bs = withTelnetPtr t $ \telnetP -> FFI.telnetRecv telnetP bs
Sorry if this is a rather silly question but in this case is wrapper something given by the compiler? does wrapper need to be defined on its own? if so how would the wrapper function look?
The stub factory mkCallback turns any Haskell computation of type IO () into a C function pointer that can be passed to C routines, which can call back into the Haskell context by invoking the referenced function.