haskellfficabalhsc2hs

Haskell: How do I get the values of #define-d constants?


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


Solution

  • 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.