[Haskell-cafe] FFI for a beginner

63 views
Skip to first unread message

Andrew Pennebaker

unread,
Apr 8, 2011, 6:32:22 AM4/8/11
to Haskell Cafe
ncurses is proving too difficult to setup, so I'm working on a new library called charm. The C code works by itself, but I can't compile a Haskell wrapper for it. While the tutorials at HaskellWiki are helpful, they're outdated. Argh! The docs say that -#include pragmas no longer work, but fail to explain how to load code without them. Suffice to say I have no recourse but trial and error.

GitHub: charm and hscharm

$ make
cp /usr/include/charm.c .
ghc --make -fforce-recomp -o hellocharm hellocharm.hs charm.hs charm.c -I/usr/include -dylib-install-name /usr/lib/libcharm.dynlib
[1 of 2] Compiling Charm            ( charm.hs, charm.o )
[2 of 2] Compiling Main             ( hellocharm.hs, hellocharm.o )
Linking hellocharm ...
ld: duplicate symbol _Charm_getWidth_info in charm.o and charm.o
collect2: ld returned 1 exit status
make: *** [hellocharm] Error 1

Cheers,

Andrew Pennebaker

Jason Dagit

unread,
Apr 9, 2011, 9:50:31 PM4/9/11
to Andrew Pennebaker, Haskell Cafe

I don't know how to make ghc load them without using either hsc2hs or c2hs. I've had better experiences with hsc, but your mileage may vary.

_______________________________________________
Haskell-Cafe mailing list
Haskel...@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Andrew Pennebaker

unread,
Apr 12, 2011, 5:55:15 PM4/12/11
to Jason Dagit, Haskell Cafe
hsc2hs and c2hs are good suggestions, and some of the tutorials I'm following use them.

But 1) Many Haskell FFI tutorials don't require them, so they only seem to help, or only help in older versions of GHC.

