I l@ve RuBoard Previous Section Next Section

8.4 Type Identifiers

The only problem that remains is the management of type identifiers. Still, adding type identifiers requires a fair amount of discipline and centralized control. Whenever you add a new shape class, you must check all the existing type identifiers and add one that doesn't clash with them. If a clash exists, the second call to RegisterShape for the same ID fails, and you won't be able to create objects of that type.

We can solve this problem by choosing a more generous type than int for expressing the type identifier. Our design doesn't require integral types, only types that can be keys in a map, that is, types that support operator== and operator<. (That's why we can be happy we chose maps instead of vectors.) For example, we can store type identifiers as strings and establish the convention that each class is represented by its name: Line's identifier is "Line", Polygon's identifier is "Polygon", and so forth. This minimizes the chance of clashing names because class names are unique.

If you enjoy spending your weekends studying C++, maybe the previous paragraph rang a bell for you. Let's use type_info! The std::type_info class is part of the runtime type information (RTTI) provided by C++. You get a reference to a std:: type_info by invoking the typeid operator on a type or an expression. What seems nice is that std::type_info provides a name member function that returns a const char* pointing to a human-readable name of the type. For your compiler, you might have seen that typeid(Line).name() points to the string "class Line", which is exactly what we wanted.

The problem is, this does not apply to all C++ compiler implementations. The way type_info::name is defined makes it unsuitable for anything other than debugging purposes (such as printing it in a debug console). There is no guarantee that the string is the actual class name, and worse, there is no guarantee that the string is unique throughout the application. (Yes, you can have two classes that have the same name according to std::type_info::name.) And the shotgun argument is that there's no guarantee that the type name will be unique in time. There is no guarantee that typeid(Line).name() points to the same string when the application is run twice. Implementing persistence is an important application of factories, and std::type_info::name is not persistent. All this makes std::type_info deceptively close to being useful for our object factory, but it is not a real solution.

Back to the management of type identifiers. A decentralized solution for generating type identifiers is to use a unique value generator—for instance, a random number or random string generator. You would use this generator each time you add a new class to the program, then hardcode that random value in the source file and never change it.[3] This sounds like a brittle solution, but think of it this way: If you have a random string generator that has a 10-20 probability of repeating a value in a thousand years, you get a rate of error smaller than that of a program using a "perfect" factory.

[3] Microsoft's COM factory uses such a method. It has an algorithm for generating unique 128-bit identifiers (called globally unique identifiers, or GUIDs) for COM objects. The algorithm relies on the uniqueness of the network card serial number or, in the absence of a card, the date, time, and other highly variable machine states.

The only conclusion that can be drawn here is that type identifier management is not the business of the object factory itself. Because C++ cannot guarantee a unique, persistent type ID, type ID management becomes an extra-linguistic issue that must be left to the programmers.

We have described all the elements in a typical object factory, and we have a prototype implementation. It's time now for the next step—the step from concrete to abstract. Then, enriched with new insights, we'll go back to concrete.

    I l@ve RuBoard Previous Section Next Section