Monday, October 26, 2009

A poorly chosen warning

GCC spits out warnings when you declare a local variable and don't use it. For example:

int x = 3, y = 4;
cout << "The value is " << y << endl;

This would warn you that you never used the value 'x', and is a useful warning. But I think they're too overzealous:

int success = doSomethingImportant();
#ifdef _DEBUG_ON_
cout << "Did something important. It was "
<< (success ? "successful." :
"unsuccessful.") << endl;

If you don't define the _DEBUG_ON_ symbol, GCC will issue a warning here. And if it's late at night and you're not paying attention, you might make a mistake and move that 'doSomethingImportant()' call inside of the #ifdef, because after all, GCC is warning you that it's not doing anything. And sometime on Saturday, while fixing several hundred warnings, I did precisely that (although in my defense, the real code was a bit longer). It took several hours to figure out why the test suite was failing; if we hadn't had a test suite, we may have accidentally allowed a very buggy piece of software out the door. As it was, it kept me at work late.

This particular warning strikes me as poorly thought out. I agree with the warning when the initialization value is constant, but when the initialization is complex, with the potential for side effects, I believe the warning should not be issued. It certainly would have saved me a literal headache today.

I haven't tried this with the latest GCC yet, by the way; I only have a really old 3.4.5 compiler on my machine. Feel free to contradict my conclusions in a flaming, foaming-at-the-mouth sort of way.

Thursday, October 22, 2009

Porting C++

Blowing the dust off this old blog, I notice that I never finished my previous post about vector-based sets. I've been using one for a while, and the performance is great, but unless some reader shows an enormous amount of interest, I'm not going to bother finishing the explanation. Instead, I'm going to complain about porting.

As part of my work, I write cross-platform code. Most of the time, however, I just write in Visual Studio, and every few months we check that everything still compiles and runs in GCC, preferably with zero warnings. Compiling on GCC & Visual Studio isn't everything, but it's a pretty good litmus test. While doing this, I've found a few important "gotchas" where Visual Studio zigs and GCC zags. Here, for your delectation, is my list:

1. Templates. There are so many issues in porting Visual Studio templates to GCC that I'll just brush gently over the top and move on. It's tricky. Visual Studio allows horrible syntax problems within template code, and won't tell you there's a problem until the code is instantiated and used in a fully specified type. GCC is much stricter. GCC also sticks more tightly to the rules in the standard. If you're having problems with a template that worked in Visual Studio, some good tips are:

- Try adding the word "typename" in front of template parameters when they refer to a type. In some rare cases, this can disambiguate the syntax. Visual Studio delays evaluation of the syntax until it can check the symbol table, but GCC requires it to be unambiguous up front.

- Visual Studio will often accept a partial name (i.e. TypeName) where GCC requires a full name (i.e. TypeName< A, B >). Using full names is almost never wrong. This is very often true if you combine inheritance and templates. Also be aware that you probably need to specify the full template name when referring to member variables in a base class, i.e. "BaseClass< A, B >::member_var" rather than just "member_var". Again, there are ambiguity problems in the dark, smelly corners of the C++ language.

2. Newlines at the end of a file. Visual Studio doesn't care, GCC warns you if there isn't one.

3. Initialization order. The C++ standard requires you to initialize member variables in the order they're declared in the class. If there's a mismatch between the initializer list in the constructor and the declaration order, GCC throws a warning at you. Visual Studio doesn't care. Keep the order the same and GCC will be happy. It took me hours to fix all of these last time, and fixing them is a mistake-prone process.

4. GCC is more aggressive in warning you about local variables which were declared & initialized but never used. This is mostly helpful, honestly. It is a bit annoying when it starts picking out file-local (static) variables that are only used in debug mode, though :P. In the end, the workaround for that one was not using a static variable.

5. GCC has a 'uint' type, short for 'unsigned int'. Visual Studio does not. We get around this with an ifdef/typedef block in a common header, since we like uint.

6. The two compilers have different pragmas. #pragma warning does not work in GCC. If you don't want to be warned about unrecognized pragmas, you should wrap #ifdef _MSC_VER around your pragmas (that's a flag to test for whether you're using Visual Studio, roughly). #ifdef WIN32 is not as good, because GCC defines that symbol when you're compiling under Windows ;).

7. Your life will be better if you don't inherit std::exception. If you must, though, ensure that your child class has a destructor with the "throw()" specifier, since the auto-generated destructor has no such specifier and this will cause compile errors in GCC.

8. GCC objects (warning) if you use "switch" with an enumerated type but do not include all possible values of the enumeration in the "case:" statements. Visual Studio doesn't care. If you stick in a blank/useless "default:" case, then all is well.

9. GCC warns about type mismatches if you compare unsigned and signed integers using == and !=. Visual Studio doesn't care.

10. All of Microsoft's secured functions are non-standard (sscanf_s and so on). You can't use them in GCC. Either do it the C++ way (cin, istream, etc.), roll your own (if you're complaining about how slow the C++ ones are, then you should, because you'll probably go faster), or use the unsafe/standard functions and define _CRT_SECURE_NO_DEPRECATE to avoid the idiotic warnings in Visual Studio.

These are a few things I've run into myself recently. Some of them are a bit tricky to figure out, so maybe I shall aid some poor Google searcher :P. Happy porting!