C++ Gotcha
If you don’t care a bit about programming, then skip this.
Stephen Dewhurst wrote an excellent book titled “C++ Gotchas”. I’ve read it twice already, and was just re-looking through a few things again last night. One of them actually hit me today, which I find quite ironic.
I have a small piece of code that looks like this:
try {
return linearConversion( getRawValue(device), d->devicedata[device].cal );
} catch (Gina::InvalidDevice &) {
return getValue(device);
}
The premise of the code is to attempt to perform a conversion on a raw data value. If however, the raw data value doesn’t exist, then we return a regular data value instead.
This line is the culprit:
return linearConversion( getRawValue(device), d->devicedata[device].cal );
When the program executes getRawValue(device), it attempts to first determine if the device is valid. It does this by looking up to see if it exists in the devicedata list. If it doesn’t exist, this thing throws an exception - otherwise, it runs the linearConversion function and passes in the calibration (also stored in devicedata).
But here’s what interesting. C++ has no requirement on the order of evaluation for function arguments. For example:
doSomethingFancy( calculateA(), calculateB())
In this case, you cannot know whether calculateA or calculateB will be executed first. Granted, they have to both be executed before you can run doSomethingFancy(), but the order in which they are executed is picked by the compiler. It doesn’t happen left to right.
That was what was biting me before:
return linearConversion( getRawValue(device), d->devicedata[device].cal );
I was using getRawValue() as a determination of whether or not we could continue; however, in some cases d->devicedata[device].cal was being executed first. d->devicedata[device] didn’t already exist in the list, and my getRawValue() checks specifically for it, but since it was being executed first C++ was adding it to the list - causing getRawValue() to not fail anymore. Yikes.
November 9th, 2006 at 3:35 pm
note to self: when caleb says skip it, it’s probably worth skipping
November 9th, 2006 at 6:10 pm
And if you use OpenMP-type stuff, you can end up with cases that calculateA() and calculateB() both run at the SAME time.
November 10th, 2006 at 5:30 am
The C language does not specify the evaluation order either. BTW, exceptions are to be caught via *const* reference.
November 10th, 2006 at 3:20 pm
Thanks for the reminder. I knew this, but like you I don’t always remember it.
I just wish it was subtle things like this that were the cause of my of my errors…
November 13th, 2006 at 4:42 pm
For one there is no such thing as a const reference. It’s a reference-to-const. If you think it makes no difference then try talking about pointers.
January 27th, 2007 at 12:51 pm
is this really a gotcha? it seems like careless programming to me.