FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
C++
Email
Print
Reprint

add to:
Del.icio.us
Digg
Google
Furl
Slashdot
Y! MyWeb
Blink
October 22, 2007

C++ Hash Table Memoization: Simplifying Dynamic Programming

(Page 4 of 7)

Sample Code for gcc

gcc puts the hashing header files in the ext folder, and uses the __gnu_cxx namespace, isolating the non-standard library extensions into a ghetto of sorts. Both of these are dealt with in the first two lines of the sample code below.

The gcc implementation of hash_map needs two template parameters to handle keys that aren't defined as built-in types: one class that has an operator which returns a hash index given an input key, and a second that returns a boolean value for equality test. I pack both of these into a single class, SampleTraits. The traits class is then passed to the hash_map type definition as the third and fourth template parameters.

When you are defining the hash function, you need to somehow combine hash values for each of the members of your structure. Creating hash keys is somewhat of an art, so I try to use the ones provided by the library writer as the basis for my hash function. In this case I take the hash values for the two members of class Sample and simply XOR them together, which should provide a decent value.

If you are using g++ 4.x, you may be better off using the TR1 unordered_map class. While it is not quite part of the standard yet, it should be soon, and that will insure its future support and compatibility.

#include <ext/hash_map> 
using namespace __gnu_cxx; 
#include <string> 
class Sample { 
public : 
   Sample( int age=-1, const std::string &genre="" ) 
   : mAge( age ) 
   , mGenre( genre ){} 
   int mAge; 
   std::string mGenre; 
}; 
struct SampleTraits 
{ 
   size_t operator()( const Sample& that ) const 
   { 
      return hash<int>()( that.mAge ) ^ 
        hash<const char *>()( that.mGenre.c_str() ); 
   } 
   bool operator()( const Sample &that1, const Sample& that2 ) const 
   { 
      return that1.mAge == that2.mAge && that1.mGenre == that2.mGenre; 
   } 
}; 
int main() 
{ 
   hash_map<Sample, 
      int, 
      SampleTraits, 
      SampleTraits< AnnualBooksReadByAgeAndGenre; 
      AnnualBooksReadByAgeAndGenre[ Sample( 15, "Fantasy" ) ] = 15; 
      AnnualBooksReadByAgeAndGenre[ Sample( 18, "Sports" ) ] = 12; 
      AnnualBooksReadByAgeAndGenre[ Sample( 35, "Mystery" ) ] = 17; 
      AnnualBooksReadByAgeAndGenre[ Sample( 42, "Romance" ) ] = 125; 
   return 1; 
} 

Visual C++ .NET 2003/2005

Microsoft's compilers have similar issues, but deal with them differently. (That's the problem with non-standard features.)

Although the hash_map header file is accessed from the normal C++ header folder, the class itself, as was the case with g++, is defined in a different namespace, stdext. Again, that is dealt with in the first two lines of the sample code.

The remaining two issues are resolved by defining a global hash_value class that has an operator that returns a hash key for a given object of class Sample, and a global comparison operator that is used to test for equality of two objects of class Sample. (It is not unusual to use the less-than operator to test for equality, by testing both a > b and a < b, we determine whether the objects are equal or not.)

#include  
using namespace stdext; 
#include <string> 
class Sample { 
public : 
  Sample( int age=-1, const std::string &genre="" ) 
    : mAge( age ) 
    , mGenre( genre ){} 
    int mAge; 
    std::string mGenre; 
}; 
   namespace stdext { 
     size_t hash_value( const Sample& that ) 
     { 
        return hash_value( that.mAge ) ^ hash_value( that.mGenre ); 
     } 
}; 
bool operator<( const Sample& that1, const Sample& that2) 
{ 
    return that1.mAge <that2.mAge && that1.mGenre <that2.mGenre; 
} 
int main() 
{ 
    hash_map<Sample,int> AnnualBooksReadByAgeAndGenre; 
    AnnualBooksReadByAgeAndGenre[ Sample( 15, "Fantasy" ) ] = 15; 
    AnnualBooksReadByAgeAndGenre[ Sample( 18, "Sports" )  ] = 12; 
    AnnualBooksReadByAgeAndGenre[ Sample( 35, "Mystery" ) ] = 17; 
    AnnualBooksReadByAgeAndGenre[ Sample( 42, "Romance" ) ] = 125; 
    return 1; 
} 

Some Notes on Usage

The member functions of most implementations of hash_map will be identical to those of the standard std::map container. Storing an entry in a hash_map can be done using an overloaded operator that makes the container look like an associative array:

hash_map<std::string,int>my_map; 
 ... 
my_map[ "foo" ] = 42; 

Looking up a value from the table is done using a call to hash_map::find(). This method takes a reference to a key as its argument, and returns an iterator which points to the end of the container on failure, or to a key/value pair on success. The pair is stored in an std::pair object, which allows you to access the elements with the first and second members of that object:

hash_map<std::string,int>::iterator ii = my_map.find( "bar" ); 
if ( ii == my_map.end() ) 
    std::cout <<"Couldn't find an entry for 'bar'\n"; 
else 
    std::cout <<"Entry in map for key '" 
              <<ii->first 
              <<" is " 
              <<ii->second 
              <<"\n"; 
Previous Page | 1 Introduction | 2 DP Problems | 3 Hash Table Refresher | 4 Sample Code for gcc | 5 A Weightier Example | 6 The Dynamic Programming Solution | 7 The Solution and the Test Program Next Page
TOP 5 ARTICLES
No Top Articles.



MICROSITES
FEATURED TOPIC

ADDITIONAL TOPICS

INFO-LINK