2.4 Common Functionality Among Classes
This section describes issues that affect almost every Essential Tools Module class, including member functions, memory allocation, information flow, multithread safety, indexing, and finding the module version number.
2.4.1 Common Member Functions
Whatever their category, all classes have similar programming interfaces. This section highlights their common member functions.
2.4.1.1 Persistence Functions
The Essential Tools Module uses the following member functions to store an object of type ClassName to and from an RWFile, and to and from the Rogue Wave virtual streams facility, and to restore it later:
 
RWFile& operator<<(RWFile& file, const ClassName&);
RWFile& operator>>(RWFile& file, ClassName&);
RWvostream& operator<<(RWvostream& vstream,const ClassName&);
RWvistream& operator>>(RWvistream& vstream, ClassName&);
Class RWFile, which encapsulates lower levelC I/O API efficiently, saves/restores objects to/from files. The result is efficient storage and retrieval to files. For more information on RWFile, see Chapter 10 and the SourcePro C++ API Reference Guide.
Classes RWvistream and RWvostream are abstract base classes used by the Rogue Wave virtual streams facility. The final output format is determined by the specializing class. For example, RWpistream and RWpostream are two classes that derive from RWvistream and RWvostream, respectively. They store and retrieve objects using a portable ASCII format. The results can be transferred between different operating systems. These classes are discussed in more detail in Chapter 5 and the SourcePro C++ API Reference Guide.
You can decide whether to store to RWFiles or to Rogue Wave streams. Storing to RWFiles gives you speed, but limits portability of results to the host machine and operating system. Storing to Rogue Wave streams is not as fast, but you get several specializing classes that provide other useful features. These features include a highly portable format between different machines and XDR stream encapsulation for distributed computations.
2.4.1.2 Store Size Functions
The following common member functions return the number of bytes of secondary storage necessary to store an object of type ClassName to an RWFile:
 
RWspace ClassName::binaryStoreSize() const;
RWspace ClassName::recursiveStoreSize() const;
The member functions use the function:
 
RWFile& operator<<(RWFile& file, const ClassName&);
The above member functions are good for storing objects using classes RWFileManager and RWBTreeOnDisk. For objects that inherit from RWCollectable, the second variant recursiveStoreSize() can calculate the number of bytes used in a recursive store. The variant uses the function:
 
RWFile& operator<<(RWFile& file, const RWCollectable&)
You can use class RWAuditStreamBuffer in conjunction with any stream to count the number of bytes that pass through the buffer. RWAuditStreamBuffer thus provides the same functionality for streams that the StoreSize member functions (discussed above) provide for files. For more information on class RWAuditStreamBuffer, see the SourcePro C++ API Reference Guide.
2.4.1.3 Stream I/O Functions
The overloaded left-shift operator <<, taking an ostream object as its first argument, will print the contents of an object in human-readable form. Conversely, the overloaded right-shift operator >>, taking an istream object as its first argument, will read and parse an object from the stream in a human-understandable format.
 
ostream& operator<<(ostream& ostr, const ClassName& x);
istream& operator>>(istream& istr, const ClassName& x);
The overloaded left-shift and right-shift operators contrast with the persistence operators:
 
RWvostream& operator<<(RWvostream& vstream, const ClassName&);
RWvistream& operator>>(RWvistream& vstream, ClassName&);
Although the persistence shift operators may store and restore to and from a stream, they will not necessarily do so in a form that could be called “human-readable.”
2.4.1.4 Comparison Functions
Finally, most classes have comparison and equality member functions:
 
int compareTo(ClassName*) const;
bool equalTo(ClassName*) const;
and their logical operator counterparts:
 
bool operator==(const ClassName&) const;
bool operator!=(const ClassName&) const;
bool operator<=(const ClassName&) const;
bool operator>=(const ClassName&) const;
bool operator<(const ClassName&) const;
bool operator>(const ClassName&) const;
2.4.2 Memory Allocation and Deallocation
When an object is allocated off the heap, who is responsible for deleting it? With some libraries, ownership can be a problem.
Most of the Rogue Wave classes take a very simple approach: if you allocate something off the heap, then you are responsible for deallocating it. If the Rogue Wave library allocates something off the heap, then it is responsible for deallocating it.
There are two exceptions for creation of objects. The first exception involves the operators:
 
