Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #11525 > unrolled thread

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

Started byChris Stankevitz <chrisstankevitz@gmail.com>
First post2011-11-16 10:37 -0800
Last post2011-11-18 09:23 -0800
Articles 20 on this page of 41 — 11 participants

Back to article view | Back to comp.lang.c++


Contents

  How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-16 10:37 -0800
    Re: How can I remove dynamic_cast and if statements from this code snippet? Ian Collins <ian-news@hotmail.com> - 2011-11-17 07:52 +1300
      Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-16 11:14 -0800
        Re: How can I remove dynamic_cast and if statements from this code snippet? red floyd <no.spam.here@its.invalid> - 2011-11-16 12:03 -0800
          Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-16 13:28 -0800
            Re: How can I remove dynamic_cast and if statements from this code snippet? Leigh Johnston <leigh@i42.co.uk> - 2011-11-16 21:39 +0000
              Re: How can I remove dynamic_cast and if statements from this code snippet? Leigh Johnston <leigh@i42.co.uk> - 2011-11-16 21:40 +0000
                Re: How can I remove dynamic_cast and if statements from this code snippet? Leigh Johnston <leigh@i42.co.uk> - 2011-11-16 21:43 +0000
                  Re: How can I remove dynamic_cast and if statements from this code snippet? Leigh Johnston <leigh@i42.co.uk> - 2011-11-16 21:45 +0000
              Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-16 13:46 -0800
                Re: How can I remove dynamic_cast and if statements from this code snippet? Leigh Johnston <leigh@i42.co.uk> - 2011-11-16 21:49 +0000
        Re: How can I remove dynamic_cast and if statements from this code snippet? Leigh Johnston <leigh@i42.co.uk> - 2011-11-16 20:35 +0000
          Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-16 13:38 -0800
        Re: How can I remove dynamic_cast and if statements from this code snippet? Ian Collins <ian-news@hotmail.com> - 2011-11-17 10:37 +1300
          Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-16 13:41 -0800
            Re: How can I remove dynamic_cast and if statements from this code snippet? Ian Collins <ian-news@hotmail.com> - 2011-11-17 10:56 +1300
              Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-16 14:03 -0800
                Re: How can I remove dynamic_cast and if statements from this code snippet? Ian Collins <ian-news@hotmail.com> - 2011-11-17 11:12 +1300
                  Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-16 15:20 -0800
                    Re: How can I remove dynamic_cast and if statements from this code snippet? Werner <werasm@gmail.com> - 2011-11-16 23:39 -0800
                      Re: How can I remove dynamic_cast and if statements from this code snippet? Goran <goran.pusic@gmail.com> - 2011-11-17 05:42 -0800
                        Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-17 10:37 -0800
                          Re: How can I remove dynamic_cast and if statements from this code snippet? Werner <werasm@gmail.com> - 2011-11-17 23:23 -0800
                            Re: How can I remove dynamic_cast and if statements from this code snippet? Goran <goran.pusic@gmail.com> - 2011-11-18 01:46 -0800
                          Re: How can I remove dynamic_cast and if statements from this code snippet? Goran <goran.pusic@gmail.com> - 2011-11-18 01:25 -0800
                        Re: How can I remove dynamic_cast and if statements from this code snippet? Werner <werasm@gmail.com> - 2011-11-17 23:28 -0800
                      Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-17 10:27 -0800
                    Re: How can I remove dynamic_cast and if statements from this code snippet? Goran <goran.pusic@gmail.com> - 2011-11-17 05:17 -0800
                      Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-17 10:35 -0800
    Re: How can I remove dynamic_cast and if statements from this code snippet? Francesco <xtrigger303@gmail.com> - 2011-11-17 01:56 -0800
      Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-17 10:31 -0800
    Re: How can I remove dynamic_cast and if statements from this code snippet? <address_is@invalid.invalid> - 2011-11-17 19:21 +0000
      Re: How can I remove dynamic_cast and if statements from this code snippet? Leigh Johnston <leigh@i42.co.uk> - 2011-11-17 19:33 +0000
      Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-17 14:09 -0800
        Re: How can I remove dynamic_cast and if statements from this code snippet? Werner <werasm@gmail.com> - 2011-11-17 23:30 -0800
          Re: How can I remove dynamic_cast and if statements from this code snippet? Bart v Ingen Schenau <bart@ingen.ddns.info> - 2011-11-18 14:45 +0100
      Re: How can I remove dynamic_cast and if statements from this code snippet? Goran <goran.pusic@gmail.com> - 2011-11-18 02:58 -0800
        Re: How can I remove dynamic_cast and if statements from this code snippet? Tobias Müller <troplin@bluewin.ch> - 2011-11-18 16:56 +0000
          Re: How can I remove dynamic_cast and if statements from this code snippet? Chris Stankevitz <chrisstankevitz@gmail.com> - 2011-11-18 17:27 -0800
      Re: How can I remove dynamic_cast and if statements from this code snippet? Goran <goran.pusic@gmail.com> - 2011-11-18 01:29 -0800
    Re: How can I remove dynamic_cast and if statements from this code snippet? Noah Roberts <roberts.noah@gmail.com> - 2011-11-18 09:23 -0800

