How can I remove dynamic_cast and if statements from this code snippet?

Discussion in 'C++' started by Chris Stankevitz, Nov 16, 2011.

  1. Hello,

    I would like to remove the "dynamic_cast" and "if" statements from the
    code below. I believe some people would describe this as making the
    code "polymorphic". Can you recommend a way to do this without
    modifying the original classes in "Library A"?

    My intention is to create an "XML Writer" class(es) for shapes without
    putting "XML code" in the base classes. I plan to use a similar
    pattern for drawing and other tasks my application has to perform on
    Shape objects.

    Thank you,

    Chris

    // Library A
    struct Shape { virtual ~Shape() {} };
    struct Circle : public Shape { float radius; };
    struct Square : public Shape { float edge; };

    // Library B
    #include <iostream>

    class XmlWriter
    {
    static void write(Shape* shape)
    {
    if (Circle* circle = dynamic_cast<Circle*>(shape))
    {
    std::cout << "<Circle Radius=" << circle->radius << "/>";
    }
    else if (Square* square = dynamic_cast<Square*>(shape))
    {
    std::cout << "<Square Edge=" << square->edge << "/>";
    }
    }
    };
    Chris Stankevitz, Nov 16, 2011
    #1
    1. Advertising

  2. Chris Stankevitz

    Ian Collins Guest

    Re: How can I remove dynamic_cast and if statements from this codesnippet?

    On 11/17/11 07:37 AM, Chris Stankevitz wrote:
    > Hello,
    >
    > I would like to remove the "dynamic_cast" and "if" statements from the
    > code below. I believe some people would describe this as making the
    > code "polymorphic". Can you recommend a way to do this without
    > modifying the original classes in "Library A"?
    >
    > My intention is to create an "XML Writer" class(es) for shapes without
    > putting "XML code" in the base classes. I plan to use a similar
    > pattern for drawing and other tasks my application has to perform on
    > Shape objects.
    >
    > Thank you,
    >
    > Chris
    >
    > // Library A
    > struct Shape { virtual ~Shape() {} };
    > struct Circle : public Shape { float radius; };
    > struct Square : public Shape { float edge; };
    >
    > // Library B
    > #include<iostream>
    >
    > class XmlWriter
    > {
    > static void write(Shape* shape)
    > {
    > if (Circle* circle = dynamic_cast<Circle*>(shape))
    > {
    > std::cout<< "<Circle Radius="<< circle->radius<< "/>";
    > }
    > else if (Square* square = dynamic_cast<Square*>(shape))
    > {
    > std::cout<< "<Square Edge="<< square->edge<< "/>";
    > }
    > }
    > };


    Something like:

    struct Shape {
    virtual void write() = 0;
    };
    struct Circle : public Shape {
    float radius;
    void write()
    {
    std::cout<< "<Circle Radius="<< circle->radius<< "/>";
    }
    };

    struct Square : public Shape {
    float edge;
    void write()
    {
    std::cout<< "<Square Edge="<< square->edge<< "/>";
    }
    };

    class XmlWriter
    {
    static void write(Shape* shape)
    {
    shape->write();
    }
    };

    --
    Ian Collins
    Ian Collins, Nov 16, 2011
    #2
    1. Advertising

  3. On Nov 16, 10:52 am, Ian Collins <> wrote:
    > Something like:
    >
    > struct Shape {
    >    virtual void write() = 0;};


    Ian,

    I prefer/require a solution in which the shapes themselves are not
    writing out XML -- I want the XML related code restricted to the
    "XmlWriter" class in "Library B". This appears to be an overly
    draconian requirement in this simple example, but should make more
    sense if you consider adding something like a "drawing" or "network"
    class that would introduce dependencies on drawing or socket code.

    I want drawing/xml/socket code relegated to "Library B" without
    compile or link dependencies in "Library A".

    Thank you,

    Chris
    Chris Stankevitz, Nov 16, 2011
    #3
  4. Chris Stankevitz

    red floyd Guest

    Re: How can I remove dynamic_cast and if statements from this codesnippet?

    On 11/16/2011 11:14 AM, Chris Stankevitz wrote:
    > On Nov 16, 10:52 am, Ian Collins<> wrote:
    >> Something like:
    >>
    >> struct Shape {
    >> virtual void write() = 0;};

    >
    > Ian,
    >
    > I prefer/require a solution in which the shapes themselves are not
    > writing out XML -- I want the XML related code restricted to the
    > "XmlWriter" class in "Library B". This appears to be an overly
    > draconian requirement in this simple example, but should make more
    > sense if you consider adding something like a "drawing" or "network"
    > class that would introduce dependencies on drawing or socket code.
    >
    > I want drawing/xml/socket code relegated to "Library B" without
    > compile or link dependencies in "Library A".
    >



    You are somewhat correct here. Have the "write" function return
    a string which the XML code can display. By divorcing the I/O from
    the representation return, you're more general anyways.

    BTW, what book are you reading? This fragment/problem is actually
    one of the canonical OOP examples.

    class Shape {
    public:
    //...
    virtual std::string as_string() = 0;
    }

    class Square {
    //...
    std::string as_string() { return "Edge..." };
    }

    class
    red floyd, Nov 16, 2011
    #4
  5. On Nov 16, 12:03 pm, red floyd <> wrote:
    > You are somewhat correct here.  Have the "write" function return
    > a string which the XML code can display.  By divorcing the I/O from
    > the representation return, you're more general anyways.


    Red,

    Thank you for your reply. I believe you have attempted to solve my
    problem by modifying the original shapes to return their "XML
    components" as strings.

    Unfortunately this is not what I am interested in because it does not
    scale to what I really want to do. Instead of writing a long-winded
    response that might not come across correctly, allow me to change my
    original question to use drawing instead of string writing:

    ===

    Is it possible in C++ to modify "Library B" below to eliminate the
    dynamic_cast and switch statements, while at the same time not doing
    any of the following:
    - Do not use DeviceContext in "Library A"
    - Do not put drawing code in "Library A"
    - Do not put the concept of drawing into "Library A" including adding
    a class Shape::GetPixelsToDraw

    My goal is to
    a) not put any reference to drawing into Library A
    b) not use dynamic_cast or switch/if blocks in Library B

    The answer might be something like "use factories" or "use template"
    or "this is not possible in c++".

    Thank you,

    Chris


    // Library A
    struct Shape { virtual ~Shape() {} };
    struct Circle : public Shape { float radius; };
    struct Square : public Shape { float edge; };

    // Library B

    #include <cmath>
    struct DeviceContext { void FillPixel(int PixelX, int PixelY) {}; };

    class Drawer
    {
    static void write(Shape* shape, DeviceContext& Dc)
    {
    if (Circle* circle = dynamic_cast<Circle*>(shape))
    {
    for (float Angle = 0; Angle < 2*3.14156; Angle += 0.1)
    {
    Dc.FillPixel(cos(Angle) * circle->radius, sin(Angle) * circle-
    >radius);

    }
    }
    else if (Square* square = dynamic_cast<Square*>(shape))
    {
    Dc.FillPixel(0, 0);
    Dc.FillPixel(square->edge, 0);
    Dc.FillPixel(square->edge, square->edge);
    Dc.FillPixel(0, square->edge);
    }
    }
    };
    Chris Stankevitz, Nov 16, 2011
    #5
  6. Chris Stankevitz

    Ian Collins Guest

    Re: How can I remove dynamic_cast and if statements from this codesnippet?

    On 11/17/11 08:14 AM, Chris Stankevitz wrote:
    > On Nov 16, 10:52 am, Ian Collins<> wrote:
    >> Something like:
    >>
    >> struct Shape {
    >> virtual void write() = 0;};

    >
    > Ian,
    >
    > I prefer/require a solution in which the shapes themselves are not
    > writing out XML -- I want the XML related code restricted to the
    > "XmlWriter" class in "Library B". This appears to be an overly
    > draconian requirement in this simple example, but should make more
    > sense if you consider adding something like a "drawing" or "network"
    > class that would introduce dependencies on drawing or socket code.
    >
    > I want drawing/xml/socket code relegated to "Library B" without
    > compile or link dependencies in "Library A".


    Well you have to look seriously at whether a shape knows how to write
    its self, or an XmlWriter knows how to write a shape. If it's the
    latter, you gave to provide a means for an XmlWriter to access the
    required information. This can lead to significant complexity and
    coupling in your design.

    It gets more complex still if you consider drawing a shape.

    --
    Ian Collins
    Ian Collins, Nov 16, 2011
    #6
  7. On Nov 16, 12:35 pm, Leigh Johnston <> wrote:
    >         virtual void write(const serializer& writer) const



    Leigh,

    Thank you for your reply. I believe you have effectively modified the
    base class to return the XML components as strings. This is not what
    I am attempting to do as it requires me to modify the base class to
    assume it will be serialized. I'm looking for an approach in which the
    base class is not modified or at least is modified only in a way that
    allows a Factory or Template or some other technique to give shape
    details to another library. I do not want to modify the base class to
    share "serializing" details. I try to explain this a little better in
    my reply to Red.

    Thank you,

    Chris
    Chris Stankevitz, Nov 16, 2011
    #7
  8. On Nov 16, 1:37 pm, Ian Collins <> wrote:
    > Well you have to look seriously at whether a shape knows how to write
    > its self, or an XmlWriter knows how to write a shape.  If it's the
    > latter, you have to provide a means for an XmlWriter to access the
    > required information.


    Yes, it is the latter that I would like to do.

    > This can lead to significant complexity


    Yes, I am looking for complexity and am curious if it is possible to
    do this with c++.

    > It gets more complex still if you consider drawing a shape.


    Yes, I am trying to draw Shapes without Shapes knowing how to draw
    themselves. Is it possible to do this in c++ without using
    dynamic_cast or lots of switch/if statements?

    Thank you,

    Chris
    Chris Stankevitz, Nov 16, 2011
    #8
  9. On Nov 16, 1:39 pm, Leigh Johnston <> wrote:
    > struct DrawableCircle : Circle, Drawable


    Unfortunately the object that creates my shapes cannot create
    DrawableShapes as that library has no dependency on or knowledge of
    the concept of drawing. But thank you for continuing to attempt to
    help me.

    Chris
    Chris Stankevitz, Nov 16, 2011
    #9
  10. Chris Stankevitz

    Ian Collins Guest

    Re: How can I remove dynamic_cast and if statements from this codesnippet?

    On 11/17/11 10:41 AM, Chris Stankevitz wrote:
    > On Nov 16, 1:37 pm, Ian Collins<> wrote:
    >> Well you have to look seriously at whether a shape knows how to write
    >> its self, or an XmlWriter knows how to write a shape. If it's the
    >> latter, you have to provide a means for an XmlWriter to access the
    >> required information.

    >
    > Yes, it is the latter that I would like to do.
    >
    >> This can lead to significant complexity

    >
    > Yes, I am looking for complexity and am curious if it is possible to
    > do this with c++.
    >
    >> It gets more complex still if you consider drawing a shape.

    >
    > Yes, I am trying to draw Shapes without Shapes knowing how to draw
    > themselves. Is it possible to do this in c++ without using
    > dynamic_cast or lots of switch/if statements?


    The problem has little to do with C++. The issue of who knows how to do
    what is common to any language.

    The literal answer to your question is yes. You can always replace
    conditionals with virtual member functions.

    --
    Ian Collins
    Ian Collins, Nov 16, 2011
    #10
  11. On Nov 16, 1:56 pm, Ian Collins <> wrote:
    > You can always replace conditionals with virtual member functions.


    I assume in the drawing case you are suggesting I add a virtual
    function "GetVerticies" or a virtual function "Draw". This is now
    what I want for these reasons:

    1. I have to modify the Shape class to be aware that it is being drawn
    2. Some shapes cannot be drawn with "verticies" and need to be drawn
    carefully and uniquely -- for example a doughnut that has a hole in
    the middle.

    Thank you,

    Chris
    Chris Stankevitz, Nov 16, 2011
    #11
  12. Chris Stankevitz

    Ian Collins Guest

    Re: How can I remove dynamic_cast and if statements from this codesnippet?

    On 11/17/11 11:03 AM, Chris Stankevitz wrote:
    > On Nov 16, 1:56 pm, Ian Collins<> wrote:
    >> You can always replace conditionals with virtual member functions.

    >
    > I assume in the drawing case you are suggesting I add a virtual
    > function "GetVerticies" or a virtual function "Draw". This is now
    > what I want for these reasons:
    >
    > 1. I have to modify the Shape class to be aware that it is being drawn
    > 2. Some shapes cannot be drawn with "verticies" and need to be drawn
    > carefully and uniquely -- for example a doughnut that has a hole in
    > the middle.


    So how would a generic drawing class be expected to know this? It would
    have to changed for each new shape.

    Does your window manager know how to render every type of window on your
    system?

    --
    Ian Collins
    Ian Collins, Nov 16, 2011
    #12
  13. On Nov 16, 2:12 pm, Ian Collins <> wrote:
    > So how would a generic drawing class be expected to know this?


    This is basically the question I am asking this group.

    Below is a pseudocode answer to the question that
    - leaves Shape, Circle, and Square untouched (good)
    - places no drawing dependency on the shapes (good)
    - leaves drawing code entirely in the Drawer classes (good)
    - does not have switch or if statements (good)
    * does use a dynamic cast (BAD)

    Can you (or anyone else) come up with some c++ pseudocode that
    - leaves Shape, Circle, and Square untouched (good)
    - places no drawing dependency on the shapes (good)
    - leaves drawing code entirely in the Drawer classes (good)
    - does not have switch or if statements (good)
    - does not use a dynamic cast (good)

    Chris

    =====

    //----------
    struct Drawer
    {
    virtual void Draw(Shape* shape) = 0;
    };

    //----------
    struct CircleDrawer : public Drawer
    {
    void Draw(Shape* shape)
    {
    // dynamic_cast<Circle*>(shape)
    // draw the circle
    }
    };

    //----------
    struct SquareDrawer : public Drawer
    {
    void Draw(Shape* shape)
    {
    // dynamic_cast<Square*>(shape)
    // draw the square
    }
    };

    //----------
    struct DrawerCreator
    {
    virtual Drawer* GetNewDrawer() = 0;
    virtual bool CanDraw(Shape* shape) = 0;
    };

    //----------
    struct CircleDrawerCreator : public DrawerCreator
    {
    Drawer* GetNewDrawer() { return new CircleDrawer; }
    bool CanDraw(Shape* shape) { return dynamic_cast<Circle*>(shape); }
    };

    //----------
    struct SqareDrawerCreator : public DrawerCreator
    {
    Drawer* GetNewDrawer() { return new SquareDrawer; }
    bool CanDraw(Shape* shape) { return dynamic_cast<Square*>(shape); }
    };

    //----------
    struct DrawerManager
    {
    void RegisterDrawerCreator(DrawerCreator* dc)
    {
    drawers.push_back(dc);
    }

    void Draw(Shape* shape)
    {
    if (Drawer* drawer = GetDrawer(shape))
    {
    drawer->Draw(shape);
    }
    }

    Drawer* GetDrawer(Shape* shape)
    {
    for (int i = 0; i < drawers.size(); ++i)
    {
    if (drawers->CanDraw(shape))
    {
    return drawers->GetNewDrawer();
    }
    }

    return 0;
    }

    vector<Drawer*> drawers;
    };

    //----------
    int main()
    {
    DrawerManager drawer_manager;

    drawer_manager.RegisterDrawerCreator(new CircleDrawerCreator);
    drawer_manager.RegisterDrawerCreator(new SquareDrawerCreator);

    Shape* shape1 = new Circle;
    Shape* shape2 = new Square;

    drawer_manager.Draw(shape1);
    drawer_manager.Draw(shape2);
    }
    Chris Stankevitz, Nov 16, 2011
    #13
  14. Chris Stankevitz

    Werner Guest

    On Nov 17, 1:20 am, Chris Stankevitz <>
    wrote:

    > Below is a pseudocode answer to the question that
    >  - leaves Shape, Circle, and Square untouched (good)
    >  - places no drawing dependency on the shapes (good)
    >  - leaves drawing code entirely in the Drawer classes (good)
    >  - does not have switch or if statements (good)
    >  * does use a dynamic cast (BAD)
    >
    > Can you (or anyone else) come up with some c++ pseudocode that
    >  - does not use a dynamic cast (good)


    I can't see why using dynamic cast is so bad. It is not as
    if you have an if/else style of programming that requires
    constant modification. dynamic_cast under your control that
    requires no modification is good (except for being a tid
    slower, perhaps).

    You might want to remove the burden from your client though (see
    below):

    #include <vector>

    struct Shape{};
    struct Circle : Shape{};
    struct Square : Shape{};

    struct AbstractDrawer
    {
    virtual void Draw(Shape* shape) = 0;

    };

    template <class ShapeT>
    struct TypedDrawer : AbstractDrawer
    {
    typedef ShapeT shape_type;

    protected:
    virtual void DoDraw( shape_type& shape ) = 0;
    //{ Default implementation ??? }

    private:
    virtual void Draw( Shape* shape )
    {
    DoDraw( dynamic_cast<shape_type&>(*shape) );
    }
    };

    struct CircleDrawer : TypedDrawer<Circle>
    {
    protected:
    virtual void DoDraw( Circle& shape )
    {
    //Implementation
    }
    };

    struct SquareDrawer : TypedDrawer<Square>
    {
    protected:
    virtual void DoDraw( Square& shape )
    {
    //Implementation
    }
    };

    //----------
    struct AbstractDrawerCreator
    {
    virtual AbstractDrawer* GetNewDrawer() = 0;
    virtual bool CanDraw(Shape* shape) = 0;
    };

    template <class DrawerT>
    struct TypedDrawerCreator : AbstractDrawerCreator
    {
    private:
    typedef DrawerT drawer_type;
    typedef typename DrawerT::shape_type shape_type;

    virtual drawer_type* GetNewDrawer() const
    {
    return new drawer_type;
    }
    virtual bool CanDraw( Shape* shape ) const
    {
    return dynamic_cast<shape_type*>(shape);
    }
    };

    typedef TypedDrawerCreator<CircleDrawer> CircleDrawerCreator;
    typedef TypedDrawerCreator<SquareDrawer> SquareDrawerCreator;
    //etc...


    //[Werner Erasmus: Left this in as I've removed compiler
    // errors]
    struct DrawerManager
    {
    void RegisterDrawerCreator(AbstractDrawerCreator* dc)
    {
    drawers.push_back(dc);
    }

    void Draw(Shape* shape)
    {
    if (AbstractDrawer* drawer = GetDrawer(shape))
    {
    drawer->Draw(shape);
    }
    }

    AbstractDrawer* GetDrawer(Shape* shape)
    {
    for (int i = 0; i < drawers.size(); ++i)
    {
    if (drawers->CanDraw(shape))
    {
    return drawers->GetNewDrawer();
    }
    }

    return 0;
    }

    std::vector<AbstractDrawerCreator*> drawers;
    };
    Werner, Nov 17, 2011
    #14
  15. Chris Stankevitz

    Francesco Guest

    On 16 Nov, 19:37, Chris Stankevitz <> wrote:
    > Hello,
    >
    > I would like to remove the "dynamic_cast" and "if" statements from the
    > code below.  I believe some people would describe this as making the
    > code "polymorphic".  Can you recommend a way to do this without
    > modifying the original classes in "Library A"?
    >
    > My intention is to create an "XML Writer" class(es) for shapes without
    > putting "XML code" in the base classes.  I plan to use a similar
    > pattern for drawing and other tasks my application has to perform on
    > Shape objects.
    >
    > Thank you,
    >
    > Chris
    >
    > // Library A
    > struct Shape { virtual ~Shape() {} };
    > struct Circle : public Shape { float radius; };
    > struct Square : public Shape { float edge; };
    >
    > // Library B
    > #include <iostream>
    >
    > class XmlWriter
    > {
    >   static void write(Shape* shape)
    >   {
    >     if (Circle* circle = dynamic_cast<Circle*>(shape))
    >     {
    >       std::cout << "<Circle Radius=" << circle->radius << "/>";
    >     }
    >     else if (Square* square = dynamic_cast<Square*>(shape))
    >     {
    >       std::cout << "<Square Edge=" << square->edge << "/>";
    >     }
    >   }
    >
    >
    >
    >
    >
    >
    >
    > };


    Hi,

    IMHO you can try one of the following approaches:
    - map the type_info of the classes to some function that does the
    actual work, any map will give you on the average better than linear
    behavior (cascade of ifs). This applies only if you have lots of
    classes, if you have only a handful it will be worse (probably).
    - create a mirror hierarchy of classes (deriving or composing). Add
    the virtual write method on those classes and just use those instead
    of the original ones...

    Obviously if you could modify the original classes, that would be the
    way to go...
    Bye,
    Francesco

    // CODE


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Library A
    struct Shape { virtual ~Shape() {} };
    struct Circle : public Shape { float radius; };
    struct Square : public Shape { float edge; };

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Library B
    #include <iostream>
    #include <cassert>

    #define USE_UNORDERED_MAP

    #ifdef USE_UNORDERED_MAP
    #include <tr1/unordered_map>
    #else
    #include <map>
    #endif

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Type unsafe functions!!

    void WriteCircle( Shape * shape )
    {
    Circle * circle = static_cast< Circle * >( shape );
    std::cout << "<Circle Radius=" << circle->radius << "/>";
    }

    void WriteSquare( Shape * shape )
    {
    Square * square = static_cast< Square * >( shape );
    std::cout << "<Square Edge=" << square->edge << "/>";
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // TypeInfo class for map key

    class CTypeInfo
    {
    public:
    CTypeInfo( std::type_info const & inTypeInfo )
    : mTypeInfoPtr( &inTypeInfo )
    #ifdef USE_UNORDERED_MAP
    , mHash( std::tr1::hash< std::string >()
    ( inTypeInfo.name() ) )
    #endif
    { }

    bool operator<( CTypeInfo const & inTypeInfo ) const
    { return mTypeInfoPtr->before( *inTypeInfo.mTypeInfoPtr ); }

    bool operator==( CTypeInfo const & inTypeInfo ) const
    { return *mTypeInfoPtr == *inTypeInfo.mTypeInfoPtr; }

    #ifdef USE_UNORDERED_MAP
    size_t GetHash( void ) const
    { return mHash; }
    #endif

    private:
    std::type_info const * mTypeInfoPtr;
    #ifdef USE_UNORDERED_MAP
    size_t mHash;
    #endif

    };

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Map stuff

    #ifdef USE_UNORDERED_MAP
    struct CTypeInfoHash
    {
    size_t operator()( CTypeInfo const & inTypeInfo ) const
    { return inTypeInfo.GetHash(); }
    };
    #endif

    typedef void (*tFuncPtr)( Shape * );
    #ifdef USE_UNORDERED_MAP
    typedef std::tr1::unordered_map< CTypeInfo, tFuncPtr, CTypeInfoHash >
    CMap;
    #else
    typedef std::map< CTypeInfo, tFuncPtr > CMap;
    #endif


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // XmlWriter

    class XmlWriter
    {
    private:
    // this is all thread unsafe!!!
    static CMap & GetMap()
    {
    static CMap sMap;
    return sMap;
    }

    static void Init()
    {
    static bool sInit = false;
    if( !sInit )
    {
    CMap & map = GetMap();
    map.insert( CMap::value_type( typeid( Circle ),
    &WriteCircle ) );
    map.insert( CMap::value_type( typeid( Square ),
    &WriteSquare ) );
    sInit = true;
    }
    }
    public:
    void write(Shape* shape)
    {
    Init();
    CMap & map = GetMap();
    CMap::const_iterator iter = map.find( typeid( *shape ) );
    assert( iter != map.end() );
    iter->second( shape );
    }
    };

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // augmenting classes
    // WARNING: quick example, many things to look after...

    struct CShapePlus
    {
    virtual ~CShapePlus() {}
    virtual void Write() const = 0;
    };

    // composition

    struct CCirclePlus : public CShapePlus
    {
    Circle mCircle;
    virtual void Write() const
    { std::cout << "<Circle Radius=" << mCircle.radius << "/>"; }

    };

    struct CSquarePlus : public CShapePlus
    {
    Square mSquare;
    virtual void Write() const
    { std::cout << "<Square Edge=" << mSquare.edge << "/>"; }

    };

    // inheritance

    struct CCirclePlus2 : public Circle, public CShapePlus
    {
    virtual void Write() const
    { std::cout << "<Circle Radius=" << this->radius << "/>"; }
    };

    struct CSquarePlus2 : public Square, public CShapePlus
    {
    virtual void Write() const
    { std::cout << "<Square Edge=" << this->edge << "/>"; }
    };



    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // main

    int main()
    {
    Shape * shape1 = new Circle;
    Shape * shape2 = new Square;

    XmlWriter writer;

    writer.write( shape1 ); std::cout << '\n';
    writer.write( shape2 ); std::cout << '\n';


    CShapePlus * shape3 = new CCirclePlus;
    CShapePlus * shape4 = new CSquarePlus;
    CShapePlus * shape5 = new CCirclePlus2;
    CShapePlus * shape6 = new CSquarePlus2;

    shape3->Write(); std::cout << '\n';
    shape4->Write(); std::cout << '\n';
    shape5->Write(); std::cout << '\n';
    shape6->Write(); std::cout << '\n';
    }

    // END CODE
    Francesco, Nov 17, 2011
    #15
  16. Chris Stankevitz

    Goran Guest

    On Nov 17, 12:20 am, Chris Stankevitz <>
    wrote:
    > On Nov 16, 2:12 pm, Ian Collins <> wrote:
    >
    > > So how would a generic drawing class be expected to know this?

    >
    > This is basically the question I am asking this group.
    >
    > Below is a pseudocode answer to the question that
    >  - leaves Shape, Circle, and Square untouched (good)
    >  - places no drawing dependency on the shapes (good)
    >  - leaves drawing code entirely in the Drawer classes (good)
    >  - does not have switch or if statements (good)
    >  * does use a dynamic cast (BAD)
    >
    > Can you (or anyone else) come up with some c++ pseudocode that
    >  - leaves Shape, Circle, and Square untouched (good)
    >  - places no drawing dependency on the shapes (good)
    >  - leaves drawing code entirely in the Drawer classes (good)
    >  - does not have switch or if statements (good)
    >  - does not use a dynamic cast (good)
    >
    > Chris
    >
    > =====
    >
    > //----------
    > struct Drawer
    > {
    >   virtual void Draw(Shape* shape) = 0;
    >
    > };
    >
    > //----------
    > struct CircleDrawer : public Drawer
    > {
    >   void Draw(Shape* shape)
    >   {
    >     // dynamic_cast<Circle*>(shape)
    >     // draw the circle
    >   }
    >
    > };
    >
    > //----------
    > struct SquareDrawer : public Drawer
    > {
    >   void Draw(Shape* shape)
    >   {
    >     // dynamic_cast<Square*>(shape)
    >     // draw the square
    >   }
    >
    > };
    >
    > //----------
    > struct DrawerCreator
    > {
    >   virtual Drawer* GetNewDrawer() = 0;
    >   virtual bool CanDraw(Shape* shape) = 0;
    >
    > };
    >
    > //----------
    > struct CircleDrawerCreator : public DrawerCreator
    > {
    >   Drawer* GetNewDrawer() { return new CircleDrawer; }
    >   bool CanDraw(Shape* shape) { return dynamic_cast<Circle*>(shape); }
    >
    > };
    >
    > //----------
    > struct SqareDrawerCreator : public DrawerCreator
    > {
    >   Drawer* GetNewDrawer() { return new SquareDrawer; }
    >   bool CanDraw(Shape* shape) { return dynamic_cast<Square*>(shape); }
    >
    > };
    >
    > //----------
    > struct DrawerManager
    > {
    >   void RegisterDrawerCreator(DrawerCreator* dc)
    >   {
    >     drawers.push_back(dc);
    >   }
    >
    >   void Draw(Shape* shape)
    >   {
    >     if (Drawer* drawer = GetDrawer(shape))
    >     {
    >       drawer->Draw(shape);
    >     }
    >   }
    >
    >   Drawer* GetDrawer(Shape* shape)
    >   {
    >     for (int i = 0; i < drawers.size(); ++i)
    >     {
    >       if (drawers->CanDraw(shape))
    >       {
    >         return drawers->GetNewDrawer();
    >       }
    >     }
    >
    >     return 0;
    >   }
    >
    >   vector<Drawer*> drawers;
    >
    > };
    >
    > //----------
    > int main()
    > {
    >   DrawerManager drawer_manager;
    >
    >   drawer_manager.RegisterDrawerCreator(new CircleDrawerCreator);
    >   drawer_manager.RegisterDrawerCreator(new SquareDrawerCreator);
    >
    >   Shape* shape1 = new Circle;
    >   Shape* shape2 = new Square;
    >
    >   drawer_manager.Draw(shape1);
    >   drawer_manager.Draw(shape2);
    >
    >
    >
    >
    >
    >
    >
    > }


    What you seem to be showing here is the adapter pattern. The way you
    went about it seems backwards, though. When you create your adapter
    (Drawer), you should strive to do it using the correct adaptee type.
    But what you did is to throw said type back to base (by passing Shape*
    to DrawerManager) and reached for dynamic_cast.

    So how about this:

    int main()
    {
    Canvas c;

    Circle shape1;
    Square shape2;

    Draw(c, CircleDrawer(shape1));
    Draw(c, SquareDrawer(shape2));
    }

    class Canvas { /*Driving primitives here*/ };

    class Drawer
    {
    public: virtual void Draw(Canvas& c) const = 0;
    };

    void Draw(Canvas& c, Drawer& d)
    {
    d.Draw(c);
    }

    // Trivial intermediary: drawer for Shapes.
    class ShapeDrawer
    {
    const Shape& s_;
    public:
    ShapeDrawer(Shape& s) : s_(s) {}
    };

    // Implementation starts here

    class CircleDrawer : public ShapeDrawer
    {
    public:
    CircleDrawer(const Circle& s) : ShapeDrawer(c) {}
    virtual void Draw(Canvas& c) const {knock yourself out}
    };

    class SquareDrawer {you get the picture};

    IOW... __Don't__ use generic Shapes in lib B logic. __Adapt__ them as
    soon as you get them (because then you should know their types, and
    that's the only way to avoid casting) and use adapters. Added bonus:
    adapters will let you mix your own stuff with lib A stuff.

    Goran.

    P.S. You didn't want dynamic_cast, but rather static_cast, there. ;-)
    Goran, Nov 17, 2011
    #16
  17. Chris Stankevitz

    Goran Guest

    On Nov 17, 8:39 am, Werner <> wrote:
    > I can't see why using dynamic cast is so bad. It is not as
    > if you have an if/else style of programming that requires
    > constant modification. dynamic_cast under your control that
    > requires no modification is good (except for being a tid
    > slower, perhaps).


    Well, conceptually, there's no difference between if/else and
    dynamic_cast (and indeed OP's first post is dynamic_cast with if/
    else). dynamic_cast merely moves decision to the type itself (whereas
    if/else decides on the data). IOW, with dynamic_cast, type is used as
    data. And indeed, if new type introduced, conditional logic must be
    changed with itand therefore open/closed principle is compromised.

    Goran.
    Goran, Nov 17, 2011
    #17
  18. Werner,

    Thank you for your reply.

    > I can't see why using dynamic cast is so bad.


    Perhaps it is not bad. One way or another I am going to complete this
    task and I know I can complete it using dynamic_casts so I might end
    up using them. I suppose my question to the group is "is it possible
    to do something like this without using a dynamic_cast." The answer
    to that question might be "no" -- which I'm okay with.

    > You might want to remove the burden from your client though


    Thank you your method is certainly cleaner than mine. I need to look
    at it a bit more closely but I believe we are both doing the same
    thing.

    Chris
    Chris Stankevitz, Nov 17, 2011
    #18
  19. Francesco,

    Thank you for your reply.

    > - map the type_info of the classes to some function


    This is the sort of thing I came to the group to hear. I've never
    heard of type_info before, so I am going to go learn about it. Just
    from the name my suspicion is that type_info is doing the same thing
    as dynamic_cast under the covers, but I'm going to find out.

    Thank you for providing a novel solution to my problem that satisfies
    my conditions!

    Chris
    Chris Stankevitz, Nov 17, 2011
    #19
  20. Goran,

    On Nov 17, 5:17 am, Goran <> wrote:
    > What you seem to be showing here is the adapter pattern. The way you
    > went about it seems backwards, though.


    Thank you this is exactly the kind of information I was looking for.
    I'm going to go read up on Adapters. I've never heard of 'adapter
    pattern'.

    > But what you did is to throw said type back to base (by passing Shape*
    > to DrawerManager) and reached for dynamic_cast.


    I noticed this after I posted and some others pointed that out here on
    the list. I understand the concept and the idea behind sending the
    correct type to the drawer classes.

    Thank you,

    Chris
    Chris Stankevitz, Nov 17, 2011
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Neil Zanella
    Replies:
    8
    Views:
    1,186
    mfmehdi
    Oct 20, 2006
  2. Clare Hsiao
    Replies:
    0
    Views:
    327
    Clare Hsiao
    Feb 12, 2004
  3. Harry George
    Replies:
    6
    Views:
    375
    Bart Nessux
    Feb 23, 2004
  4. Vince
    Replies:
    12
    Views:
    747
    Martin Gregorie
    Jan 21, 2008
  5. John Crichton
    Replies:
    6
    Views:
    264
    John Crichton
    Jul 12, 2010
Loading...

Share This Page