[Haskell-cafe] Suggestions for #defines with FFI

Malcolm Wallace Malcolm.Wallace at cs.york.ac.uk
Thu Jul 28 09:02:41 EDT 2005


John Goerzen <jgoerzen at complete.org> writes:

> On Wed, Jul 27, 2005 at 10:52:41PM +0200, Tomasz Zielonka wrote:
> > On Wed, Jul 27, 2005 at 01:28:38PM -0500, John Goerzen wrote:
> > > I'm writing a Haskell interface to a library that #defines about 100
> > > constants corresponding to numeric exit codes.  It also defines hundreds
> > > of contants, over the same numbering ranges, for other purposes.  The
> > > exit codes are non-contiguous as well.
> > > 
> > > So my question is: is there a nice way I can represent this in Haskell,
> > > preferably without having to key in all 100 constants?
> > 
> > Did you consider hsc2hs?
> 
> Yes.  That's what I'm using now.  It does pull in the values for the
> consts, but that's about it.  I still have to key everything in
> manually.

I don't know whether this will be a helpful suggestion or not,
because it still requires to key in all constants, but nhc98 uses the
following configuration technique to grab the "errno.h" constants into
an enumerated Haskell datatype, with instances of Eq, Ord, and Enum.

There is a little haskell program, GenerateErrNo.hs, which is run
to generate a C program, which is then run to generate the Haskell
module I require.  The source code is attached.  Maybe you can adapt
it for your purposes.

Another suggestion for an FFI tool specifically designed for this
kind of problem is c2hs:
    http://www.cse.unsw.edu.au/~chak/haskell/c2hs/

Regards,
    Malcolm
-------------- next part --------------

main = do
  putStr cprogHeader
  mapM_ (putStr . cppify) possibleErrNos
  putStr cprogFooter

cprogHeader =
  "#include <errno.h>\n" ++
  "main() {\n" ++
  "  int i, j=0, max=0, noncontig=0, dup=0;\n" ++
  "  char *errs[2000];\n" ++
  "  char *dups[20];\n" ++
  "  int   dupi[20];\n" ++
  "  char  sep;\n" ++
  "  for (i=0; i<2000; i++) {\n" ++
  "    errs[i]=(char*)0;\n" ++
  "  }\n" ++
  "  for (i=0; i<20; i++) {\n" ++
  "    dups[i]=(char*)0;\n" ++
  "    dupi[i]=0;\n" ++
  "  }\n"

cppify symbol =
  "#ifdef "++symbol++"\n" ++
  "  if (errs["++symbol++"]) {\n" ++
  "    dups[dup]=\""++symbol++"\"; dupi[dup]="++symbol++"; dup++;\n" ++
  "  } else\n" ++
  "    errs["++symbol++"] = \""++symbol++"\";\n" ++
  "  if ("++symbol++">max) max="++symbol++";\n" ++
  "#endif\n"