Page 2 of 3 — ← Prev page 1 [2] 3  Next page →


#11571

FromGoran <goran.pusic@gmail.com>
Date2011-11-17 05:42 -0800
Message-ID<5eaa3de3-a998-42ee-9d1b-d69993782d4b@k26g2000yqd.googlegroups.com>
In reply to#11552
On Nov 17, 8:39 am, Werner <wer...@gmail.com> 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.

[toc] | [prev] | [next] | [standalone]


#11578

FromChris Stankevitz <chrisstankevitz@gmail.com>
Date2011-11-17 10:37 -0800
Message-ID<06d2928d-ec77-4d8c-81d1-a3197cb705db@r28g2000yqj.googlegroups.com>
In reply to#11571
On Nov 17, 5:42 am, Goran <goran.pu...@gmail.com> wrote:
> 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.

Goran,

In the quote above are you trying to say this:

1. dynamic_cast and if/else are basically the same (for the reasons
you stated)
2. If someone is opposed to solving a problem with a bunch of if/else
statements based on data, then
3. that person should also be opposed to solving the problem using
dynamic_cast

Thank you,

Chris

[toc] | [prev] | [next] | [standalone]


#11588

FromWerner <werasm@gmail.com>
Date2011-11-17 23:23 -0800
Message-ID<57589d77-f985-4a8c-8d45-081ee488f15e@q16g2000yqn.googlegroups.com>
In reply to#11578
On Nov 17, 8:37 pm, Chris Stankevitz <chrisstankev...@gmail.com>
wrote:
> On Nov 17, 5:42 am, Goran <goran.pu...@gmail.com> wrote:
>
> > 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.
>
> Goran,
>
> In the quote above are you trying to say this:
>
> 1. dynamic_cast and if/else are basically the same (for the reasons
> you stated)

Wrong.


> 2. If someone is opposed to solving a problem with a bunch of if/else
> statements based on data, then
> 3. that person should also be opposed to solving the problem using
> dynamic_cast

Also wrong.

Firstly, as mentioned previously, one only needs dynamic_cast
if you have virtual inheritance. Static_cast will suffice most
of the time.

Secondly, there are many ways to kill a cat. Whether you use
names or casting, you're still comparing...

Thirdly, in your original example using dynamic_cast did
not violate OCP (and in my example even less).

Definition of OCP:

Open to extension, closed to modification:

Bad use of dynamic cast (open to modification):

if( dynamic_cast<Square*>(shape) )
{
}
else if( dynamic_cast<Circle*>(shape ) )
else if()
{
  //etc...
}

Consider the acyclic visitor as example as described in
Agile Software Development (Robert C. Martin), where
dynamic_cast is put to good use.

Now, furthermore (Goran):

Show me how the use of dynamic cast in my example (or the
original example, for that matter), violates OCP. None
of the code using dynamic_cast (or static_cast as better
alternative) needs modification. OCP is not violated...

Do I need to modify existing classes to change logic?
No. Dynamic_cast usage does not imply OCP is violated,
only a certain style of dynamic_cast usage.

Kind regards,

Werner

[toc] | [prev] | [next] | [standalone]


#11595

FromGoran <goran.pusic@gmail.com>
Date2011-11-18 01:46 -0800
Message-ID<4a3487e8-bc63-4942-969b-f348c0a96e9d@p16g2000yqd.googlegroups.com>
In reply to#11588
On Nov 18, 8:23 am, Werner <wer...@gmail.com> wrote:
> On Nov 17, 8:37 pm, Chris Stankevitz <chrisstankev...@gmail.com>
> wrote:
>
> > On Nov 17, 5:42 am, Goran <goran.pu...@gmail.com> wrote:
>
> > > 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.
>
> > Goran,
>
> > In the quote above are you trying to say this:
>
> > 1. dynamic_cast and if/else are basically the same (for the reasons
> > you stated)
>
> Wrong.
>
> > 2. If someone is opposed to solving a problem with a bunch of if/else
> > statements based on data, then
> > 3. that person should also be opposed to solving the problem using
> > dynamic_cast
>
> Also wrong.
>
> Firstly, as mentioned previously, one only needs dynamic_cast
> if you have virtual inheritance. Static_cast will suffice most
> of the time.

Not really. Indeed, OP needs dynamic_cast and has no virtual
inheritance.

>
> Secondly, there are many ways to kill a cat. Whether you use
> names or casting, you're still comparing...
>
> Thirdly, in your original example using dynamic_cast did
> not violate OCP (and in my example even less).
>
> Definition of OCP:
>
> Open to extension, closed to modification:
>
> Bad use of dynamic cast (open to modification):
>
> if( dynamic_cast<Square*>(shape) )
> {}
>
> else if( dynamic_cast<Circle*>(shape ) )
> else if()
> {
>   //etc...
>
> }
>
> Consider the acyclic visitor as example as described in
> Agile Software Development (Robert C. Martin), where
> dynamic_cast is put to good use.
>
> Now, furthermore (Goran):
>
> Show me how the use of dynamic cast in my example (or the
> original example, for that matter), violates OCP. None
> of the code using dynamic_cast (or static_cast as better
> alternative) needs modification. OCP is not violated...
>
> Do I need to modify existing classes to change logic?
> No. Dynamic_cast usage does not imply OCP is violated,
> only a certain style of dynamic_cast usage.

I agree with you. TBH, I didn't look at your example much, nor was I
complaining about --your-- use of the dynamic_cast. I was complaining
about OP's use of it.

However, I think your use of dynamic_cast can be avoided, using the
same logic I applied in my first (I think) post. If your typed drawer
is --created-- with a reference to it's shape, then your DoDraw can be
using the correct type --without-- a cast at all (and Draw wouldn't
have a "shape" parameter at all). In my mind, you still made the same
mistake as the OP: you lost the original type of the Shape, and that
caused more code to be added.

BTW, your code breaks in unexpected ways if you derive from Square or
Circle: you might get SquareDrawer to draw, I dunno, DerivedSquare.

Goran.

[toc] | [prev] | [next] | [standalone]


#11593

FromGoran <goran.pusic@gmail.com>
Date2011-11-18 01:25 -0800
Message-ID<4f41e795-140a-44ff-8f39-57a62ffef666@p1g2000yqh.googlegroups.com>
In reply to#11578
On Nov 17, 7:37 pm, Chris Stankevitz <chrisstankev...@gmail.com>
wrote:
> On Nov 17, 5:42 am, Goran <goran.pu...@gmail.com> wrote:
>
> > 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.
>
> Goran,
>
> In the quote above are you trying to say this:
>
> 1. dynamic_cast and if/else are basically the same (for the reasons
> you stated)
> 2. If someone is opposed to solving a problem with a bunch of if/else
> statements based on data, then
> 3. that person should also be opposed to solving the problem using
> dynamic_cast

I guess you can put it that way.

Still, striving for polymorphism will (IMHO) lower the number "data-
based" if/else (or switch) decisions, even if some dynamic_cast stay.
Use of if/else where polymorphism would help is bad, dynamic_cast is
slightly better, appropriate abstraction (possibly well-known pattern,
if I can recognize it) is best ;-).

Goran.

[toc] | [prev] | [next] | [standalone]


#11589

FromWerner <werasm@gmail.com>
Date2011-11-17 23:28 -0800
Message-ID<6c230e4a-0185-46be-98fb-606589b54283@p5g2000vbm.googlegroups.com>
In reply to#11571
On Nov 17, 3:42 pm, Goran <goran.pu...@gmail.com> wrote:


> 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.

