C++ quick trick : named constructors

January 26, 2008 at 5:13 pm 4 comments

For those still fortunate enough to program in C++, I have a little trick for you.
It’s called named constructors, something which is not really a standard feature of C++.

Normally in C++, the constructor’s name is exactly the same as the classname. The only way you can differentiate between constructors, is by overloading.

Like this

class  CBox
{

  public:

    CBox( const CPoint & p1, const CPoint & p3 ) ;

    CBox( const int left    ,
          const int top     ,
          const int right   ,
          const int bottom   ) ;

    CPoint    Point1() const { return mPoint1 ; }
    CPoint    Point3() const { return mPoint3 ; }

  private:

    CPoint   mPoint1 ;
    CPoint   mPoint3 ;

};

Suppose the first constructor, the one taking two points as an argument, makes sure that before the points are stored, they are normalized, meaning that – in a typical Cartesian coordinate system – method Point1 returns the point that is at the bottom left and Point3 returns the point that is at the top right.
This way, the CBox class always behaves in a deterministic way. For instance when you calculate the width as mPoint3 – mPoint1, you don’t have to check whether the result is negative.

As your application grows, you start adding more methods to CBox class.
Some of the methods operate on mPoint1 and mPoint3 in such a way that they do not alter the normalization of the points.

For example.


class CBox
{
  public:

    ...

      // translates the rect
    CBox    operator+( const CVector & v )
    {

      return CBox( mPoint1 + v,
                   mPoint3 + v ) ;
    }

};

As you can see, the operation cannot possible affect the normalization of the CBox, every invariant relation between the new mPoint1 and mPoint3, is identical to those of the old mPoint1 and mPoint3.
However, what happens now is that when you call the regular constructor, it will still try to normalize the points, although they are already normalized.
Probably the ctor is doing some comparisons, not a lot of work, but still, the granularity of classes like CBox is tiny, and they could be used in the context of highly critical drawing code.
So you do not want the extra comparisons, you just want to return the points with the delta added to them in a new CBox.

But still, there is only one constructor that takes two points as an argument, and that one is normalizing.
What you would like to do is call something like CNoNormalizationConstructor( … ), but alas this does not work in C++.

Hence the trick. What you do is this

class  CBox
{

  public:

    CBox( const CPoint & p1,
          const CPoint & p3 ) ;

    CBox( const int left    ,
          const int top     ,
          const int right   ,
          const int bottom   ) ;

    CPoint    Point1() const { return mPoint1 ; }
    CPoint    Point3() const { return mPoint3 ; }

    CBox   operator+( const CVector & v )
    {
      return CBox( mc_eNoNorm,
                  mPoint1 + v,
                  mPoint3 + v ) ;
    }

  private:

    enum mt_eNoNorm { mc_eNoNorm } ; 

    CBox( const mt_eNoNorm,
          const CPoint & p1,
          const CPoint & p3  )
    : mPoint1( p1 )
    , mPoint3( p3 )
    {}

    CPoint   mPoint1 ;
    CPoint   mPoint3 ;

};

You simply use the C++ overloading mechanism to make extra constructors by creating dummy types. This is as close as you will ever get to a named constructor in C++.

Advertisements

Entry filed under: Command Line.

Fast Graphical String Truncation Traffic Masking

4 Comments Add your own

  • 1. xtofl  |  February 15, 2008 at 10:42 am

    Since you need an extra type per named constructor,
    And since manually creating types takes you 1 keyword and 2 “arbitrary” names,
    And since these arbitrary names are to be kept unique within the scope,
    Why not let the compiler generate the types?

    i.e. use an enum to destinguish between the constructors, and have a constructor that is templatized and specialized per enum value:

    class CBox
    {
    enum mt_eConstructorName { mc_eNoNorm, mc_eTurnUpsideDown };

    template [ mt_eConstructorName at_Name ]
    CBox( const CPoint &, const CPoint & ) ; // to be specialized

    template[]
    CBox[ mc_eNoNorm ]( p1, p2 ) : mP1( p1 ), mP2( p2 ){};

    template{}
    CBox[ eTurnUpsideDown ]( p1, p2 ) : mP1( p2 ), mP2( p1 ){};

    CBox CBox::operator+( const Vector& v)
    {
    return CBox[ mc_eNoNorm ]( mP1 + v, mP2 + v );
    }

    };

    Reply
  • 2. qbziz  |  February 15, 2008 at 6:26 pm

    Ok. If you replace the [] with the “less-than” and “greater than” chars you should be able to figure out the idea xtofl is proposing here.
    I don’t have enough time now to do bracket-karate. I’ll get back to that.

    This is a good technique as well. A bit more complicated if you just want to have 1 extra ctor. Starts paying off when ctor domain becomes large.

    Reply
  • 3. xdotx  |  April 19, 2008 at 10:19 am

    xtofl’s solution is nicer. also it is a metaprogramming pattern called ‘tagging’.

    Reply
  • 4. qbziz  |  April 19, 2008 at 11:02 am

    I have never heard of any technique that is per se ‘nicer’, there is always a cost / benefit angle.
    It is a good technique if the problem you are addressing requires this technique. I like it a lot myself, but it requires more complicated C++ constructs ( templates, specialization ), so I wouldn’t throw it at every legacy class that needs just one or two extra ways of construction.
    However you could come up with a better design upfront if this technique is in your bag of tricks. For example a ‘Transformation’ class, that needs ways of construction like ‘Translate’, ‘Scale’, ‘Rotate’, ‘Shear’, …. Arguments for these could all be supplied by the same type ( a tuple of numbers ). So overloading wouldn’t work, and that’s were this tagging technique would come in handy.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Trackback this post  |  Subscribe to the comments via RSS Feed


Recent Posts

Categories


%d bloggers like this: