farblog

by Malcolm Rowe

std::verbosity

I’ve been doing some C++ programming recently, which unsurprisingly has involved using the C++ STL. The STL is great for collection classes, but it keeps trying to suck me into trying out its functional-programming parts, and every time, I end up regretting it.

The problem I find, again and again, is that STL functional-programming code takes longer to write, and is less clear to read, than the equivalent procedural code. The reason it takes me longer to write is because of the verbosity of the error messages: a 200-character error message is not uncommon, and it’s really hard to read.

To illustrate the ‘harder to read’ part, here’s an example of what I ended up with today. I had a vector of pairs of integers (vector<pair<int, int> >), and I wanted to find the first element where the first number of that element’s pair was greater than a particular value. (So for the pairs (1, 10), (2, 20), (4, 40), I wanted a search for the value ‘3’ to return the last pair.)

I started out using upper_bound, which implements ‘find first element with value greater-than x’ using a binary search, and tried to adapt it to be able to check inside the pair objects, but no go: while you can specify a custom comparator, the value type of the iterators has to match the value type of what you’re searching for.

I suppose I might have been able to add a dummy pair object containing the value I was searching for, but the performance wasn’t critical, so I switched to using find_if instead, which implements a linear search for the first item that matches a given unary predicate. Then I just needed a predicate that would be true when the first number in the candidate pair was greater than the value that I was searching for. Here’s what I ended up with:

vector<pair<int, int> >::const_iterator it =
    find_if(vec.begin(),
            vec.end(),
            compose1(bind2nd(greater<int>(), search_value),
                     select1st<pair<int, int> >()));

Crystal-clear, I’m sure you’ll agree. It does also, actually, work, but then, so does this:

vector<pair<int, int> >::const_iterator it;
for (it = vec.begin(); it != vec.end(); ++it)
  if (it->first > search_value)
    break;

Both versions are functionally equivalent. Guess which version I’m now using?


Addendum: Regarding inscrutable C++ error messages, David Glasser pointed me towards a short paper called “Searching for Type-Error Messages” which describes a method for improving (type-related, not syntax) error messages by first finding a valid program that is ‘close’ in input to an invalid program, and then suggesting the necessary change as part of the error message. Most of the paper is devoted to a Caml implementation, but there’s also a short section on a C++ prototype.