470,815 Members | 1,740 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 470,815 developers. It's quick & easy.

Decoupling classes

Hi, I've looked through the FAQ, and I can't seem to find an answer to
this one. Can anyone point me to a design pattern that will produce the
desired behaviour illustrated below please? I know why it doesn't print
"W visiting B", and I've read about double dispatch now too. What I'd
really like though is to keep A and V completely unaware of the
existence of B and W and still end up with "W visiting B" getting
printed. Ideally too I'd like to avoid putting a dynamic_cast in
W::visit(A&).

Is there a nice design pattern for doing this? Or an I searching for the
impossible.

Thanks for any advice,
Alan

class A {
};
class V {
public:
virtual void visit(A& a) = 0;
};

class B : public A {
};

class W : public V {
public:
virtual void visit(A& a) {
std::cout << "W visiting A" << std::endl;
}

virtual void visit(B& b) {
std::cout << "W visiting B" << std::endl;
}
};

int main(void) {
B b;
A a;
A *t = &a;

W *v = new W();

v->visit(*t);
t = &b;
v->visit(*t);

return 0;
}
Apr 25 '06 #1
4 2559
Alan Woodland wrote:
Hi, I've looked through the FAQ, and I can't seem to find an answer to
this one. Can anyone point me to a design pattern that will produce
the desired behaviour illustrated below please? I know why it doesn't
print "W visiting B", and I've read about double dispatch now too.
What I'd really like though is to keep A and V completely unaware of
the existence of B and W and still end up with "W visiting B" getting
printed. Ideally too I'd like to avoid putting a dynamic_cast in
W::visit(A&).

Is there a nice design pattern for doing this? Or an I searching for
the impossible.

Thanks for any advice,
Alan

class A {
};
class V {
public:
virtual void visit(A& a) = 0;
};

class B : public A {
};

class W : public V {
public:
virtual void visit(A& a) {
std::cout << "W visiting A" << std::endl;
}

virtual void visit(B& b) {
std::cout << "W visiting B" << std::endl;
}
};

int main(void) {
B b;
A a;
A *t = &a;

W *v = new W();
Did you mean

V *v = new W();

? You would probably need a virtual destructor in 'V', of course.
It doesn't really matter, though. What you're trying to do _is_
impossible.
v->visit(*t);
t = &b;
v->visit(*t);
No matter how you slice it, the type of 't' is A*. There is no way
for the program to learn where 't' came from. If 'A' or 'B' _were_
polymorphic types, one could try using 'dynamic_cast', yet it might
still fail (say, if 't' is part of another class deriving from 'A').

Double dispatch would help, if you allow that 'B' could know about 'W'.
return 0;
}


What problem are you trying to solve? Perhaps it's possible to do
using templates?

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Apr 25 '06 #2
Alan Woodland wrote:
Hi, I've looked through the FAQ, and I can't seem to find an answer to
this one. Can anyone point me to a design pattern that will produce the
desired behaviour illustrated below please? I know why it doesn't print
"W visiting B", and I've read about double dispatch now too. What I'd
really like though is to keep A and V completely unaware of the
existence of B and W and still end up with "W visiting B" getting
printed. Ideally too I'd like to avoid putting a dynamic_cast in
W::visit(A&).

Is there a nice design pattern for doing this? Or an I searching for the
impossible.

Thanks for any advice,
Alan

class A {
};
class V {
public:
virtual void visit(A& a) = 0;
};

class B : public A {
};

class W : public V {
public:
virtual void visit(A& a) {
std::cout << "W visiting A" << std::endl;
}

virtual void visit(B& b) {
std::cout << "W visiting B" << std::endl;
}
};

int main(void) {
B b;
A a;
A *t = &a;

W *v = new W();

v->visit(*t);
t = &b;
v->visit(*t);

return 0;
}


The "problem" is that you have used a polymorphic reference to treat
your B object as an A object. Thus, the best function overload for
W::visit() is the one that accepts an A-reference. If you instead
passed a B-reference directly, it would happily use the other overload.
If you can't make such a change, you'll have to either downcast using
dynamic_cast to regain the lost type information or redesign. The
dynamic_cast, BTW, would not need to be present in V since W can
override the virtual function that accepts an A-reference. (The
B-reference overload obviously cannot be called with a V object except
where the B object is treated as an A, so V would still be ignorant of
B and W types.)

