Borland Online And The Cobb Group Present:


September, 1994 - Vol. 1 No. 9

Borland C++ 4.0 Class Libraries - Using dictionaries in Borland C++ 4.0

In the article Borland C++ 3.1 Class libraries - Retrieving data with dictionaries and associations from last month's issue of Borland C++ Devel-oper's Journal, we showed you how to implement simple database-style lookup tables by using the Dictionary and Association classes that ship with the 3.1 version of Borland C++. Unfortunately, these classes are part of Borland's older, Object-based container class library, which means Association and Dictionary objects can contain only objects from classes that derive from the Object class.

With the release of C++ 4.0, Borland redesigned the template-based collection class library (also known as the Borland International Data Structures, or BIDS, library) to include replacements for the Dictionary and Association classes. However, there are some important differences between the old versions of these classes and the new ones. In this article, we'll show how to use the new TDictionaryAsHashTable class and the corresponding association classes.

Differences in 4.0

We've already mentioned the biggest change that Borland made in the classes that implement dictionaries and associations: moving them to the BIDS library. While this change allows you to create associations that use key/value pairs from classes that don't derive from the Object class, it also forces you to make some design decisions that the previous version of the library made for you. First, let's look at the new classes you'll use to create associations; then, we'll examine the new dictionary classes.

New associations

There are four new association class templates: TDDAssociation, TDIAssociation, TIDAssociation, and TIIAssociation. Each of these classes performs the same function­­binding a key object to a value object.

However, the second and third letters of the template names indicate whether each template's key and value elements are actual objects (D for Direct) or pointers (I for Indirect). The second letter specifies the type of key object you'll use, and the third letter specifies the type of value object.

For example, a TDIAssociation-derived association object will contain an object for its key (D), but it will contain a pointer to an object for its value (I). Likewise, a TIDAssociation-derived association object will have a pointer to an object for its key and an object for its value. Obviously, the TDDAssociation and TIIAssociation classes represent associations that use objects or pointers, respectively, for both the key and value objects.

New dictionaries

To use the new association class templates, you'll use one of the new dictionary class templates. As with the associations, there are different versions of the dictionary classes for working with association objects directly or via a pointer.

There are four dictionary class templates:

TMDictionaryAsHashTable 
TDictionaryAsHashTable 
TMIDictionaryAsHashTable 
TIDictionaryAsHashTable

You'll use the TDictionaryAsHashTable class most often, so we'll focus on this class.

From the standpoint of the class's interface, the most obvious difference between the older Dictionary class and the new TDictionaryAsHashTable class template is the Find() member function. You'll use the TDictionaryAsHashTable class's Find() member function to perform the actual lookup in the dictionary.

At first, the Find() function may seem to duplicate the action of the Dictionary class's lookup() member function. However, the lookup() function returns the found value object directly, while the Find() function returns a pointer to the association that contains the found value.

As you've probably noticed, Borland implemented all the new dictionary class templates as hash tables, just as the company implemented the older Dictionary class. One reason the Association class could contain only objects derived from the Object class was that the Dictionary class had to be able to calculate a hash value for each association it contained. To provide an interface to the Dictionary class for retrieving these values, the Object class declared a pure virtual function hashValue().

The new dictionary class templates call a global function HashValue() instead. If you create a member function for your key classes in the form

unsigned HashValue() const;

the dictionary classes will call this member function via a global template function. Instead, you can create your own global function HashValue() to calculate the correct hash value for your objects.

To see how the new dictionary and association class templates work, let's rewrite the application from last month's article. To make the differences between the new and old versions of the classes more obvious, we'll change as little of the code as possible.

Rewriting CMDLOOK.EXE

To begin, launch the Borland C++ 4.0 Integrated Development Environment (IDE). When the IDE's main window appears, choose New from the File menu and enter the code from Listing A in the data-file window.


Listing A: CMDLOOK2.CPP

#include <cstring.h>
#include "classlib\assoc.h"
#include "classlib\dict.h"
#include <iostream.h>

unsigned HashValue(const string& str)
{ return str.hash(); } // Calculates the hash
                       // value for string items 

typedef TDDAssociation<string,string> 
        StringAssociation;

