C++ – Flags to enable thorough and verbose g++ warnings

cg++warnings

Often in C under gcc, I will start with the following set of warning flags (painfully assembled from multiple sources):

-Wall -Wextra -Wformat-nonliteral -Wcast-align -Wpointer-arith -Wbad-function-cast \
-Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations -Winline -Wundef \
-Wnested-externs -Wcast-qual -Wshadow -Wwrite-strings -Wno-unused-parameter \
-Wfloat-equal -pedantic -ansi

I will build (at least my debug versions) with this set of warnings and fix everything I possibly can (usually everything), and then only remove flags if they are either not relevant or not fixable (almost never the case). Sometimes, I'll also add -Werror if I have to step away while compiling.

I'm just picking up C++ (yes, I'm 15 years behind the times), and I'd like to start off on the right foot.

My question is: Does someone have a precompiled similar set of complete warning flags for C++ under g++? (I know many of them will be the same.)

Best Answer

I went through and found the minimal set of includes that should get the maximum level of warning. I then removed from that list the set of warnings that I feel do not actually indicate something bad is happening, or else have too many false positives to be used in a real build. I commented as to why each of the ones I excluded were excluded. This is my final set of suggested warnings:

-pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op -Wmissing-declarations -Wmissing-include-dirs -Wnoexcept -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-null-sentinel -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror -Wno-unused