RWFile& operator>>(RWFile& file, RWCollectable*&);
RWvistream& operator>>(RWvistream& vstream, RWCollectable*&);
These operators restore an object inheriting from RWCollectable from an RWFile or RWvistream, respectively. They return a pointer to an object allocated off the heap: you are responsible for deleting it.
The second exception is member function:
 
RWCollection* RWCollection::select(RWtestCollectable,
void*)const;
This function returns a pointer to a collection, allocated off the heap, with members satisfying some selection criterion. Again, you are responsible for deleting this collection when you are done with it.
There is also an exception for object deletion: As a service, many of the collection classes provide a method, clearAndDestroy(), which will remove all pointers from the collection and delete each. Even with clearAndDestroy(), however, it is still your responsibility to know that it is safe to delete all the pointers in that collection.
These methods are documented in detail in the SourcePro C++ API Reference Guide.
2.4.3 Information Flow
With the Rogue Wave libraries, information generally flows into a function via its arguments and out through a return value. Most functions do not modify their arguments. Indeed, if an argument is passed by value or as a const reference:
 
void foo(const RWCString& a)
you can be confident that the argument will not be modified. However, if an argument is passed as a non-const reference, you may find that the function will modify it.
If an argument is passed in as a pointer, there is the strong possibility that the function will retain a copy of the pointer. This is typical of the collection classes:
 
RWOrdered::insert(RWCollectable*);
The function retains a copy of the pointer to remind you that the collection will be retaining a pointer to the object after the function returns.
An alternative design strategy would be to pass objects that are to be inserted into a collection by reference, as in The NIH Classes. We rejected this approach for two reasons: it looks so similar to pass-by-value that the programmer could forget about the retained reference, and it becomes too easy to store a reference to a stack-based variable.
2.4.4 Multithread Safety
Rogue Wave defines three levels of thread safety, depending on how a class is implemented. These include unsafe, safe, and multithread-safe, as described below.
*Unsafe (MT-0) -- An MT-0 function, class, or library is not safe to use in a multithreaded application unless the application arranges for only one thread at a time to access or execute within that function, class or library. Unsafe functions, classes or libraries often contain either global or static data that is not protected, or make use of functions or classes that are not safe.
*Safe (MT-1) -- MT-1 code is reentrant. An MT-1 function, class, or library can be used in a multithreaded application, but may not be safely accessed by more than one thread at a time. If the code has any undocumented or private shared, global, or static data, it will automatically guard that data even across thread boundaries, so its behavior will be as expected: class instances will "act like an int" even in a multithreaded environment. Of course, if you share MT-1 objects between threads, you are responsible for avoiding race conditions.
*MT-Safe (MT-2) -- An MT-2 function, class, or library is fully prepared for multithreaded access and execution. MT-2 objects are reentrant, protect their internal global or static data, and ensure that methods that access member data are protected. An MT-2 class or library implies that individual operations can be performed safely without external user locking and unlocking. However, an MT-2 class or library might still require the user to perform external synchronization or locking in situations where several individual operations must be combined and treated as a single atomic operation (for example, testing for, and reading the contents of a queue).
2.4.4.1 Assigning Thread Safety Levels to Classes and Libraries
When a class or library is assigned a thread safety level, it is possible that one or more individual members will differ from the classification of the whole. In the event that a member of a class or library is less thread-safe than its parent, this difference will be clearly documented. In addition, we’ll make every effort to document any thread-safe exceptions in a summary discussion of the class or library.
2.4.4.2 Thread Safety in the Essential Tools Module
Unless otherwise specified, all classes in the Essential Tools Module are assigned a thread safety level of MT-1. All of these classes perform sufficient internal locking on global and static data to ensure that they behave correctly when used in a multithreaded environment; however they also require that the user provide external locking around operations on objects that are shared between multiple threads.
2.4.5 Indexing
Indexes have type size_t, an unsigned integral type defined by your compiler, usually in <stddef.h>. Because size_t is unsigned, it allows 2n minus one, where n is the number of bits the size_t type has on your system.
Invalid indexes are signified by the special value RW_NPOS, defined in <rw/defs.h>.
2.4.6 Version
When programming, you may need to know the specific version number of the Essential Tools Module to perform certain operations. This number is given by the macro RWTOOLS, expressed as a hexadecimal number. For example, version 1.2.3 would be 0x123. This can be used for conditional compilations.
If the version is needed at run time, you can find it via the function rwToolsVersion(), declared in header file <rw/tooldefs.h>.