Warnings: Compiling
The level of warnings generated by the gcc compiler has been
set to be more pedantic than the default [1].
While this introduces some pain to writing code that compiles
without warnings it helps catch mistakes and makes for more portable
code.
This document is an attempt to give examples of some of the most
common warning messages and suggest remedies.
Example:
/home/minlib/minossoft/packages/MyPackage/HEAD/MyClass.cxx: In member
function `void MyClass::Print(const Option_t* option) const':
/home/minlib/minossoft/packages/MyPackage/HEAD/MyClass.cxx:394: warning: unused
parameter `const Option_t* option'
This will probably be the most common warning.
It is generated when the implementation of a class method fails
to use one of the parameters. Often it is quite legitimate to not
make use of a parameter because the method's signature is derived
from overriding a base class's method and the argument is irrelevant
for the particular usage. But it can be an important reminder of
potential logic mistakes -- so one should seriously evaluate whether
it makes sense for a particular method to not use
all of the supplied arguments.
Eliminating the warning is trival; simply comment out the naming of
the argument in the implementation file (.cxx), e.g.:
void MyClass::Print(const Option_t* option) const
becomes:
void MyClass::Print(const Option_t* /* option */) const
Example:
In file included from /home/minlib/minossoft/packages/MyPackage/HEAD/MyClass.cxx:3:
/home/minlib/ROOT_CVS/GCC_3_2/include/TObject.h:143: warning: `virtual void
TObject::Print(const Option_t*) const' was hidden
/home/minlib/minossoft/releases/development/include/MyPackage/MyClass.h:23: warning:
by `void MyClass::Print(const Option_t*)'
This warning arises because out of two common cases:
- The class author was attempting to override a virtual function
of the base class but made a small but significant error in
declaring the signature[2].
- Authors of both classes tried to make use of common words for methods
names, e.g.: Clear, Compare, Print.
This happens when the base class (often TObject) defines such a method
and the writer of the derived class seeks to define a similar concept
but with a quite distinct signature.
The example above falls into the first category. If one looks carefully
one discovers that the MyClass version is a non-const method
and thus the attempted overriding wasn't acheived. This can be
remedeed by bringing the signatures into accord.
An example of the second category might be declaring MyClass::Clear()
while TObject has a method Clear(const Option_t*).
Here it might be worthwhile to morph the new declaration to match the
base class while retaining the illusion of the old behaviour.
In this case one could declare, in the header, the method as:
Clear(const Option_t* option="")
By supplying a default value no current arg-less usage must be changed.
Question for the C++ language lawyer:
If the base class B has signatures
- Clear(const Option_t* opt="")
- Doit(AnotherObj* x)
and the derived class D declares and implements
- Clear()
- Doit(DifferentObj* y)
and one has a D object:
- Are there cases where B::Clear will be called instead of D:Clear?
- Is Doit(AnotherObj*) inaccessible unless rewritten in class D?
(This would empirically appear not to be the case).
Example:
/home/minlib/minossoft/packages/MyPackage/HEAD/MyClass.cxx:200: warning: default
argument given for parameter 1 of `void MyClass::Trace(const char* = "")
const'
/home/minlib/minossoft/releases/development/include/MyPackage/MyClass.h:42: warning: after
previous specification in `virtual void MyClass::Trace(const char* = "")
const'
This occurs when the implementation file (.cxx) specifies
a default argument. Only header files (.h) should ever
give a default value.
This can also occur if the header is included twice and not protected
against the second parsing via the usual mechanism:
#ifndef THISHEADERFILE_H
#define THISHEADERFILE_H
(header information goes here)
#endif
This protection is a required by the coding conventions.
Example:
/home/minlib/minossoft/packages/MyPackage/HEAD/MyClass.cxx: In
constructor `MyClass::MyMethod(MyObject*)':
/home/minlib/minossoft/packages/MyPackage/HEAD/MyClass.cxx:85: warning: deprecated
conversion from string constant to `char*'
This comes from the assignment of a string constant (or literal) to
a non-const char*. A literal is text within double-quotes (").
This is generally allowed by the older standard, but is dangerous because
common literals might be shared and thus if one were to modify the
value others variables would magically change, e.g.:
const char* mystring = "xyzzy";
char* another = "xyzzy"; // might share address w/ 'mystring'
strcpy(another,"ABCDE"); // in principle modifying a non-const is okay
cout << mystring << endl; // suprisingly might be 'ABCDE'
Generally the simple fix is simply to always make the assignment of
a literal to a const char*. Sometimes when passing a literal
to a method/function one gets this warning. In this case one has two kosher
options (excluding const_cast<char*>()):
- Convert the method/function to accept const char*.
Often the method doesn't need to be able to modify the
argument, in which case it makes good sense to make it const
(and falls in line with the MINOS coding conventions). This is the
preferred approach if possible.
- Make a temporary copy that can be passed to the method. This is
much more distasteful as it involves allocating space, copying,
calling the method/function then deallocating.
Example:
/home/minlib/minossoft/packages/MyPackage/HEAD/MyClass.cxx: In
member function `void
MyClass::Doit(int)':
/home/minlib/minossoft/packages/Monitoring/HEAD/MyClass.cxx:782: warning: ISO
C++ forbids variable-size array `abc'
This warning points to the usage of a GNU gcc extension
is a serious portablity problem. Technically the code:
void MyClass::Doit(int n) {
float abc[n];
....
abc[i] = 3.1415
...
}
is illegal, because the value of n is not known at compile time.
GNU gcc provides an extension for automatic arrays
that allocate based on run time values; but relying on these makes
the code out of compliance with the standard.
If the value of n isn't known until run-time then one can
bring the code into compliance by either converting to performing
the allocation/deallocation explicitly on conventions arrays,
or by using STL containers.(e.g. std::vector).
void MyClass::Doit(int n) {
float *abc = new float[n];
....
abc[i] = 3.1415
....
delete [] abc; // don't forget to delete with []
}
#include
void MyClass::Doit(int n) {
std::vector abc(n); // note ()'s, not []
....
abc[i] = 3.1415
...
}
Example:
home/minlib/minossoft/packages/MyPackage/HEAD/MyClass.cxx: In member
function `Float_t MyClass::MyMethod(float)':
/home/minlib/minossoft/packages/MyPackage/HEAD/MyMethod.cxx:160: warning: control
reaches end of non-void function
These are caused when there is some way for a function declared to return
a value (i.e. non-void), but doesn't, e.g.:
Float_t MyClass::MyMethod(float x) {
if ( x >= 0 ) return 100.;
}
if x is negative the function ends but no value is explicitly
returned. What generally happens is that the caller of the function
will get whatever garbage was on the stack, which can lead to unpredictable
results. Users should ensure that non-void function return a meaningful
value under all conditions.
[1]: The additional flags placed on the g++
command line are:
| -Wall |
Issue warnings for conditions which pertain to
usage that we [gcc] recommend avoiding and that we
believe is easy to avoid, even in conjunction with
macros.
|
| -pedantic |
Issue all the warnings demanded by strict ANSI
standard C; [generate warnings for] all programs that
use forbidden extensions.
|
| -W |
Print extra warning messages for these events:
- An unsigned value is compared against zero with >
or <=.
- An expression-statement or the left-hand side of a
comma expression contains no side effects. To suppress
the warning, cast the unused expression to
void. For example, an expression such as x[i,j]
will cause a warning, but x[(void)i,j] will not.
- A nonvolatile automatic variable might be changed
by a call to longjmp. These warnings are possible
only in optimizing compilation.
|
| -Wno-long-long |
Do not warn if long long type is used. (Takes this
out of those enabled by -pedantic).
|
| -Wwrite-strings |
Give string constants the type const char[length]
so that copying the address of one into a non-const
char * pointer will get a warning. These warnings
will help you find at compile time code that can
try to write into a string constant, but only if
you have been very careful about using const in
declarations and prototypes. Otherwise, it will
just be a nuisance; this is why we did not make
-Wall request these warnings.
|
| -Winline |
Warn if a function can not be inlined, and either
it was declared as inline, or else the
-finline-functions option was given.
[RWH: I'm not sure this works]
|
| -Woverloaded-virtual |
In a derived class, the definitions of virtual
functions must match the type signature of a virtual
function declared in the base class. Use this
option to request warnings when a derived class
declares a function that may be an erroneous
attempt to define a virtual function: that is, warn
when a function with the same name as a virtual
function in the base class, but with a type signature
that doesn't match any virtual functions from
the base class.
|
[2]: A particular function has a
signature that is defined by the function's name;
its parameter types (in order);
and for class method whether it is const (i.e.: can act
on const objects because it does not modify any data
members; this is signified by a trailing const after
the argument list).
Contact:
Robert Hatcher (<rhatcher@fnal.gov>)