int main()
{
  char key[10];

  cout << "Press a key for the quit command";
  cout << endl;
  cin >> key;
  string QuitCommand("Quit Key");
  string QuitKey(key);
  StringAssociation Quit(QuitKey, QuitCommand);

  cout << "Press a key for command #1" << endl;
  cin >> key;
  string Cmd1Command("Command #1");
  string Cmd1Key(key);
  StringAssociation Cmd1(Cmd1Key, Cmd1Command);

  cout << "Press a key for command #2" << endl;
  cin >> key;
  string Cmd2Command("Command #2");
  string Cmd2Key(key);
  StringAssociation Cmd2(Cmd2Key, Cmd2Command);

  cout << "Press a key for command #3" << endl;
  cin >> key;
  string Cmd3Command("Command #3");
  string Cmd3Key(key);
  StringAssociation Cmd3(Cmd3Key, Cmd3Command);

  TDictionaryAsHashTable<StringAssociation> 
     CmdDictionary;

  CmdDictionary.Add(Quit);
  CmdDictionary.Add(Cmd1);
  CmdDictionary.Add(Cmd2);
  CmdDictionary.Add(Cmd3);

  string LastKey;
  do
  {
    cout << "Enter a command - ";
    cin >> key;

    LastKey = string(key);

    StringAssociation temp(LastKey, "");
    StringAssociation* command = 
            CmdDictionary.Find(temp);

    if(command != 0)
    {
      cout << command->Value() << endl;
    }
    else
      cout << "Invalid command!" << endl;
  }
  while(LastKey != QuitKey);

  return 0;
}

When you finish entering the code, choose Save from the File menu. In the Save File As dialog box, enter CMDLOOK2.CPP as the filename and click OK. Before you compile and run this program, let's look at a few details in the code.

First, notice the global HashValue() function we create. Since we use objects from the string class for our keys, we can write this function to simply call the hash() member function (note the different name) of the corresponding string object.

Next, we declare the StringAssociation type using the TDDAssociation class template with the string class for both the key and value parameters. Using a StringAssociation object is somewhat equivalent to using the Association class with String objects in the previous version of the program.

To create the dictionary, we first declare the variable CmdDictionary, using the class template TDictionaryAsHashTable and the StringAssociation type as the association parameter. Unfortunately, the design of the new association class templates prevents you from using associations that contain both direct (objects) and indirect (pointers) key/value elements, because you have to specify the type of association when you create the dictionary. So, we changed the construction of the Command #2 and #3 key and value objects to make them direct (nonpointer) objects.

If you're not familiar with the syntax that we used to create the dictionary, it basically creates a new object without a formal name for the type. Normally, you'll want to name types you create from a template class by using a typedef statement as we did last month. However, we need to create only one dictionary object, so we used this shortcut.

Finally, we change the lookup loop to work correctly with the new Find() member function and to take advantage of some of the string class's behavior. For example, to create the temp variable, we used the string class's const char* conversion constructor to create a temporary object from the literal "". Then, we changed the while() statement to use the string class's overloaded inequality operator (!=).

Running the program

Right-click on the window for the file CMDLOOK2.CPP and choose Target Expert from the pop-up menu. When the TargetExpert dialog box appears, choose EasyWin from the Target Type list box and select the Class Library check box. Click OK when you're finished.

Now, choose Run from the Debug menu to build and run the program. When the program runs, you'll enter the keys for the four commands and then enter the command loop.

To quit the CMDLOOK2 program, enter the key you assigned to the Quit command. To close the EasyWin window, double-click on its System menu icon.

The value of hash

It's important that you make the parameter of the HashValue() function const. If you don't, the compiler will try to find a better match by using the class library's template function

template <class T> 
inline unsigned HashValue(const T& t)
{  return t.HashValue(); }

and will display an error when it can't find a HashValue() member function in the string class.

This is because the TDictionaryAsHashTable class calls the HashValue() function when it's searching for a matching key. To keep the function from changing the key object, the TDictionaryAsHashTable class converts the key to a const object before calling the function HashValue().

Return to the Borland C++ Developer's Journal index

Subscribe to the Borland C++ Developer's Journal


Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.