Cheers! --M

Apr 25 '06 #3
Alan Woodland wrote:
Hi, I've looked through the FAQ, and I can't seem to find an answer to
this one. Can anyone point me to a design pattern that will produce the
desired behaviour illustrated below please? I know why it doesn't print
"W visiting B", and I've read about double dispatch now too. What I'd
really like though is to keep A and V completely unaware of the
existence of B and W and still end up with "W visiting B" getting
printed. Ideally too I'd like to avoid putting a dynamic_cast in
W::visit(A&).


I don't think those two requirements are compatible - either you need V
to know about both A and B (in which case you have the normal, cyclic
visitor pattern), or you need to use dynamic_cast (in which case you
have the acyclic visitor pattern).

Google for "cyclic visitor" and "acyclic visitor". A good implementation
of both is available as part of the Loki library. See
http://sourceforge.net/projects/loki-lib/

Here's your code in acyclic form:

struct V {
virtual ~V(){}
};

struct Base {
virtual ~Base(){}
virtual void accept(V&) = 0;
};

struct A;

struct AV: public virtual V {
virtual void visit(A& a) = 0;
};

struct A: public Base {
virtual void accept(V& v)
{
if (AV* av = dynamic_cast<AV*>(&v))
av->visit(*this);
}
};

struct B;

struct BV: public virtual V {
virtual void visit(B& b) = 0;
};

struct B : public A {
virtual void accept(V& v)
{
if (BV* bv = dynamic_cast<BV*>(&v))
bv->visit(*this);
}
};

#include <iostream>

struct W : public AV, public BV {
virtual void visit(A& a) {
std::cout << "W visiting A" << std::endl;
}

virtual void visit(B& b) {
std::cout << "W visiting B" << std::endl;
}
};

int main(void) {
B b;
A a;
A *t = &a;

W *v = new W();

t->accept(*v);
t = &b;
t->accept(*v);
std::cin.get();
return 0;
}
Tom
Apr 25 '06 #4
Victor Bazarov wrote:
Alan Woodland wrote:
snip
W *v = new W();


Did you mean

V *v = new W();

Yes, sorry
? You would probably need a virtual destructor in 'V', of course.
It doesn't really matter, though. What you're trying to do _is_
impossible. It's as I feared then :( I was just trying to keep the example shorter.
v->visit(*t);
t = &b;
v->visit(*t);


No matter how you slice it, the type of 't' is A*. There is no way
for the program to learn where 't' came from.

Yes, I was hoping there was a gem of wisom that could make it appear
like to not be.
If 'A' or 'B' _were_
polymorphic types, one could try using 'dynamic_cast', yet it might
still fail (say, if 't' is part of another class deriving from 'A'). Yes, thats not too much of a problem though, I can handle that.
Double dispatch would help, if you allow that 'B' could know about 'W'. I thought double dispatch required A knowing about W too? It's fine for
B to know about W I think - they'll both be being compiled into the same
shared object. (See below)
What problem are you trying to solve? Perhaps it's possible to do
using templates?


I've got a group of Data structures that extend one base class. I've
written all the code so far to be very modular, and load a series of
plugins using shared objects (which works beautifuly). The problem now
though is that I want there to be output plugins that can do various
different types of output (e.g, rendering to OpenGL, saving to XML file
etc.) without ending up tying the Data structures to the output type.
The problem is though that the data structures themselves get extended
by some plugins, so adding a 'virtual void render(DataStructure& ds) =
0' pure virtual method in the base class for the OutputPlugin will only
result in the output plugin being aware of the basic information, not
the fact that it may (or may not) have been subclassed. So to sumarise
the code doesn't really feel like it belongs in a 'virtual void
render()' method in the DataStructure class, and putting it in the
OutputPlugin misses the sub classing.

Alan
Apr 25 '06 #5

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

9 posts views Thread by Aguilar, James | last post: by
12 posts views Thread by Langy | last post: by
3 posts views Thread by Ken Allen | last post: by
18 posts views Thread by Edward Diener | last post: by
reply views Thread by George Sakkis | last post: by
7 posts views Thread by Sharon | last post: by
reply views Thread by mihailmihai484 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.