Questionable warnings that are present:

  • I include -Wno-unused because I often have variables that I know I will use later, but do not yet have the functionality written for. Removing warnings about that allows me to write in my preferred style of occasionally deferring the implementation of things. It is useful to turn that off every once in a while to make sure nothing slipped through the cracks.

  • -Wdisabled-optimization seems like a strong user-preference setting. I just added this one to my build (only for optimized builds for obvious reasons) and it didn't turn anything up, so it doesn't seem to be an especially chatty warning, at least for the way I code. I include it (even though code that triggers this warning isn't necessarily wrong) because I believe in working with my tools instead of against them. If gcc is telling me that it cannot optimize code for the way I wrote it, then I should look at rewriting it. I suspect that code that triggers this warning could benefit from being more modular, regardless, so although the code is not technically wrong (probably), stylistically it likely is.

  • -Wfloat-equal warns for safe equality comparisons (in particular, comparison with a non-computed value of -1). An example in my code where I use this is that I have a vector of float. I go through this vector, and there are some elements I cannot evaluate yet what they should be, so I set them to -1.0f (since my problem only uses positive numbers, -1 is out of the domain). I later go through and update -1.0f values. It does not easily lend itself to a different method of operation. I suspect that most people don't have this problem, and comparison of an exact number in floating point is probably an error, so I'm including it in the default list.

  • -Wold-style-cast has a lot of false positives in library code I'm using. In particular, the htonl family of functions used in networking, as well as a Rijndael (AES) encryption implementation I'm using has old-style casts that it warns me about. I intend to replace both of these, but I'm not sure if there is anything else in my code that it will complain about. Most users should probably have this on by default, though.

  • -Wsign-conversion was a tough one (and almost didn't make the list). Turning it on in my code generated a huge amount of warnings (100+). Almost all of them were innocent. However, I have been careful to use signed integers wherever I wasn't sure, although for my particular problem domain, I would usually get a slight efficiency increase using unsigned values due to the large amount of integer division I do. I sacrificed this efficiency because I was concerned about accidentally promoting a signed integer to an unsigned and then dividing (which is not safe, unlike addition, subtraction, and multiplication). Turning on this warning allowed me to safely change most of my variables to unsigned types and add a few casts in some other places. It's currently a little hard to use because the warning isn't that smart. For instance, if you do unsigned short + (integral constant expression), that result is implicitly promoted to int. It then warns about a potential sign problem if you assign that value to unsigned or unsigned short, even though it's safe. This is definitely the most optional warning for almost all users.

  • -Wsign-promo: see -Wsign-conversion.

  • -Wswitch-default seems pointless (you don't always want a default case if you've enumerated all possibilities explicitly). However, turning on this warning can enforce something that is probably a good idea. For cases where you explicitly want to ignore everything except the listed possibilities (but other numbers are possible), then put in default: break; to make it explicit. If you explicitly enumerate all possibilities, then turning on this warning will help ensure that you put something like assert (false) to make sure that you've actually covered all possible options. It lets you be explicit in what the domain of your problem is and programatically enforces that. However, you'll have to be careful in just sticking assert (false) everywhere. It's better than doing nothing with the default case, but as usual with assert, it won't work in release builds. In other words, you cannot rely on it to validate numbers that you get from, say, a network connection or a database that you do not have absolute control over. Exceptions or returning early are the best way to handle that (but still require you to have a default case!).

  • -Werror is an important one for me. When compiling large amounts of code in a multi-threaded build with multiple targets, it's easy for a warning to slip by. Turning warnings into errors ensures that I notice them.

Then there is a set of warnings that are not included in the above list because I did not find them to be useful. These are the warnings and my comments on why I'm not including them in the default list:

Warnings that are absent:

  • -Wabi is not needed because I'm not combining binaries from different compilers. I tried compiling with it anyway, and it didn't trigger, so it doesn't seem needlessly verbose.

  • -Waggregate-return is not something that I consider an error. For instance, it triggers when using a range-based for loop on a vector of classes. Return value optimization should take care of any negative effects of this.

  • -Wconversion triggers on this code: short n = 0; n += 2; The implicit conversion to int causes a warning when it's then converted back to its target type.

  • -Weffc++ includes a warning if all data members are not initialized in the initializer list. I intentionally do not do this in many cases, so the set of warnings is too cluttered to be useful. It's helpful to turn on every once in a while and scan for other warnings, though (such as non-virtual destructors of base classes). This would be more useful as a collection of warnings (like -Wall) instead of a single warning on its own.

  • -Winline is absent because I don't use the inline keyword for optimization purposes, just to define functions inline in headers. I don't care if the optimizer actually inlines it. This warning also complains if it can't inline a function declared in a class body (such as an empty virtual destructor).

  • -Winvalid-pch is missing because I don't use precompiled headers.

  • -Wmissing-format-attribute is not used because I do not use gnu extensions. Same for -Wsuggest-attribute and several others

  • Potentially notable for its absence is -Wno-long-long, which I have no need for. I compile with -std=c++0x (-std=c++11 in GCC 4.7), which includes long long integer types. Those stuck back on C++98 / C++03 may consider adding that exclusion from the warning list.

  • -Wnormalized=nfc is already the default option, and looks to be the best.

  • -Wpadded is turned on occasionally to optimize the layout of classes, but it is not left on because not all classes have enough elements to remove padding at the end. In theory I could get some extra variables for 'free', but it's not worth the extra effort of maintaining that (if my class size changes, it's not easy to remove those previously free variables).

  • -Wstack-protector is not used because I do not use -fstack-protector

  • -Wstrict-aliasing=3 is turned on by -Wall and is the most accurate, but it looks like level 1 and 2 give more warnings. In theory a lower level is a 'stronger' warning, but it's at the cost of more false positives. My own test code compiled cleanly under all 3 levels.

  • -Wswitch-enum isn't behavior that I want. I don't want to handle every switch statement explicitly. It would be useful if the language had some mechanism to activate this on specified switch statements (to ensure that future changes to the enum are handled everywhere that they need to be), but it's overkill for an "all-or-nothing" setting.

  • -Wunsafe-loop-optimizations causes too many spurious warnings. It may be useful to apply this one periodically and manually verify the results. As an example, it generated this warning in my code when I looped over all elements in a vector to apply a set of functions to them (using the range-based for loop). It is also warning for the constructor of a const array of const std::string (where this is no loop in user code).

  • -Wzero-as-null-pointer-constant and -Wuseless-cast are GCC-4.7-only warnings, which I will add when I transition to GCC 4.7.

I've filed a few bug reports / enhancement requests at gcc as a result of some of this research, so hopefully I'll be able to eventually add more of the warnings from the "do not include" list to the "include" list. This list includes all warnings mentioned in this thread (plus I think a few extra). Many of the warnings not explicitly mentioned in this post are included as part of another warning I do mention. If anyone notices any warnings that are excluded from this post entirely, let me know.

edit: It looks like I had missed several (which I have now added in). There is actually a second page at http://gcc.gnu.org that is quite well hidden. General warning options and C++ options (scroll down to the bottom for warnings)

Related Topic