Get Up 8

Erasing Elements from a Map: A Mini-Pattern

It's a reality of programming in C++ that all containers are not created equal, and do not receive equal protection under the algorithms. Or something.

Today at work, for example, I needed to scan a std::map, and erase elements from it for which some predicate was true. The typical way to remove an element from a map is to use the map's erase method. There is an erase method that takes a range (a start and end iterator), but no way to perform the erase based on the result of a predicate. That is, there's no map::erase_if.

For sequence containers, there exists an algorithm, std::remove_if(start, end, pred), but it operates by reordering the elements of the range it's operating on, by assigning an element from one position to another with operator=. Because the first element of the pair that comprises a map::value_type, the key, is const, it can't be assigned to.

    
typedef std::map<T,U> Map;
typedef Map::value_type Value;
extern bool predicate(const Value &v);

Map m;

// Wrong: compile error.
std::remove_if(m.begin(), m.end(), std::ptr_fun(predicate)); 
    

The next obvious thing to try is an explicit loop, with an explicit branch based on the predicate. The naïve implementation, though, will have problems.

    
for(Map::iterator i = m.begin(); i != m.end(); i++) {
    if(predicate(*i))
        m.erase(i); // ONOZ!
}
    

ONOZ! because after the call to m.erase(i), i is no longer valid: it refers to memory that has been released. I suspect the increment clause of the for-statement puts us in nasal demon territory, but I'm not sure what the standard says.

To be honest, I knew the above constructs were wrong, but I didn't know if there was an idiom to get the behavior I wanted. After some research and some experimenting, the following is the construct I decided to use.

    
for(Map::iterator i = m.begin(); i != m.end(); /* increment in body */ ) {
    Map::iterator current = i++;
    if(predicate(*current))
        m.erase(current);
}
    

Thu, 21 Feb 2008 22:41

ICU4C, FreeBSD 5.3, U_REGEX_MISMATCHED_PAREN: A Fix

I've come up with a fix for the U_REGEX_MISMATCHED_PAREN problem in ICU running on FreeBSD 5.3. I removed an entire type, and some casts to that type. I've tested on FreeBSD, Red Hat 9, and Ubuntu Dapper Drake, all passing the included tests with flying colors.

I haven't figured out why yet, but the cast of doOpenNonCaptureParen to EParseAction at i18n/regexcmp.cpp:345 was yielding 0. While digging in to this, I discovered that EParseAction was only used as the type for the single parameter to RegexCompile::doParseActions. (It's used in rbbiscan.cpp, too, but that's unrelated code, with a separate definition of the enum.) To reduce complexity, I tried passing a int32_t to doParseActions and stripping out the casts. That did the trick.

I assume that in 3.8, there will be a more comprehensive solution, but for now, this works.

The patches:

Mon, 11 Dec 2006 19:01