Use the #error directive to check whether the compiler even sees you


You may find yourself in a twisty maze of #ifdefs. Or you may be wondering why your macros aren't working.

I have these lines in my header file:

#define MM_BUSY     0x0001
#define MM_IDLE     0x0002

but when I try to use them, I get errors.

sample.cpp(23): error C2065: 'MM_BUSY': undeclared identifier
sample.cpp(40): error C2065: 'MM_IDLE': undeclared identifier

Any idea why this is happening?

First, make sure the compiler even sees you. Notice that for macros, generating a preprocessed file doesn't accomplish anything since #defines don't show up in the preprocessor output. (They are preprocessor input.) What I do is use the #error directive. Add it to the header file and recompile.

#define MM_BUSY     0x0001
#define MM_IDLE     0x0002
#error Did we get here?

If you get

sample.h(80) : error C1189: #error :  Did we get here?

then you know that the line is indeed being compiled and that somebody after you is doing an #undef MM_BUSY. If not, then you get to investigate why the lines in the header file are being ignored. For example, they might be hidden by an #ifdef, or (if you're using Visual Studio with precompiled headers), your #include directive might be ignored due to an overriding precompiled header directive. You can scatter #error directives into other parts of the header file (or other header files) to narrow down why your lines are being skipped.

Comments (47)
  1. Name required says:

    I used to do something similar, but my messages were never so polite.

  2. scott says:

    Too much real typing required, I prefer this method:

    #define MM_BUSY     0x0001

    #define MM_IDLE     0x0002

    asdasdasd

    The compiler will choke on the garbage and you’ll know if you’re there.

  3. SRS says:

    I’m raising a request to have:

    #define asdasdasd

    added to windows.h

    Ha! That’ll teach you not to use #error

  4. Matthias says:

    Mmh, compile-time printf debugging.  It’s always nice to have a low-tech fallback when the power tools are failing.

  5. GregM says:

    I’ve used this type of trick many times, it’s really handy.

    If putting in the error text is too much typing, this works just as well:

    #error

  6. mikeb says:

    > Mmh, compile-time printf debugging.  It’s always nice to have a low-tech fallback when the power tools are failing.

    Unfortunately, there are pretty much no ‘high-tech’ ways to debug preprocessor actions.

    I was so very happy when MS added the /showIncludes option to the compiler.  That feature helped several times on projects that had very complex include directory requirements.

  7. Or… open up the header in Visual Studio. If it’s grayed out, an #ifdef is hiding it.

  8. Koro says:

    What? There is a /showIncludes switch?

    Damn, I spent half a day writing an "include checker" once which would follow includes in C files and show them, to debug some complex cyclic headers dependencies…

  9. Jay Bazuzi says:

    Sjoerd: that only works if you’re using a Visual Studio project to compile.  Sadly, there are plenty of makefile-based and custom build systems out there.  

    (I say sadly because they miss out on so much cool stuff.)

    For example, for a long time, most Microsoft software was built with a custom build system, because the Visual C++ project system didn’t scale to the very large sizes of Microsoft software.  

    With the advent of msbuild and the adoption of C#, that is changing at Microsoft.  Yay!

  10. scott says:

    I’m raising a request to have:

    define asdasdasd

    added to windows.h

    Ha! That’ll teach you not to use #error

    OK, fine, I’ll generate a GUID and use that as my compiler poison pill. Let the arms race begin!

  11. Sjoerd: as clever as Visual Studio’s syntax highlighting is, it surely can’t tell you accurately which #if or #ifdef statements are in effect — the condition could easily evaluate differently in different source files.

  12. Evil says:

    until somebody doesn’t do

    #define asdasdasd ;

    :)

  13. IgorD says:

    It took me a while to understand what are you talking about and I’m still not sure how to word my question.

    In my IDE, if something is defined and the source has already compiled, syntax coloring will catch all declared macros and they turn from black to some color, in my settings it is dark blue.

    So if I have:

    #define MM_BUSY

    .. as I type ‘MM_BUS’, the word is black, but as I add ‘Y’ the whole word turns blue and I know I typed it right.

    Right-click on the word can show me where it was defined. What is it that I’m missing here?

  14. DB says:

    The Microsoft compiler supports similiar options:

    cl.exe /P – generates preprocessed output files (*.i)

    cl.exe /EP – outputs preprocessed output to stdout

  15. Evan says:

    @Sjoerd Verweij: "Or… open up the header in Visual Studio. If it’s grayed out, an #ifdef is hiding it."

    Good heuristic, but not absolutely reliable. For instance, I worked on a project where there was a situation something like there was a file with additional command line options used with cl … @file; the file forced the inclusion of a certain header in each file; this header modified the macros in force. The upshot was that VS was wrong about what code was active, which was both misleading and may have suppressed intellisense in the disabled part (I forget if it does that). The solution was to turn that feature off in the editor.

    @Miguel Duarte: "Raymond, I use another approach which may be more complex than your hint, but it may be more useful when dealing with more complex macro problems."

    This is also a useful trick, but it’s helpful in different situations. The #error trick is useful if you want to figure out if a particular piece of code is being compiled. Looking at the preprocessed source is useful if you’re having macro expansion problems, and less useful if you want to find out if code is there.

  16. Tom_ says:

    The Visual Studio IDE doesn’t always get the status of #defines correct, so sometimes you can find parts of code greyed out or coloured in, when they shouldn’t be. The same goes for "go to definition", which will find something irrelevant just often enough to be annoying. With this in mind, #error is still useful.

    (This is more of an issue with multi platform projects that contain a lot of files that are irrelevant for any given platform. The IDE seems to have a tendency to look inside these files sometimes, even though they’re excluded from build, or #ifdef’d out, or the project excluded, or however it is you like to do this stuff.)

  17. pingpong says:

    @IgorD: assuming that your IDE perfectly highlights all #defines, Raymond’s post refers to the scenario when MM_BUSY remains black when you complete typing it(because header file was not included, probably due to #ifdef/#endif guards surrounding it).

  18. Brian K says:

    We use these macros for inserting warnings like "fix this later":

    #define STR2(x) #x

    #define STR1(x) STR2(x)

    #define LOC FILE "("STR1(LINE)") : warning: "

    #define LOC2 FILE "("STR1(LINE)") : "

    #define COMPILE_WARN(s) message(LOC s)

    #define COMPILE_ERROR(d,s) message(LOC2 "error " d ": " s)

    Then in the source code use:

    #pragma COMPILE_WARN("Check it here")

    #pragma COMPILE_ERROR("C9999","Don’t forget to code here!")

  19. I agree, the IDE is not perfect — but it does well enough 99% of the time.

    And yes, /showIncludes is an absolute blessing; I have it on all the time. It really helps with the little things, like, oh, how STLport (don’t ask) needs an #undef __in_range to compile with the latest SDK. Reason 1,349,088 why I switched to C# (except for maintenance). C++ is just too time-consuming.

  20. Evan says:

    @Sjoerd Verweij: "I agree, the IDE is not perfect — but it does well enough 99% of the time."

    Oh, I agree. But you still need a technique for dealing with the remaining 1%, and this is it. ;-)

  21. IgorD says:

    @pingpong: Raymond’s post refers to the scenario when MM_BUSY remains black

    Well yes, and now I see why VS team needed some ‘official’ recomendation they could put into VS manual for such cases when 90% of regular guys just type ‘asdfasdf’ as SRS suggested above.

  22. IgorD says:

    AH!

    … as scott sugested above! SRS was mocking him!

  23. Peter says:

    Reminds me of ‘debugging’ ASP waaaay back in a past life; pretty much the only tool you had was to response.write things (often obscene or ridiculous ones) to find if it was working.

    It’s possible that #error might even be slightly cruder though… isn’t the preprocessor great :-)

  24. Marc K says:

    I’ve used this before.  Most often I’m trying to use an API in which the SDK says it should be available in Windows X, but the header has it enabled only for Windows Y and newer.  It would so nice if there was a feature in the IDE that would show you what (nested) pre-processor conditions are in effect for the selected line.

  25. MadQ1 says:

    The problem with IntelliSense graying out excluded code is that it doesn’t seem to to take into account pre-defined macros, or those defined with the /D command line switch.

    For example:

    #if !defined(_CPPRTTI)

       error C++ Run-Time Type Information must be enabled.

    #else

       pragma message("C++ Run-Time Type Information is enabled.")

    #endif

    works as expected when compiled, but the message pragma is always grayed out. It’s not really a big deal, but it has confused me a couple of times.

  26. steveg says:

    @Sjoerd Verweij: "C++ is just too time-consuming."

    Totally, utterly agree. Once the complexity of the application goes beyond a certain point C++ becomes more a game of fighting the compiler than actually doing work.

    Last big C++ project I did took 6 hours to compile (GCC runs like molasses compared to Intel’s or MS’s compilers). Very intricate hierarchy of templated classes (and macros that generated macros that generated templated classes — <aside>Patterns gone mad — Big 4 patterns generate Too Many Classes. Consider a data/table-driven approach with 50000% less code to maintaint</aside>. Only to find that right at the end you had 73 template-related compile errors — have you ever seen one of those suckers? They’re dozens of lines long and don’t simply don’t help!

    Which is why I like C#. I spend more time solving a work-related problem than solving a "WTF was Bjourne thinking".

  27. Raymond, I use another approach which may be more complex than your hint, but it may be more useful when dealing with more complex macro problems.

    I just instruct the compiler to generate all the code that would be compiled after the preprocessing stage and inspect the output.

    With gcc(*) you can achieve that with -E compiler option

    from the help:

        -E  Stop after the preprocessing stage; do not run the

            compiler proper.  The output is in the form of

            preprocessed source code, which is sent to the standard

            output.

            Input files which don’t require preprocessing are

            ignored.

    I’m not really familiar with Microsoft(*) tools, but I’m sure all compilers should have similar options.

    By the way, although I’m not familiar with Windows development, I really like your blog! Thank you for sharing the hairy details!

  28. Evan says:

    @IgorD: "Right-click on the word can show me where it was defined. What is it that I’m missing here?"

    In part, that if they are not defined (so they don’t turn blue and go to definition doesn’t work), those features don’t do anything to help you figure out why the definition you *think* should be in force is not.

  29. chrismcb says:

    @Miguel Duarte "I just instruct the compiler to generate all the code that would be compiled after the preprocessing stage and inspect the output."

    Miquel, did you read what Raymond wrote?

    "Notice that for macros, generating a preprocessed file doesn’t accomplish anything since #defines don’t show up in the preprocessor output."

    The problem is you think you’ve #defined MM_BUSY to 1, but it doesn’t show up as 1 in the code. You know this because you have an error.Looking at the precompiled output only confirms it. But it doesn’t give you a clue why the #define isn’t working.

    @steveg how big are your C# projects compared to your C++ projects? I imagine 10 years from now when the C# codebase approaches the complexity of the C++ codebase you’ll be seening similar issues.

  30. Miral says:

    @MadQ: "The problem with IntelliSense graying out excluded code is that it doesn’t seem to to take into account pre-defined macros, or those defined with the /D command line switch."

    Thing is, if you’re looking inside a .h file, it can’t.  For any given line in the .h file, the defined symbols could vary significantly, depending on the compiler options and earlier include files and #defines made in the including .cpp file.  And since a .h file can be included from more than one .cpp (each of which having different patterns of definitions), you’re SOL.

    In fact a header file can even be intentionally included more than once in a single .cpp in certain cases (which can be a useful technique — it gives you a poor-man’s-codegen), which practically guarantees the definitions will be different each time.

  31. Peter says:

    steveg: I’d argue it’s more about program design. At work we’ve got (among many other things…) a moderately large C++ app and a fairly small C# app. The C# app takes me heaps more time to come to grips with every time I have to use it, because it’s poorly structured and very not obvious how anything actually works. The C++ one has some nice hierarchies of templated excitement, but I’d rather figure out technical compiler messages (which are my fault anyway…) that at least have logic behind them rather than try to figure out what some programmer intended, which very probably had no logic at all.

    My experience with GCC isn’t so negative either. My copy of VS2005 has two modes: slow and fast, and some days it will randomly enter slow mode, in which case it is far, far slower than GCC (or pretty much anything…).

    I don’t believe that increasing project size has to automatically make things that much worse in C++ if it’s done properly; it may be difficult to do do but who ever said this programming game would be easy?

  32. Dave says:

    My copy of VS2005 has two modes: slow and fast, and some days it will

    randomly enter slow mode, in which case it is far, far slower than

    GCC (or pretty much anything…).

    Have you tried disabling IntelliSense?  (Google for something like "intellisense" + "slow" to find endless posts on this).  I’ve found it so bad on some machines that it rendered them more or less unusable (as have numerous others), but disabling IntelliSense by removing the necessary DLL fixes it.

    ObSnarkyComment (for Raymond): MS sucks, can’t even write a decent compiler, blah blah blah :-).

  33. Xepol says:

    required "that that’s why xxxx is better than yyyy" religious language war comment here…

  34. steveg says:

    Peter & chrismcb: okay, I’ll give some ground on design vs language. My job after the C++ one was a contract maintaining a Delphi application that was written by accountants (and used by around a million people) that was the worst structured program I’ve ever encountered (ooh look a function 3km long. With gotos). We tried to model it three times and gave up; there is no modelling technique I’m aware of that can generate useful documentation from a quantumly entagled n-dimension hypercube.

    The C++ app had a CORBA ACE/TAO layer of template fun in the mix with all its memory-management-related joys. Add in GCCs incomplete/non-standard template support with occassional bonus invalid generated code it was a whole bunch of fun. By the end even the C++ bigots who designed the system gave up on C++ and started writing new modules in Java — basically all roads from C++ lead to a managed environment.

    But to keep this approximately on topic (sorry Raymond) when you’re generating templates with multiple layers of macros the #error trick is REALLY handy. Or trying to work out how MFCs macros work.

    Interestingly, C# is limited to simple macro support, you can only #define FOO.

  35. Worf says:

    My one wish is that #warning would be supported. (Similar to #error, but it generates a warning during the build, rather than an error.)

    That way, when doing libraries, you can use various tricks to warn when people use deprecated APIs or do even rudimentary checks that won’t halt the build, but hopefully someone notices.

  36. Miguel Duarte says:

    @chrismcb Miquel, did you read what Raymond wrote?

    Silly me… Expanding preprocessor macros if the compiler won’t really help if the compiler is already telling you that the macro has been undefined. In that case I would just grep for #undef MACRONAME. But the #error directive is really helpful since you’ll probably discover more subtle problems

    But undefined macros are just one kind of macro problems.  The macro may have been expanded to some unexpected value. If you know the value the macro has been expanded to, you can easily grep for it in all your project includes and discover the culprit easily.

    @steveg how big are your C# projects compared to your C++ projects? (..)

    I’m with you on this one. C++ has a steep learning curve, but as soon one is able to grasp the concepts it’s actually better for complex problems.

    Most of the bad reputation with C++ comes from historically bad programming education and poor compiler support (one only has too look into what the Boost team had to do to achieve some sort of portability between compilers). The fact the standard library doesn’t include all sorts of stuff developers take for granted nowadays doesn’t help either…

  37. Miguel Duarte says:

    Replying to myself

    "In that case I would just grep for #undef MACRONAME"

    This doesn’t work in the scenario Raymond presented either :-) The macro may have never been defined as far as the compiler is concerned.

    Yeah… the #error directive is really the only way to go. I guess I’m having caffeine withdrawal symptoms :-)

  38. pingpong says:

    Regarding C# vs. C++: observe the steady decline in quality of VStudio, directly proportional to amount of managed code in it. VC6 IDE FTW!

  39. Curious Observer says:

    It’s declined how? I’ve been coding in Visual Studio since ’97. The IDE has improved by leaps and bounds IMHO.

    Your post reads like the typical "it’s m$ it’s junk blah blah".

    I know it sounds like I’m picking on you, but I’m not. But for the benefit of the rest of us could you elaborate a little on how manage code has negatively affected the quality of visual studio?

  40. name required says:

    (ooh look a function 3km long. With gotos)

    If you’re looking for a new contract I see you have the kind of experience we are looking for…

  41. pingpong says:

    @Curious: If I’d be typical "m$ junk blah blah" type I wouldn’t use VC6 for comparison, would I?

  42. MadQ1 says:

    @Miral: The only header file I’m aware of in the Windows SDK that can be included twice in the manner you describe is SchemaDef.h, which is included by tmschema.h (the second pass defines the visual styles strings,) and it’s obsolete as of Longhorn.

    IMO, including header files multiple times with different effects is one of those tricks that’s just too clever for its own good.

    I don’t recall ever coming across another example in anything I’ve ever worked on, so for me at least it’s not much of an issue.

  43. Curious Observer says:

    @pingpong

    Touche’ but you still didn’t elaborate. I’m honestly curious. I meant what I said about not picking on your post. How has it declined? Maybe it’s something you found in features I don’t use… who knows? But as a fellow dev these things do spark my interest. So if you could elaborate it would be appreciated, if not I understand.

    Curious

  44. pingpong says:

    @Curious: I’m using VStudio for native C/C++ development. From my point of view, the quality went down – the IDE feels sluggish, the crashes are more frequent. And don’t get me started of ‘Find in files’ dialog ;)

  45. Peter says:

    pingpong: I won’t argue on any of those points. But I’ll suffer a lot in exchange for the ability to peek inside STL containers in the debugger in VS2005.

  46. pingpong says:

    @Peter: I admit, the debugger has evolved nicely. What do you think about IntelliSense in VS2005? ;)

  47. В ответ на мое описание того, как вы можете использовать директиву #error , чтобы проверить, что компилятор

Comments are closed.