No, conditional logic was not changed. The for loop ensured that...
But point to me how conditional logic changed by adding a new
class. Also (by referring to my example), show me how OCP is
violated by adding new drawers or shapes... For OCP to be
violated, code containing logic needs to change. The for
loop (whilst crude) suffices... When using a map, if/else
also exist (N times). You just don't need to modify it. The
same applies here. Therefore - nonsense.

Kind regards,

Werner

[toc] | [prev] | [next] | [standalone]


#11575

FromChris Stankevitz <chrisstankevitz@gmail.com>
Date2011-11-17 10:27 -0800
Message-ID<f327c107-07b8-4edc-942b-84239217384a@hh9g2000vbb.googlegroups.com>
In reply to#11552
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

[toc] | [prev] | [next] | [standalone]


#11568

FromGoran <goran.pusic@gmail.com>
Date2011-11-17 05:17 -0800
Message-ID<4526fc99-7bee-4633-ae94-5b936b23c248@l19g2000yqc.googlegroups.com>
In reply to#11548
On Nov 17, 12:20 am, Chris Stankevitz <chrisstankev...@gmail.com>
wrote:
> On Nov 16, 2:12 pm, Ian Collins <ian-n...@hotmail.com> 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[i]->CanDraw(shape))
>       {
>         return drawers[i]->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. ;-)

[toc] | [prev] | [next] | [standalone]


#11577

FromChris Stankevitz <chrisstankevitz@gmail.com>
Date2011-11-17 10:35 -0800
Message-ID<8952b509-b595-4fba-aee7-b9fa36dcfab7@p1g2000yqh.googlegroups.com>
In reply to#11568
Goran,

On Nov 17, 5:17 am, Goran <goran.pu...@gmail.com> 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

[toc] | [prev] | [next] | [standalone]


#11557

FromFrancesco <xtrigger303@gmail.com>
Date2011-11-17 01:56 -0800
Message-ID<4cf9b2d9-0179-4e54-9b13-0395ca15d20d@l19g2000yqc.googlegroups.com>
In reply to#11525
On 16 Nov, 19:37, Chris Stankevitz <chrisstankev...@gmail.com> 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

[toc] | [prev] | [next] | [standalone]


#11576

FromChris Stankevitz <chrisstankevitz@gmail.com>
Date2011-11-17 10:31 -0800
Message-ID<00ba0a39-4a63-41ac-8de8-389c083388b6@hh9g2000vbb.googlegroups.com>
In reply to#11557
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

[toc] | [prev] | [next] | [standalone]


#11581

From<address_is@invalid.invalid>
Date2011-11-17 19:21 +0000
Message-ID<540018325343244726.402875address_is-invalid.invalid@news.aioe.org>
In reply to#11525
Chris Stankevitz <chrisstankevitz@gmail.com> 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 << "/>";
>     }
>   }
> };

I think this is a typical usecase for the "visitor" pattern:

// Library A

class ShapeVisitor;
class Shape { public: virtual void accept(ShapeVisitor& visitor) = 0; };
class Circle : public Shape {
public:
  virtual void accept(ShapeVisitor& visitor) { visitor.visit(*this); }
};
class Square : public Shape {
public:
  virtual void accept(ShapeVisitor& visitor) { visitor.visit(*this); }
};
class ShapeVisitor {
  virtual void visit(Square& square) = 0;
  virtual void visit(Circle& circle) = 0;
};

// Library B
class XmlWriter : public ShapeVisitor {
  virtual void visit(Square& square) {
    // do something
  }
  virtual void visit(Circle& circle) {
    // do something
  }
  void write(Shape& shape) {
    shape.accept(*this);
  }
};

Tobi

[toc] | [prev] | [next] | [standalone]


#11582

FromLeigh Johnston <leigh@i42.co.uk>
Date2011-11-17 19:33 +0000
Message-ID<Gu-dnYAjuIEM_FjTnZ2dnUVZ8m6dnZ2d@giganews.com>
In reply to#11581
On 17/11/2011 19:21, address_is@invalid.invalid wrote:
> Chris Stankevitz<chrisstankevitz@gmail.com>  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<<  "/>";
>>      }
>>    }
>> };
>
> I think this is a typical usecase for the "visitor" pattern:
>
> // Library A
>
> class ShapeVisitor;
> class Shape { public: virtual void accept(ShapeVisitor&  visitor) = 0; };
> class Circle : public Shape {
> public:
>    virtual void accept(ShapeVisitor&  visitor) { visitor.visit(*this); }
> };
> class Square : public Shape {
> public:
>    virtual void accept(ShapeVisitor&  visitor) { visitor.visit(*this); }
> };
> class ShapeVisitor {
>    virtual void visit(Square&  square) = 0;
>    virtual void visit(Circle&  circle) = 0;
> };
>
> // Library B
> class XmlWriter : public ShapeVisitor {
>    virtual void visit(Square&  square) {
>      // do something
>    }
>    virtual void visit(Circle&  circle) {
>      // do something
>    }
>    void write(Shape&  shape) {
>      shape.accept(*this);
>    }
> };

Best answer yet.

/Leigh

[toc] | [prev] | [next] | [standalone]


#11584

FromChris Stankevitz <chrisstankevitz@gmail.com>
Date2011-11-17 14:09 -0800
Message-ID<079c8aee-11eb-4d7e-a6f4-e026aec9043f@gi1g2000vbb.googlegroups.com>
In reply to#11581
On Nov 17, 11:21 am, <address...@invalid.invalid> wrote:
> I think this is a typical usecase for the "visitor" pattern:

Tobi, this is exactly what I was looking for.  Case closed.

Thank you,

Chris

[toc] | [prev] | [next] | [standalone]


#11590

FromWerner <werasm@gmail.com>
Date2011-11-17 23:30 -0800
Message-ID<af7094e6-fbc9-4bed-b9c7-c1c4ad9d6841@w7g2000yqc.googlegroups.com>
In reply to#11584
On Nov 18, 12:09 am, Chris Stankevitz <chrisstankev...@gmail.com>
wrote:
> On Nov 17, 11:21 am, <address...@invalid.invalid> wrote:
>
> > I think this is a typical usecase for the "visitor" pattern:
>
> Tobi, this is exactly what I was looking for.  Case closed.

What happens when new shapes are added to the hierarchy?

Kind regards,

Werner

[toc] | [prev] | [next] | [standalone]


#11600

FromBart v Ingen Schenau <bart@ingen.ddns.info>
Date2011-11-18 14:45 +0100
Message-ID<ja5nie$9f2$1@localhost.localdomain>
In reply to#11590
Werner Wrote:

> On Nov 18, 12:09 am, Chris Stankevitz <chrisstankev...@gmail.com>
> wrote:
> > On Nov 17, 11:21 am, <address...@invalid.invalid> wrote:
> >
> > > I think this is a typical usecase for the "visitor"pattern:
> >
> > Tobi, this is exactly what I was looking for.  Case closed.
> 
> What happens when new shapes are added to the hierarchy?

Then the ShapeVisitor and derived visitors must be extended to support the
new shape.
This is no different from the original code with an if-else ladder based on
the result from dynamic_cast, except that the compiler is better able to
help warn you if you forgot to update a visitor.

> 
> Kind regards,
> 
> Werner

Bart v Ingen Schenau

[toc] | [prev] | [next] | [standalone]


#11596

FromGoran <goran.pusic@gmail.com>
Date2011-11-18 02:58 -0800
Message-ID<b1edcb6b-ec3d-4b9f-8da7-897eb0bd1ffe@l19g2000yqc.googlegroups.com>
In reply to#11581
On Nov 17, 8:21 pm, <address...@invalid.invalid> wrote:
> Chris Stankevitz <chrisstankev...@gmail.com> 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 << "/>";
> >     }
> >   }
> > };
>
> I think this is a typical usecase for the "visitor" pattern:
>
> // Library A
>
> class ShapeVisitor;
> class Shape { public: virtual void accept(ShapeVisitor& visitor) = 0; };
> class Circle : public Shape {
> public:
>   virtual void accept(ShapeVisitor& visitor) { visitor.visit(*this); }};
>
> class Square : public Shape {
> public:
>   virtual void accept(ShapeVisitor& visitor) { visitor.visit(*this); }};
>
> class ShapeVisitor {
>   virtual void visit(Square& square) = 0;
>   virtual void visit(Circle& circle) = 0;
>
> };
>
> // Library B
> class XmlWriter : public ShapeVisitor {
>   virtual void visit(Square& square) {
>     // do something
>   }
>   virtual void visit(Circle& circle) {
>     // do something
>   }
>   void write(Shape& shape) {
>     shape.accept(*this);
>   }
>
> };
>
> Tobi

Visitor requires changing Shape and the rest of Lib A, which OP said
didn't want elsethread.

Goran.

[toc] | [prev] | [next] | [standalone]


#11603

FromTobias Müller <troplin@bluewin.ch>
Date2011-11-18 16:56 +0000
Message-ID<221431000343327612.468866troplin-bluewin.ch@news.aioe.org>
In reply to#11596
Goran <goran.pusic@gmail.com> wrote:
> Visitor requires changing Shape and the rest of Lib A, which OP said
> didn't want elsethread.
> 
> Goran.

Fair point. However, when reading the rest of the thread I had the
impression that what the OP really wanted is not to leave Lib A unchanged,
but rather not to include any XML specific features.
And a vistior interface is quite abstract and useful for a library like A.

And apparently the OP is happy with that solution.

Tobi.

[toc] | [prev] | [next] | [standalone]


#11608

FromChris Stankevitz <chrisstankevitz@gmail.com>
Date2011-11-18 17:27 -0800
Message-ID<64977197-4215-47b0-8cb6-d6be899f0cfd@i12g2000prl.googlegroups.com>
In reply to#11603
Goran <goran.pu...@gmail.com> wrote:
> Visitor requires changing Shape and the rest of Lib A, which OP said
> didn't want elsethread.

On Nov 18, 8:56 am, Tobias Müller <trop...@bluewin.ch> wrote:
> However, when reading the rest of the thread I had the
> impression that what the OP really wanted is not to leave Lib A unchanged,
> but rather not to include any XML specific features.

You are both correct.  At first I said "without modifying LibA" to
discourage "just put the XML specific feature in LibA" (turned out
this didn't discourage the first few replies).  Later, as it became
clear I did not pose my question well, my question evolved.  As Tobi
picked up, I did not want XML specific features in Library A but was
willing to make changes to Library A.  However, I can imagine
scenarios in which LibA is not alterable -- in such a case Tobi's
solution would not work.  I apologize for leading the group to believe
that.

> And a vistior interface is quite abstract and useful for a library like A.
>
> And apparently the OP is happy with that solution.

Yes!  Not only was I happy with it, when I saw it I knew immediately
that this was what I was thinking of -- I just didn't have a name for
it or know how to go about it or even know if it was possible with c+
+.

Thank you,

Chris

[toc] | [prev] | [next] | [standalone]


#11597

FromGoran <goran.pusic@gmail.com>
Date2011-11-18 01:29 -0800
Message-ID<e6e17a92-8244-4cef-9d1f-b6a661d55f78@s7g2000yqa.googlegroups.com>
In reply to#11581
On Nov 17, 8:21 pm, <address...@invalid.invalid> wrote:
> Chris Stankevitz <chrisstankev...@gmail.com> 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 << "/>";
> >     }
> >   }
> > };
>
> I think this is a typical usecase for the "visitor" pattern:
>
> // Library A
>
> class ShapeVisitor;
> class Shape { public: virtual void accept(ShapeVisitor& visitor) = 0; };
> class Circle : public Shape {
> public:
>   virtual void accept(ShapeVisitor& visitor) { visitor.visit(*this); }};
>
> class Square : public Shape {
> public:
>   virtual void accept(ShapeVisitor& visitor) { visitor.visit(*this); }};
>
> class ShapeVisitor {
>   virtual void visit(Square& square) = 0;
>   virtual void visit(Circle& circle) = 0;
>
> };
>
> // Library B
> class XmlWriter : public ShapeVisitor {
>   virtual void visit(Square& square) {
>     // do something
>   }
>   virtual void visit(Circle& circle) {
>     // do something
>   }
>   void write(Shape& shape) {
>     shape.accept(*this);
>   }
>
> };

Hmmm... I was thinking of "visitor", but found it inappropriate
because OP said he doesn't want to change library A, and visitor
requires changing the base class.

Goran.

[toc] | [prev] | [next] | [standalone]


Page 2 of 3 — ← Prev page 1 [2] 3  Next page →

Back to top | Article view | comp.lang.c++


csiph-web