Bug Vanquisher

2 December 2007

Every Problem in Software can be Solved with Another Layer of Indirection

Filed under: C++ — Tanveer Badar @ 11:26 PM

How true! I keep rediscovering this fact over and over again.

As an example, consider the problem of virtual function invocation during object construction and destruction in C++. A virtual function resolves to the type of running constructor/destructor even if it has been overridden in derived classes. Apparently, C# and Java do not suffer from this particular ailment because their object type is always the most derived during constructions. Sadly they do not have destructors and have much worse problems than not being able to call proper virtual functions.

This limitation (actually a very good thing) stems from the fact that a constructor set vtable in the object to only its current type as it does not know what further derives from that type. Similarly, a destructor sets vtable to point to implementations in the current type because derived class sub-object has been destroyed by the time destructor for a base class runs.

Aside from this minor annoyance, it is part of a larger problem too. Pure virtual function calls fail during construction/destruction.

Therefore, how to overcome this particular obstacle. A very simple case is performing parameter validation in constructor. This cannot possible depend on state information as there is none at the moment.

The workaround is just around the corner. Introduce a parallel class hierarchy that performs validation, for the hierarchy under consideration acts as factories and pass a base class reference/pointer in constructors. Even passing state is possible through references and pointer or directly by copying if the object is copy-constructible.

Some code is in order here.

struct base
{
    base( int param )
    {
        validate( param );
    }

    virtual void validate( int value )
    {
        if( value < 0 )
            throw invalid_argument( "value cannot be less than zero." );
    }
};

struct derived : public base
{
    derived( int param ) 
        : base( param )
    {
    }

    void validate( int value )
    {
        base::validate( value );
        if( value > 5 )
            throw invalid_argument( "value cannot be greater than five." );
    }
};

This problem with this code derived( 10 ) passes validation because derived::validate is never called.

With the validation code moved to parallel class hierarchy we avoid this problem because those objects have already passed their construction phase.

struct basevalidator
{
    virtual void validate( int value )
    {
        if( value < 0 )
            throw invalid_argument( "value cannot be less than zero." );
    }
};

struct derivedvalidator : public basevalidator
{
    void validate( int value )
    {
        base::validate( value );
        if( value > 5 )
            throw invalid_argument( "value cannot be greater than five." );
    }
};

struct base
{
    base( int param )
    { 
        basevalidator( ).validate( param );
    } 

protected:
    base( int param , const basevalidator& validator )
    {
        validator.validate( param );
    }
};

struct derived : public base

    derived( int param ) 
        : base( param , derivedvaliator( ) )
    {
    }
};

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Please log in using one of these methods to post your comment:

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

Blog at WordPress.com.

%d bloggers like this: