Thursday, 2 July 2009

C++: How to say "#warning" to the Visual Studio C++ compiler

I encountered a piece of code that would no longer compile in a particular build variant of our product. I wanted to hack it out, and leave a compiler warning in it's place so we wouldn't lose track of the change.

It's easy in gcc. You simply say:
#warning FIXME: Code removed because...
So that's me sorted for Linux and MacOS. I'm happy in the Fun Place.

But in the Dark Place I was clueless. How do you say #warning to Visual Studio? You can happily write #warning in C#, but not C++.

Interestingly, the answer fell below my Google/Boredom Threshold (i.e. a web search didn't reveal the answer in sufficient few clicks that I lost interest). I just shoved in a run-time assertion instead. It'd do the job, but not as immediately as I would have liked.

Thanks to hashpling and the miracle that is Twitter, I now know the answer, and share it with you in the vein hope it might come higher up the Google rankings for those poor souls that follow me:
#pragma message ("FIXME: Code removed because...")
Needless to say, this is all tediously non-standard.

For bonus points

This still doesn't get us exactly the same behaviour as gcc's #warning. The message is produced, but without file and line information. This means that if you double-click the message in the VS IDE it will not jump to the warning in the editor window. It also means that build logs aren't much use.

Sam Saariste pointed this out, and here's the standard preprocessor mumbo-jumbo you have to jump through to get the exact warning behaviour I was after:
#define STRINGIZE_HELPER(x) #x
#define STRINGIZE(x) STRINGIZE_HELPER(x)
#define WARNING(desc) message(__FILE__ "(" STRINGIZE(__LINE__) ") : Warning: " #desc)

// usage:
#pragma WARNING(FIXME: Code removed because...)
Couldn't be simpler, could it?!

8 comments:

codemonkey_uk said...

It's a shame you can't #define a #pragma, or you could take this to it's logical conclusion:

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define WARNING( txt ) __FILE__"(" TOSTRING(__LINE__) ") : warning: " txt

#pragma message( WARNING( "a warning you can click on" ) )

Pete Goodliffe said...

To be honest, it's a shame there's still no standard way of doing this.

#warning seems quite sensible to me...

James Slaughter said...

So the obvious question is: Why do that?

I think it's a mistake to use compiler output as your bug tracking tool. We've all seen noisy builds that spew so many diagnostics that new problems are lost in the wash. Either the problem's important enough to fix now, or it should go in the bug database...

Pete Goodliffe said...

James,

Fair point. My response is simple: "yes" and "no".

My suggestion is not using compiler output as bug tracking. Of course, you have bug tracking to track bugs. I'm using it to mark a section of code I'd like to come back to shortly in a very obvious way, and as a tool to communicate to other programmers - in the code itself (the only definite place where you'll see comments ABOUT the code as you work on it) - that this is an area under work right now that is not "production" ready.

This is the kind of thing we do with comments all the time, but #warning is a construct that allows us to say it with a little more vehemence!

*If* you have an awful, noisy build then of course #warnings would get lost in the noise. But in a nice, quiet build, this stands out well.

It's a matter of being pragmatic, I guess.

Certainly, in many (perhaps not-so-well-run) dev shops, putting the issue into the bug database is just a different way of throwing the problem away and forgetting about it, lost in the noise of 1,000,000 other bugs :-)

AldurDisciple said...

An interesting note for future readers of this article:

if you use the set of macro given in section "For bonus points", you might prefer using this macro instead
#define WARNING(desc) message(__FILE__ "(" STRINGIZE(__LINE__) ") : warning: " #desc)
(notice the lowercase 'w' in 'warning'), because in this case VisualStudio will automatically display the warning in the "Error List" window, so that it don't get lost in the "Output" window.

Friedel said...

@James Sometimes you want to commit a change or some test code to investigate things.
I use this a lot (once every 6 month) when I need a define from someone else, e.g. from i13n team.
with each local #define i post a #warning "define missing"

Anonymous said...

@James
As others have said, this is for something that needs attention before release, but must not be forgotten or prevent a build.

It works especially well if you have the rule that dictates you can't release before resolving >all< warnings.

erzet said...

I don't often defend the way msvc treats the standard, but in this case, they're the ones doing it properly and #warning is the "tediously non-standard" way.

The standard has a section on preprocessor directives (16) and while #error is mentioned, #warning is not. What's more, compilers are required to ignore a pragma they don't recognize but error out on a directive they don't know.

So if a compiler wants to provide functionality the standard doesn't mention, it should be done through pragmas, not by inventing a new directive (as pretty as it may seem).