cprogFooter =
  "  printf(\"module NHC.DErrNo where\\n\\n\");\n" ++
  "  printf(\"{- Automatically generated from /usr/include/errno.h -}\\n\\n\");\n" ++
  "  printf(\"data ErrNo =\\n\");\n" ++
  "  printf(\"    Edummy\\n\");\n" ++
  "  for (i=1; i<=max; i++) {\n" ++
  "    if (errs[i]) {\n" ++
  "      printf(\"  | %s\\n\",errs[i]);\n" ++
  "    } else if (j<dup) {\n" ++
  "      errs[i] = dups[j++];\n" ++
  "      printf(\"  | %s\\n\",errs[i]);\n" ++
  "    } else { noncontig=1; }\n" ++
  "  }\n" ++
  "  for (;j<dup;j++) {\n" ++
  "      errs[++max] = dups[j];\n" ++
  "      printf(\"  | %s\\n\",errs[max]);\n" ++
  "  }\n" ++
  "  if (1/*noncontig*/) {\n" ++
  "    printf(\"  deriving (Eq,Show)\\n\\n\");\n" ++
  "    printf(\"instance Enum ErrNo where\\n\");\n" ++
  "    for (i=1; i<=max; i++) {\n" ++
  "      if (errs[i])\n" ++
  "        printf(\"  toEnum %d = %s\\n\",i,errs[i]);\n" ++
  "    }\n" ++
  "    printf(\"  toEnum _ = Edummy\\n\");\n" ++
  "    for (i=1; i<=max; i++) {\n" ++
  "      if (errs[i])\n" ++
  "        printf(\"  fromEnum %s = %d\\n\",errs[i],i);\n" ++
  "    }\n" ++
  "    printf(\"\\n\");\n" ++
  "  } else printf(\"  deriving (Eq,Enum,Show)\\n\\n\");\n" ++
  "  printf(\"eqErrNo :: ErrNo -> ErrNo -> Bool\\n\");\n" ++
  "  for (j=0;j<dup;j++) {\n" ++
  "    printf(\"eqErrNo %s %s = True\\n\",dups[j],errs[dupi[j]]);\n" ++
  "    printf(\"eqErrNo %s %s = True\\n\",errs[dupi[j]],dups[j]);\n" ++
  "  }\n" ++
  "  printf(\"eqErrNo a b = a==b\\n\\n\");\n" ++
  ioErrors "alreadyexists" ["EEXIST", "EISDIR"] ++
  ioErrors "doesnotexist"  ["ENOENT", "ESRCH", "ENXIO", "ENODEV"] ++
  ioErrors "alreadyinuse"  ["EBUSY",  "ETXTBSY"] ++
  ioErrors "full"          ["ENOSPC", "EDQUOT"] ++
  ioErrors "illegalop"     ["EPERM",  "ESPIPE"] ++
  ioErrors "nopermission"  ["EPERM",  "EACCES", "EROFS"] ++
  "  printf(\"\\n\\n\");\n" ++
  "  exit(0);\n" ++
  "}\n"

ioErrors name symbols =
  "  sep = ' ';\n" ++
  "  printf(\""++name++" = [\");\n" ++
  concatMap (\symbol->
      "#ifdef "++symbol++"\n" ++
      "  printf(\"%c "++symbol++"\",sep);\n" ++
      "  sep = ',';\n" ++
      "#endif\n") symbols ++
  "  printf(\" ]\\n\");\n"

