Friday, June 17, 2011

Evil C++ #3: Weird automatic overloading of constructors

#include<iostream>
/** C++ does some strange stuff with constructors. Having default values makes
* sense, but they've added some weird, unintuitive shorthand that's just an
* accident waiting to happen:
*
* MyClass c = MyClass(5) <--> MyClass c = 5
*
* where in both cases 5 is taken as the first argument. In both cases, it will
* do whatever casting is needed and allowed.
*
* What weirds me out is that this syntax is specific to single-argument
* constructors. You could pull this in Python (MyClass c = 1, 2, 3)...
*/
class Foo
{
public:
Foo(int a=42, int b=21) { fA=a; fB=b; }
int fA, fB;
};
int main(int argc, char* argv[])
{
// 1. use the defaults
Foo foo1;
std::cout << "foo1: " << foo1.fA << " " << foo1.fB << "\n";
// 2. call the constructor explicitly
Foo foo2 = Foo(1,2);
std::cout << "foo2: " << foo2.fA << " " << foo2.fB << "\n";
// 3. call the constructor with 1 arg
Foo foo3 = Foo(5);
std::cout << "foo3: " << foo3.fA << " " << foo3.fB << "\n";
// 4. c++ does the same thing as (3)
Foo foo4 = 5;
std::cout << "foo4: " << foo4.fA << " " << foo4.fB << "\n";
// 5. c++ invents an operator= for us...
Foo foo5 = foo4;
std::cout << "foo5: " << foo5.fA << " " << foo5.fB << "\n";
// ... which copies ...
std::cout << "foo5 @ " << &foo5 << ", foo4 @ " << &foo4 << "\n";
// which is different from pointer assignment:
Foo* fooPtr1 = new Foo(3);
Foo* fooPtr2 = fooPtr1;
std::cout << "fooPtr1: " << fooPtr1->fA << " " << fooPtr1->fB << "\n";
std::cout << "fooPtr2: " << fooPtr2->fA << " " << fooPtr2->fB << "\n";
std::cout << "fooPtr1 @ " << fooPtr1 << ", fooPtr2 @ " << fooPtr2 << "\n";
return 0;
}

Saturday, June 4, 2011

Evil C++ #2: Using GCC's -ftrapv flag to debug integer overflows

In C++, overflowing an integer type won't cause an exception and can result in weird numbers propagating through your program. GCC's ftrapv flag has your back.

#include<iostream>
#include<signal.h>
#include<limits.h>
/** g++'s -ftrapv flag provides some protection against integer overflows. It
* is a little awkward to use, though. All it will do is "trap" -- you must
* provide a signal handler to deal with it.
*
* (You must compile with -ftrapv for this to work)
*/
// a simple signal handler. it must take the signal as an argument, per
// signal.h, whether we use it or not.
void handler(int /*signal*/)
{
std::cout << "Overflow'd!" << std::endl;
}
int main()
{
// when we get a SIGABRT, call handler
signal(SIGABRT, &handler);
// LONG_MAX is the largest long integer on this system, from limits.h
long a = LONG_MAX;
int b = 1;
long c = a + b;
return 0;
}
view raw ftrapv.cpp hosted with ❤ by GitHub

Thursday, June 2, 2011

Evil C++ #1: Brackets and "at" for accessing STL vector elements

This is the first in a series of code snippets that demonstrate C/C++ pitfalls.

(For an thorough explanation of the many ways C++ is out to get you, see Yossi Kreinin's excellent C++ FQA).

#include<iostream>
#include<vector>
/** There are two ways to access the ith element of an STL vector, the usual
* v[i] syntax or using v.at(i).
*
* The former doesn't check array boundaries, so something like v[v.size()+1]
* works and gives you whatever happens to be sitting in that memory location.
*
* v.at() has the same purpose, but actually throws an exception
* (std::out_of_range) when you're outside array boundaries. This may be
* helpful for debugging buffer overflows, a common source of C++ headaches.
*
* Both v[i] and v.at(i) are of constant complexity.
*/
int main()
{
std::vector<int> a(5);
// Accessing the (a.size()+1)th element with brackets returns junk
std::cout << a[6] << std::endl;
// Accessing the (a.size()+1)th element with at throws an exception
std::cout << a.at(6) << std::endl;
return 0;
}

Ignoring GCC warnings on a per-file basis

In most cases, ignoring GCC warnings is a Bad Idea. Treating warnings as errors results in better code.

However, sometimes we are forced to deal with other people's code. For instance, a project I work on relies on JsonCpp. We include this in our source tree so that every user doesn't to have to go get JsonCpp source code in order to compile this thing.

Such dependencies can be a problem if you want really strict compiler options, since libraries will often be slightly incompatible with your particular standard (ANSI, C++0x, ...) or not be up to your lofty expectations. In my case, JsonCpp gives me a couple of warnings with GCC options -W, -Wall, -ansi, -pedantic. This means I can't compile my code with -Werror, which makes me sad. I certainly don't want to modify these external libraries.

Fortunately, in recent GCC versions ways of selectively disabling warnings have been added. If your problems are confined to headers, you can replace -I/path/to/headers with -isystem/path/to/headers and GCC will treat them as system headers, ignoring warnings.

Another less-desirable solution is to use pragmas. Headers can be marked as system headers by putting at the top:

#pragma GCC system_header


If the problems lie in the source files themselves, neither of these tricks work. We can, however, add to the top of the files causing the warnings things like this:

#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Woverflow"


to disable specific warnings generated by that file.

To figure out the names of the warnings causing the problems, recompile with the -fdiagnostics-show-option option on the g++ line. This is especially useful in the case of default warnings (i.e. those which aren't optional) like -Woverflow since they are harder to find in the documentation.

This isn't a great solution, since it does require some modification of the libraries. However, you can easily generate a patch from your changes and apply it to any new library versions should you decide later to upgrade them. Hopefully someday GCC will include an "ignore warnings from this file or subdirectory" option, but until then... it works.