CPPMiscellaneous
With {-# LANGUAGE CPP #-}
enabled, .hs
files will be processed with C preprocessor before the code is compiled. It's often useful if:
- you need backwards compatibility with an older version of GHC or some library, or you want to avoid compiler warnings
- you are writing code that depends on OS or CPU architecture
- you want to enable some parts of code depending on a flag
- you don't want to bother with Template Haskell and need some easy way to generate code (e.g. repetitive instances)
However, see the Stop (ab)using CPP in Haskell sources post for a discussion of some drawbacks of CPP (and alternatives to it).
Running CPP
To dump CPP output, do ghc -E file.hs
(it'll be saved into file.hspp
). There might be a lot of empty lines in the file, so don't forget to scroll.
To just run CPP on a file without using GHC, do either of these:
gcc -E -traditional - < file.hs
(on Linux)clang -E -traditional - < file.hs
(on macOS)cpphs file.hs
(for cpphs)
Since Clang is used instead of GCC on macOS, and Clang's preprocessor syntax is slightly different, you should use this command to see whether your code would compile under Clang:
$ ghc -pgmP "clang -E -undef -traditional" file.hs
CPP features
Defining macros
#define DOUBLE_QUOTE 34
#define POW(a,b) a ^ (b :: Int)
Once a constant/macro is defined, it'll be substituted everywhere in code (except when it's a part of a string or a word). For instance, POW(2,3)
will be replaced with 2 ^ (3 :: Int)
.
Using constants as flags
#ifdef FOO /* or #ifndef to check that a constant is *not* defined */
...
#endif
Code inside #ifdef ... #endif
will be removed unless FOO
is defined (which can be done via .cabal or with a #define
earlier in the file).
Conditionals
#if __GLASGOW_HASKELL__ < 710
...
#endif
#ifdef FOO
is actually a shortcut for #if defined(FOO)
; with #if
you can branch on an arbitrary boolean expression (see the GCC manual for full syntax of conditionals). Also, you can have more than two branches with #elif
. Note that #define
and other CPP directives can be used inside of #if
– it's useful when you're writing your own macros.
See the “Standard CPP macros” section in the GHC manual for a full list of macros defined by GHC.
Tricks
Commas
If you need to use a comma in a macro argument, just define a separate macro for the comma:
#define COMMA ,
GEN(a COMMA b COMMA c)
Concatenation
If CPP doesn't understand that a macro should be expanded in some particular place (e.g. if it's a part of a word, or has _
in front of it, etc), you can use /**/
:
#define U(x) xbar
U(foo) <- expanded into “xbar”
#define U(x) x/**/bar
U(foo) <- expanded into “foobar” on GCC
or “foo bar” on Clang
Note that if you don't want a space there, cpphs is your only bet.
Workarounds
There are three main reasons why code with CPP might not work as expected:
-
You used
#
,##
or__VA_ARGS__
. GHC runs CPP in traditional mode, which disables all advanced features. -
OS differences – Linux uses GCC and macOS uses Clang, which are slightly incompatible. Either read Differences between GCC and Clang, or consider using cpphs if you don't want to bother.
-
You used something that CPP doesn't support well, like:
-
#-}
on a separate line without space in front of it. -
Single or double quotes – CPP tries to handle them smartly and fails.
-
Multiline strings (e.g.
"foo\
and\bar"
on two separate lines). -
\
at the end of line (e.g.infix 5 \\
) – to fix it, add a comment after\
.
-
Cabal automatically generates macros (MIN_VERSION_<library>
) for all libraries your project depends upon. E.g. you can do something if e.g. bytestring
is 0.10.4 or newer:
#if MIN_VERSION_bytestring(0,10,4)
...
#endif
If a library has hyphens -
in its name, replace them with underscores _
:
#if MIN_VERSION_template_haskell(2,11,0)
...
#endif
GHC versions currently in use: 7.8 = 708
, 7.10 = 710
, 8.0 = 800
.
Do something if GHC is older than 7.10:
#if __GLASGOW_HASKELL__ < 710
...
#endif
Use different code for GHC 8.0 and older versions:
#if __GLASGOW_HASKELL__ >= 800
...
#else
...
#endif
If you need to check for patch level as well (e.g. detect GHC 8.0.1 but not 8.0.2), you can use a .cabal flag:
library
...
if impl(ghc == 8.0.1)
cpp-options: -DGHC_801
For more info on .cabal flags, see Define a .cabal flag.
Let's say you want to add asserts to your code. First, add a flag to your .cabal
file:
Flag asserts
Description: Turn on asserts
Default: False
library
exposed-modules: ...
if flag(asserts)
cpp-options: -DASSERTS
(It's also possible to define a constant by writing -DFOO=bar
.)
Now, to enable a flag during compilation, do this (to disable, use -asserts
instead of asserts
):
- Cabal:
cabal configure -f asserts
- Stack:
stack build --flag pkgname:asserts
Then you can check for this flag in code:
#ifdef ASSERTS
...
#endif
Macros are useful when you need to write repetitive code but don't want to use Template Haskell (either because it's hard or because you want to avoid the compilation time penalty). For example, monad-control uses macros to generate instances, and GHC uses macros to define shorter function names.
The simplest example is defining a synonym for a function:
#define FI fromIntegral
Now FI
can be used anywhere instead of fromIntegral
. As mentioned in the introduction, it won't be expanded inside strings or words; still, it might be better to undefine it after use:
#undef FI
We can define a more complex macro with parameters. For instance, instead of generating lenses with makeLenses
we could do it with macros (sometimes it's a necessary evil, like when you want to define lenses for data families):
#define MAKE_LENS(field, lens) lens f s = (\y -> s {field = y}) <$> f (field s)
-- | Lens for foo.
foo :: Lens' (SomeFamily Int) Foo
MAKE_LENS(foo, _foo)
(Note that we can't get rid of repetition – foo
, _foo
– without breaking compatibility with Clang. If you use cpphs or don't need your code to compile on macOS, you can use /**/
to add the underscore instead.)
Finally, you can use \
for longer macros:
#define BASE(M) \
instance MonadBaseControl (M) (M) where { \
type StM (M) a = a; \
liftBaseWith f = f id; \
restoreM = return; \
{-# INLINABLE liftBaseWith #-}; \
{-# INLINABLE restoreM #-}}
This macro isn't actually multiline, though – it will be expanded into a single line (which is why curly braces were used and there's ;
after every line).
The following variables are defined depending on OS:
mingw32_HOST_OS
– Windowsdarwin_HOST_OS
– macOSghcjs_HOST_OS
– Javascript (when compiling with GHCJS)linux_HOST_OS
– Linux (shouldn't be needed most of the time)freebsd_HOST_OS
– FreeBSDnetbsd_HOST_OS
– NetBSDopenbsd_HOST_OS
– OpenBSDsolaris_HOST_OS
– Solaris
For instance, here's how you can detect macOS:
#ifdef darwin_HOST_OS
...
#endif
Note that despite lots of libraries using #if defined(mingw32_HOST_OS) || defined(__MINGW32__)
for detecting Windows, you don't need to do it – just mingw32_HOST_OS
will suffice. See this Trac ticket.
There are variables for detecting architecture:
i386_HOST_ARCH
– x86x86_64_HOST_ARCH
– x64arm_HOST_ARCH
– ARM (there's alsoarm_HOST_ARCH_PRE_ARMv7
)powerpc_HOST_ARCH
sparc_HOST_ARCH
#if defined(i386_HOST_ARCH)
...
#elif defined(x86_64_HOST_ARCH)
...
#else
# error Unsupported architecture
#endif
# error Unsupported architecture
will emit an error at the CPP step (and stop compilation) if the architecture isn't one of the above.
Sometimes you want to hide some code from Hlint (for instance, if Hlint's parser doesn't support a particular language feature yet).
You can do it by defining HLINT
(e.g. by writing a script like lens does, or just by passing --cpp-define=HLINT
to Hlint) and then wrapping code into an #ifndef HLINT
section:
#ifndef HLINT
...
#endif
You can use CPP to find out whether Int
and Word
have 32 bits, 64 bits, or (if you're unlucky) some other amount of bits. First you'll have to add #include "MachDeps.h"
to the beginning of the file. This makes the WORD_SIZE_IN_BITS
variable available:
#if (WORD_SIZE_IN_BITS == 64)
...
#else
...
#endif
There are other constants defined in MachDeps.h as well.
Sometimes it's easier to use cpphs than to fight with GCC's and Clang's differences, idiosyncrasies, etc:
[...] There have always been problems with, for instance, string gaps, and prime characters in identifiers. These problems are only going to get worse.
So, it seemed right to provide an alternative to cpp, both more compatible with Haskell, and itself written in Haskell so that it can be distributed with compilers.
Cpphs handles single quotes and /**/
correctly, doesn't mangle Haddock comments and knows about Haskell's multiline strings.
To use cpphs, you need to add these options to all sections of your .cabal file:
library
...
build-tools: cpphs >= 1.19
ghc-options: -pgmP cpphs -optP --cpp
A warning: Stack will install cpphs automatically, but cabal-install might not (and then you'd have to install it manually). Thus, depending on cpphs isn't recommended for libraries that would be published on Hackage.
If you can't use cpphs because of its modified LGPL license (e.g. in corporate environment where each license has to be vetted by a lawyer), you can use hpp instead.
The base-feature-macros package lets you write some macros more conveniently – e.g. instead of #if MIN_VERSION_base(4,8,0)
you can write #if HAVE_FOLDABLE_TRAVERSABLE_IN_PRELUDE
, which is much more understandable to a casual reader.
On Linux and Windows, GCC's CPP is used for preprocessing; on macOS – Clang's CPP. They have some differences, which you have to account for (unless you're using cpphs).
If you're on Linux, use ghc -pgmP "clang -E -undef -traditional" file.hs
to check whether your code would compile with Clang. If you use CI (e.g. Travis-CI), you can add it to your .cabal file in the ghc-options
section (hidden under a flag) and run your CI builds both with and without the flag.
Single quotes
Clang is more strict about single quotes than GCC. For instance, this piece of code using -XDataKinds
won't be expanded correctly by Clang:
#define X(a) Foo '[a]
X(Int) <- expanded into “Foo '[a]”
A workaround is defining a separate macro for '
:
#define QUOTE '
#define X(a) Foo QUOTE[a]
X(Int) <- expanded into “Foo '[Int]”
If you need to surround something with single quotes, this might help:
#define QUOTE() '
#define QLEFT(a) QUOTE()a
#define Q(a) QLEFT(a)'
Q(Int) <- expanded into “'Int'”
Concatenation
GCC expands the following macro as _foo
, but Clang adds a space and produces _ foo
:
#define U(x) _/**/x
U(foo)
There is no way to get rid of the space on Clang. Either rewrite your code to not need such tricks, or use cpphs.