possibleErrNos =
-- beginning of linux symbols
  [ "EPERM"
  , "ENOENT"
  , "ESRCH"
  , "EINTR"
  , "EIO"
  , "ENXIO"
  , "E2BIG"
  , "ENOEXEC"
  , "EBADF"
  , "ECHILD"
  , "EAGAIN"
  , "ENOMEM"
  , "EACCES"
  , "EFAULT"
  , "ENOTBLK"
  , "EBUSY"
  , "EEXIST"
  , "EXDEV"
  , "ENODEV"
  , "ENOTDIR"
  , "EISDIR"
  , "EINVAL"
  , "ENFILE"
  , "EMFILE"
  , "ENOTTY"
  , "ETXTBSY"
  , "EFBIG"
  , "ENOSPC"
  , "ESPIPE"
  , "EROFS"
  , "EMLINK"
  , "EPIPE"
  , "EDOM"
  , "ERANGE"
  , "EDEADLK"
  , "ENAMETOOLONG"
  , "ENOLCK"
  , "ENOSYS"
  , "ENOTEMPTY"
  , "ELOOP"
  , "EWOULDBLOCK"
  , "ENOMSG"
  , "EIDRM"
  , "ECHRNG"
  , "EL2NSYNC"
  , "EL3HLT"
  , "EL3RST"
  , "ELNRNG"
  , "EUNATCH"
  , "ENOCSI"
  , "EL2HLT"
  , "EBADE"
  , "EBADR"
  , "EXFULL"
  , "ENOANO"
  , "EBADRQC"
  , "EBADSLT"
  , "EDEADLOCK"
  , "EBFONT"
  , "ENOSTR"
  , "ENODATA"
  , "ETIME"
  , "ENOSR"
  , "ENONET"
  , "ENOPKG"
  , "EREMOTE"
  , "ENOLINK"
  , "EADV"
  , "ESRMNT"
  , "ECOMM"
  , "EPROTO"
  , "EMULTIHOP"
  , "EDOTDOT"
  , "EBADMSG"
  , "EOVERFLOW"
  , "ENOTUNIQ"
  , "EBADFD"
  , "EREMCHG"
  , "ELIBACC"
  , "ELIBBAD"
  , "ELIBSCN"
  , "ELIBMAX"
  , "ELIBEXEC"
  , "EILSEQ"
  , "ERESTART"
  , "ESTRPIPE"
  , "EUSERS"
  , "ENOTSOCK"
  , "EDESTADDRREQ"
  , "EMSGSIZE"
  , "EPROTOTYPE"
  , "ENOPROTOOPT"
  , "EPROTONOSUPPORT"
  , "ESOCKTNOSUPPORT"
  , "EOPNOTSUPP"
  , "EPFNOSUPPORT"
  , "EAFNOSUPPORT"
  , "EADDRINUSE"
  , "EADDRNOTAVAIL"
  , "ENETDOWN"
  , "ENETUNREACH"
  , "ENETRESET"
  , "ECONNABORTED"
  , "ECONNRESET"
  , "ENOBUFS"
  , "EISCONN"
  , "ENOTCONN"
  , "ESHUTDOWN"
  , "ETOOMANYREFS"
  , "ETIMEDOUT"
  , "ECONNREFUSED"
  , "EHOSTDOWN"
  , "EHOSTUNREACH"
  , "EALREADY"
  , "EINPROGRESS"
  , "ESTALE"
  , "EUCLEAN"
  , "ENOTNAM"
  , "ENAVAIL"
  , "EISNAM"
  , "EREMOTEIO"
  , "EDQUOT"
  , "ENOMEDIUM"
  , "EMEDIATYPE"
-- end of linux symbols
-- beginning of SGI symbols
  , "EIORESID"
  , "EINIT"
  , "EREMDEV"
  , "ECANCELED"
  , "ENFSREMOTE"
  , "ETCP_EBASE"
  , "ETCP_ELIMIT"
  , "ENAMI_EBASE"
  , "ENAMI_ELIMIT"
  , "ENFS_EBASE"
  , "ENFS_ELIMIT"
  , "ELASTERRNO"
  , "ECKPT"
  , "ENOLIMFILE"
  , "EDISJOINT"
  , "ENOLOGIN"
  , "ELOGINLIM"
  , "EGROUPLOOP"
  , "ENOATTACH"
  , "ENOATTR"
  , "EFSCORRUPTED"
  , "EDIRCORRUPTED"
  , "EWRONGFS"
  , "ECONTROLLER"
  , "ENOTCONTROLLER"
  , "EENQUEUED"
  , "ENOTENQUEUED"
  , "EJOINED"
  , "ENOTJOINED"
  , "ENOPROC"
  , "EMUSTRUN"
  , "ENOTSTOPPED"
  , "ECLOCKCPU"
  , "EINVALSTATE"
  , "ENOEXIST"
  , "EENDOFMINOR"
  , "EBUFSIZE"
  , "EEMPTY"
  , "ENOINTRGROUP"
  , "EINVALMODE"
  , "ECANTEXTENT"
  , "EINVALTIME"
  , "EDESTROYED"
  , "EBDHDL"
  , "EDELAY"
  , "ENOBWD"
  , "EBADRSPEC"
  , "EBADTSPEC"
  , "EBADFILT"
  , "EMIGRATED"
  , "EMIGRATING"
  , "ECELLDOWN"
-- end of SGI symbols
-- beginning of Sun symbols
  , "ENOTSUP"
  , "EPROCLIM"
  , "ERREMOTE"
-- end of Sun symbols
-- beginning of HP symbols
  , "ENOSYM"
  , "EREFUSED"
  , "EREMOTERELEASE"
-- end of HP symbols
  ]


More information about the Haskell-Cafe mailing list