views:

276

answers:

2

In a Haskell program, what's the best way to use constants defined in C headers?

+4  A: 

For this task, hsc2hs is your friend.

For a simple example, let's get the value of INT_MAX from limits.h.

$ cat >IntMax.hsc
module Main where

#include <limits.h>

c_INT_MAX = #const INT_MAX

main = print c_INT_MAX

With hsc2hs, we can #include headers and use the values of constants with the #const directive.

Instead of building by hand, use Cabal:

$ cat >intmax.cabal
Name:          intmax
Version:       0.0
Cabal-Version: >=1.2
Build-Type:    Simple

Executable intmax
  Main-Is: IntMax.hs
  Build-Depends: base

Notice that even though the name of the main program is IntMax.hsc, the Main-Is line points to IntMax.hs. When Cabal looks for IntMax.hs but finds IntMax.hsc, it automatically feeds the latter through hsc2hs as part of the build.

$ cabal configure
Resolving dependencies...
Configuring intmax-0.0...

$ cabal build
Prerocessing executables for intmax-0.0...
Building intmax-0.0...
[1 of 1] Compiling Main             ( dist\build\intmax\intmax-tmp\IntMax.hs, dist\build\intmax\intmax-tmp\Main.o )
Linking dist\build\intmax\intmax.exe ...

$ ./dist/build/intmax/intmax
2147483647

Note that you'll want to break up lines with multiple constants. Say you're assembling a bitfield to pass to FormatMessage. You'll want to write it as

flags = #const FORMAT_MESSAGE_FROM_SYSTEM
        .|.
        #const FORMAT_MESSAGE_IGNORE_INSERTS

Putting them all on one line will result in syntax errors.

Greg Bacon
+3  A: 

GHC is moving away from -fvia-c and towards -fasm wherever possible.

One side effect is that your program may be compiled without using any C headers at all, even in -fvia-c mode, in order to ensure that the compilation results are functionally identical to GHC in -fasm mode.

Thus it is necessary to use hsc2hs, c2hs, or other preprocessors run before GHC compiles sources.

c2hs natively supports enum constants... it's been a while, but I think something like this is right.

#c
enum Foo = { Bar, Baz };
void something(enum Foo foo) {}
#endc

{#enum Foo#}

somethingBar = {#call pure something#} (cFromEnum Bar)

#define'd constants are a tick trickier. I've always just copied them inline, or used additional C to transform then into enums or const variables.

ephemient