Essential Tools Module User's Guide : Chapter 12 Advanced Topics : RWStringID : Implementation Details of RWStringID
Implementation Details of RWStringID
The next few sections cover implementation details of RWStringID. If you are curious about how we manage to provide virtual functionality without adding virtual methods, or if you are interested in issues of design, efficiency, and other specifics, these sections are for you.
Automatic RWClassIDs
Automatic RWClassIDs are created in a systematic way from unused RWClassIDs in the range 0x9200 to 0xDAFF. There are 18,687 possible such RWClassIDs, so only extraordinary programs can possibly run out. However, we are used to dealing with extraordinary customers, so we feel we must warn you: you will not be able to build and use more than 18,687 different classes with automatically generated RWClassIDs in any one program. 16-bit DLLS will also accumulate automatic RWClassIDs while they are loaded in memory
Note that this implies nothing about the total number of objects of each class that you may have. That number is limited only by the requirements of your operating system and compiler. Of course, you also have access to the full set of RWClassIDs below 0x8000—that is, 32,767 more possible RWCollectable s—but they will not be automatically generated. You must specify them manually.
Implementing Virtuals Via Statics
Since the virtual method isA() returns a “runtime unique” RWClassID, we can use this one virtual method to provide an index into a lookup table where various data or function pointers are stored. (This may remind you of C++ built-in vtables!) Since RWCollectables already depend on the existence of a single RWFactory, we chose to use that RWFactory instance to hold the lookup information.
The static method:
 
RWStringID RWCollectable::stringID(RWClassID id);
will attempt to look up id in the RWFactory instance. If it succeeds in finding an associated RWStringID, it will return it. Otherwise, it will return RWStringID("NoID").
The static method:
 
RWClassID RWCollectable::classID(RWStringID sid)
works in an analogous manner, looking in the RWFactory instance to see if there is an RWClassID associated with sid. If the method finds one, it returns it; otherwise, it returns RWClassID __RWUNKNOWN.
Polymorphic Persistence
Polymorphic persistence of RWCollectables is not affected by the addition of the new class RWStringID. Existing files can still be read using newly compiled and linked executables, as long as the old RWClassIDs are unchanged. New classes that have RWStringIDs may be freely intermixed with old classes. The storage size of collectables that do not have permanent RWClassIDs will reflect their larger space requirements, but the storage size of other RWCollectables will be unaffected.
Note that collections containing RWCollectables with the same RWStringID have that RWStringID stored into a stream or file only once, just as multiple references to the same RWCollectable are only stored the first time they are seen.
Efficiency
Since RWClassID is more efficient in both time and space than RWStringID, you may wish to continue using it wherever possible. RWStringIDs are useful:
For organizations that need to generate unique identifiers for many programming groups;
For third party libraries that need to avoid clashes with other libraries or users;
Anywhere the self-documenting feature of RWStringID adds enough value to compensate for its slight inefficiencies.
RWStringIDs are generated for all RWCollectable classes that are compiled under the current version of the Essential Tools Module. This additional code generation has only minor impact on programs that do not use the RWStringIDs. The RWFactory will be larger, to hold lookups from RWClassID and RWStringID; and startup time will be very slightly longer, to accommodate the addition of the extra data to the RWFactory.
Identification Collisions
While RWStringID can help alleviate identification collisions, the possibility of collisions between RWStringIDs of different classes still exists. Collisions can occur:
When an automatically generated RWStringID conflicts with a user-chosen one;
When one or more classes are accidentally assigned the same RWStringID;
When two classes in different namespaces have the same name and thus the same automatically generated RWStringID. This assumes your compiler supports namespaces.
In some cases, collisions like these will be unimportant. Automatically generated RWClassIDs are guaranteed to be distinct from one another and from any legal user-provided RWClassID. The virtual isA() method, the stringID() method, and constructor lookup based on the RWClassID will all continue to work correctly.
There will be some situations, however, where collisions will cause difficulty. Polymorphic persistence of classes with user-chosen RWStringIDs that collide will not work correctly. In these cases, the data will not be recoverable, even though it is stored correctly. Similarly, user code that depends on distinguishing between classes based only on their RWStringIDs will fail.
As a developer, you can work to avoid such collisions. First of all, you should use an RWStringID which is unlikely to collide with any other. For instance, you might choose RWStringIDs that mimic the inheritance hierarchy of your class, or that imbed your name, your company's name, a creation time, or a file path such as found in revision control systems. And of course, you should always test your program to insure that the class actually associated with your RWStringID is the one you expected.