Equality between different types

If you allow different types to be equal, be very careful that the invariant still holds: if X== Y, then Y==X. Color spaces are an excellent example of equality between different types. The following description explains how assignment works between color space objects of different classes: apply the same sort of conversion lessons to equality.

The Taligent Application Environment supports many color spaces: RGB, XYZ, Gray, and so on. In addition, developers can add new color spaces. Colors can be assigned to each other, even if they are different subclasses of TColor, the abstract base class of all colors. They can also be constructed from each other, effectively converting color spaces. Using colors like value-based objects makes them very developer friendly. For example:

      TRGBColor rgb( .5, .5, .1);
      TRGBColor rgb2( rgb );          // Plain copy construction
      rgb2 = rgb;                     // Plain assignment
      TXYZColor xyz( rgb );           // Converting construction
      xyz = rgb;                      // Converting assignment
NOTE Clients need to note that color conversion can lose precision and is therefore, in general, not reversible; for example, converting an RGB to a GrayColor and back results in a gray RGB color.

Implementation

The trick is to define pure virtual casting operators in TColor which convert any given subclass to a TXYZColor. Any color subclass, therefore, has to know how to convert itself to the canonical XYZ color space.

Also, any color subclass should have a constructor that takes a single TXYZColor argument (unfortunately, this cannot be enforced with pure virtuals). Therefore, subclasses have to know how to do the conversion the other way around (from XYZ to their own type). Once these two things are in place, you can define a pure virtual assignment operator in TColor:

    virtual TColor& operator=( const TColor& other ) = 0;
Its implementation in a typical subclass might look like this:

      TColor& THLSColor::operator=( const TColor& other )
      {
          if( typeid(other) == typeid(*this) )    // Fake RTTI calls
              *this = (const THLSColor&)other;    // Use our assignment
          else
              *this = THLSColor( other );         // Convert and use our assignment
      }
With real RTTI, typeid(*this) can be replaced by typeid(THLSColor).

The optional type check improves efficiency by using a straight (nonconverting) assignment if the two objects are of the same TColor subclass. The else clause deals with polymorphic assignment: the argument is converted to a TXYZColor by the arguments override of the XYZ casting operator. That XYZ color is then passed to the XYZ color constructor of THLSColor. This way, the other color is first up-converted to XYZ (the canonical color space) and subsequently down-converted into the target color space. These conversions aren't necessarily cheap (they can involve matrix multiplies, and so on).

Color subclasses must also have a monomorphic assignment operator
(see "When to use virtual assignment" on page 100).

    THLSColor& THLSColor::operator=( const THLSColor& );
You should call this one from the polymorphic assignment implementation, as in the previous example. This avoids having the same thing implemented in
two places.

      // Temporary until RTTI support
      #define typeid(x) *((x).GetMetaInformation()->GetClassNameAsToken())
      
      // TColor base class
      class TColor : {
      ...
          virtual TColor& operator=( const TColor& other ) =0;
          virtual operator TXYZColor() const = 0;
      ...
      }
      
      // an example color subclass
      class THLSColor : public TColor {
      ...
          THLSColor( const TXYZColor& other );                // converting ct
          THLSColor( const THLSColor& );                      // monomorphic copy constructor
          virtual TColor& operator=( const TColor& other );   // polymorphic assignment
          virtual operator TXYZColor() const;                 // conversion operator
      ...
          THLSColor& operator=( const THLSColor& other );     // monomorphic assignment
      ...
      }


[Contents] [Previous] [Next]
Click the icon to mail questions or corrections about this material to Taligent personnel.
Copyright©1995 Taligent,Inc. All rights reserved.

Generated with WebMaker