And 2) When I did compile using c2hs, it just produced the same file, but with filler comments like {#- LINE 13 #-}. And they still refused to compile for the same reason:

ld: duplicate symbol _Charm_getWidth_info in charm.o and charm.o

Cheers,

Andrew Pennebaker

Andrew Pennebaker

unread,
Jul 14, 2011, 2:57:25 AM7/14/11
to Jason Dagit, Haskell Cafe
FIXED IT!!!

The problem with charm.hs and its FFI dependency charm.c is that both want to produce an intermediary charm.o file.

Solution: rename charm.hs to hscharm.hs.

Cheers,

Andrew Pennebaker

Simon Peyton-Jones

unread,
Jul 14, 2011, 3:31:16 AM7/14/11
to Andrew Pennebaker, Jason Dagit, Haskell Cafe

FIXED IT!!!  The problem with charm.hs and its FFI dependency charm.c is that both want to produce an intermediary charm.o file. Solution: rename charm.hs to hscharm.hs.

 

Would you (or anyone else) care to update the tutorials on the Haskell Wiki?  As someone wrote below:

While the tutorials at HaskellWiki are helpful, they're outdated. Argh!

 

It would be great if someone was public-spirited enough to fix this. The root page is here

                http://haskell.org/haskellwiki/GHC/Using_the_FFI

which in turn links to the “complete examples” page mentioned above.

 

The docs say that -#include pragmas no longer work, but fail to explain how to load code without them. Suffice to say I have no recourse but trial and error.”

 

Ah, now that is a GHC documentation question.  Can you tell us what should the docs should say instead?  Then we can fix the docs.

 

Simon

Yves Parès

unread,
Jul 14, 2011, 4:18:19 AM7/14/11
to Andrew Pennebaker, Haskell Cafe
I think you should definitely use hsc2hs.
It is simple to you use and would allow you to replace things like:
peek ptr = do
    a <- peekByteOff ptr 0
    b <- peekByteOff ptr 4
    return (MyStructType a b)

By:

#include "MyStruct.h"
-- ^ Needs to be defined in a separate header

peek ptr = do
a <- (#peek MyStruct, foo) ptr
b <- (#peek MyStruct, bar) ptr
return (MyStructType a b)
But I just know hsc2hs. Does c2hs automatically writes the Storable instances ? (Because it's kind of a daunting task...)


2011/4/12 Andrew Pennebaker <andrew.p...@gmail.com>

Donn Cave

unread,
Jul 14, 2011, 1:04:25 PM7/14/11
to Haskell Cafe
>> The docs <http://www.haskell.org/ghc/docs/latest/html/users_guide/ffi-ghc.html#glasgow-foreign-headers>

>> say that -#include pragmas no longer work, but fail to explain how to
>> load code without them. Suffice to say I have no recourse but trial
>> and error."
>
> Ah, now that is a GHC documentation question. Can you tell us what
> should the docs should say instead? Then we can fix the docs.

- I believe I would say (agreeing with M Parès) that hsc2hs serves this
purpose - put "#include <termios.h>" in the .hsc file and use hsc2hs
features like #const and (#peek x, y). The termios example could
cover a lot more ground by operating on the termios struct and calling
tcsetattr directly from Haskell, using hsc2hs # macros (I append an
example implementation), but you'd also want to explain that hsc2hs
is only needed for these # macros, not for FFI in general.

But hsc2hs as of GHC 7.0.3 generates a "{-# INCLUDE" pragma, which
of course is cause for complaint from ghc. So there might be more
to be fixed, beyond just the documentation. That line probably has
no actual purpose other than to annoy ghc, and as a workaround it
can simply be removed from the .hs file.

- My impression is that Greencard isn't the best way to get started
with FFI and hasn't been for a decade or so, so it should probably
be purged from docs.

- The "root" page needs some attention from someone who understands
what "safe" and "unsafe" mean. Currently (under "Improving efficiency")
it cites the conventional interpretation that a function must be "safe"
if it calls back into the runtime, but neglects to mention that it
must also be "safe" if it is not to block execution of other threads,
including bound OS threads - so related to efficiency issues you
might want to make any slow I/O operation "safe" to avoid this.
There may be other points about "unsafe" that need to be documented.

Donn
--------

{-# LANGUAGE ForeignFunctionInterface #-}
module TTY (clearICANON) where
import Data.Word (Word32)
import Foreign
import Foreign.C

#include <termios.h>

type TCFlag = (#type tcflag_t)
type Speed = (#type speed_t)

data Termios = Termios {
termios_c_iflag :: TCFlag
, termios_c_oflag :: TCFlag
, termios_c_cflag :: TCFlag
, termios_c_lflag :: TCFlag
, termios_c_cc :: [CChar]
, termios_c_ispeed :: Speed
, termios_c_ospeed :: Speed
}
deriving Show

instance Storable Termios where
sizeOf _ = #size struct termios
alignment _ = alignment (undefined::CDouble)
peek a = do
iflag <- (#peek struct termios, c_iflag) a
oflag <- (#peek struct termios, c_oflag) a
cflag <- (#peek struct termios, c_cflag) a
lflag <- (#peek struct termios, c_lflag) a
cc <- peekArray 20 ((#ptr struct termios, c_cc) a)
ispeed <- (#peek struct termios, c_ispeed) a
ospeed <- (#peek struct termios, c_ospeed) a
return (Termios iflag oflag cflag lflag cc ispeed ospeed)
poke a (Termios iflag oflag cflag lflag cc ispeed ospeed) = do
(#poke struct termios, c_iflag) a iflag
(#poke struct termios, c_oflag) a oflag
(#poke struct termios, c_cflag) a cflag
(#poke struct termios, c_lflag) a lflag
pokeArray ((#ptr struct termios, c_cc) a) (take 20 (cc ++ repeat 0))
(#poke struct termios, c_ispeed) a ispeed
(#poke struct termios, c_ospeed) a ospeed

foreign import ccall "tcgetattr" tcgetattr
:: CInt -> Ptr Termios -> IO CInt
foreign import ccall "tcsetattr" tcsetattr
:: CInt -> CInt -> Ptr Termios -> IO CInt

setLFlag :: TCFlag -> Termios -> Termios
setLFlag c a = a { termios_c_lflag = ((termios_c_lflag a) .|. c) }
clearLFlag :: TCFlag -> Termios -> Termios
clearLFlag c a = a { termios_c_lflag = ((termios_c_lflag a) .&. (complement c)) }

setTTYAttr :: CInt -> (Termios -> Termios) -> IO ()
setTTYAttr fd fn = alloca $ \ termiosp -> do
status <- tcgetattr fd termiosp
if status == 0
then do
termios <- peek termiosp
poke termiosp (fn termios)
status <- tcsetattr fd (#const TCSAFLUSH) termiosp
if status == 0
then return ()
else throwErrno "tcgetattr"
else throwErrno "tcgetattr"

clearICANON = setTTYAttr 0 (clearLFlag (#const ICANON))

Reply all
Reply to author
Forward
0 new messages