In a Haskell program, what's the best way to use constants defined in C headers?
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.
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.