473,395 Members | 1,972 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

Avoid nested "if's"?

I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().

All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().

I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):

void meet_bob(BaseBob b1, BaseBob b2){

if (b1.getType == 1 && b2.getType == 1)
{
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2)
{
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3)
{
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2)
{
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3)
{
cout << "bob2 says hi to bob3" << endl;
}
...
...

}

This is not pretty. If I want to use a switch instead I still have to
make a switch for each case in the outer switch and then I have a design
with nested switches instead (which is even uglier).

Is there some better way to treat this kind of situation or is the use
of nested switches the best solution?
May 10 '07 #1
18 3607
On Thu, 10 May 2007 12:15:15 +0200, desktop wrote:
I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().

All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().
Um... getId() doesn't appear in your code...
I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):

void meet_bob(BaseBob b1, BaseBob b2){
If BaseBob is abstract, you can't pass it by value, since
you can't instantiate it.
if (b1.getType == 1 && b2.getType == 1) {
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2) {
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3) {
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2) {
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3) {
cout << "bob2 says hi to bob3" << endl;
}
...
...

}
I'm guessing at what you want, but how about something like:

void meet_bob(BaseBob* pb1, BaseBob* pb2){

cout << "bob" << pb1->getId() << " says hi to bob" << pb2->getId() << endl;

...

}

assuming a sensible operator << for the return type of getId().

--
Lionel B
May 10 '07 #2
On 10 Maj, 12:38, Lionel B <m...@privacy.netwrote:
On Thu, 10 May 2007 12:15:15 +0200, desktop wrote:
I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().
All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().

Um... getId() doesn't appear in your code...
I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):
void meet_bob(BaseBob b1, BaseBob b2){

If BaseBob is abstract, you can't pass it by value, since
you can't instantiate it.
if (b1.getType == 1 && b2.getType == 1) {
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2) {
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3) {
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2) {
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3) {
cout << "bob2 says hi to bob3" << endl;
}
...
...
}

I'm guessing at what you want, but how about something like:

void meet_bob(BaseBob* pb1, BaseBob* pb2){
void meet_bob(const BaseBob& b1, const BaseBob& b2) {
cout << "bob" << pb1->getId() << " says hi to bob" << pb2->getId() <<endl;
cout << "bob" << b1.getType() << " says hi to bob" << b2.getType()
<< end;

--
Erik Wikström

May 10 '07 #3
Lionel B wrote:
On Thu, 10 May 2007 12:15:15 +0200, desktop wrote:
>I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().

All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().

Um... getId() doesn't appear in your code...
>I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):

void meet_bob(BaseBob b1, BaseBob b2){

If BaseBob is abstract, you can't pass it by value, since
you can't instantiate it.
> if (b1.getType == 1 && b2.getType == 1) {
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2) {
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3) {
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2) {
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3) {
cout << "bob2 says hi to bob3" << endl;
}
...
...

}

I'm guessing at what you want, but how about something like:

void meet_bob(BaseBob* pb1, BaseBob* pb2){

cout << "bob" << pb1->getId() << " says hi to bob" << pb2->getId() << endl;

...

}

assuming a sensible operator << for the return type of getId().
Well I was more looking for at general way to optimize a large number of
nested "if's" or "switches". In each case I might want to do something
more complex that print a string in the future.

Are there alternatives to nested switches when dealing with a large
number of combinations? I was thinking about a tree structure but am nit
sure if that is supported in c++.
May 10 '07 #4
On 5ÔÂ10ÈÕ, ÏÂÎç7ʱ04·Ö, desktop <f...@sss.comwrote:
Well I was more looking for at general way to optimize a large number of
nested "if's" or "switches". In each case I might want to do something
more complex that print a string in the future.

Are there alternatives to nested switches when dealing with a large
number of combinations? I was thinking about a tree structure but am nit
sure if that is supported in c++.
Well, I think there isn't a general way to resolve all the nested "if"
or "switch". But in many situation, polymorphism may be a choice.

May 10 '07 #5
On May 10, 12:15 pm, desktop <f...@sss.comwrote:
I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().

All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().

I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):

void meet_bob(BaseBob b1, BaseBob b2){

if (b1.getType == 1 && b2.getType == 1)
{
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2)
{
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3)
{
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2)
{
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3)
{
cout << "bob2 says hi to bob3" << endl;
}
...
...

}

This is not pretty. If I want to use a switch instead I still have to
make a switch for each case in the outer switch and then I have a design
with nested switches instead (which is even uglier).

Is there some better way to treat this kind of situation or is the use
of nested switches the best solution?
Yes. There is technique called double dispatch which does not use
switch or ifs. Somebody asked when is polimorphysm appropriate.
Whenever you have case that you have actions based on types,
is sure sign to use virtual functions.
Simple implementation of your problem is here:

#include <iostream>
#include <ostream>
using namespace std;

class Bob1;
class Bob2;

class BaseBob{
public:
virtual void accept(BaseBob*)=0;
virtual void visit(Bob1*)=0;
virtual void visit(Bob2*)=0;
// you can add Bob3
virtual ~BaseBob(){}
};

class Bob1: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob1 says hi to Bob1\n";
}
virtual void visit(Bob2* b)
{
cout<<"Bob1 says hi to Bob2\n";
}
};

class Bob2: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob2 says hi to Bob1\n";
}
virtual void visit(Bob2* b)
{
cout<<"Bob2 says hi to Bob2\n";
}
};

int main()
{
Bob1 b1;
Bob2 b2;
b1.accept(&b1);
b1.accept(&b2);
b2.accept(&b1);
b2.accept(&b2);
return 0;
}

Greetings, Branimir.

May 10 '07 #6
Branimir Maksimovic wrote:
On May 10, 12:15 pm, desktop <f...@sss.comwrote:
>I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().

All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().

I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):

void meet_bob(BaseBob b1, BaseBob b2){

if (b1.getType == 1 && b2.getType == 1)
{
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2)
{
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3)
{
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2)
{
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3)
{
cout << "bob2 says hi to bob3" << endl;
}
...
...

}

This is not pretty. If I want to use a switch instead I still have to
make a switch for each case in the outer switch and then I have a design
with nested switches instead (which is even uglier).

Is there some better way to treat this kind of situation or is the use
of nested switches the best solution?

Yes. There is technique called double dispatch which does not use
switch or ifs. Somebody asked when is polimorphysm appropriate.
Whenever you have case that you have actions based on types,
is sure sign to use virtual functions.
Simple implementation of your problem is here:

#include <iostream>
#include <ostream>
using namespace std;

class Bob1;
class Bob2;

class BaseBob{
public:
virtual void accept(BaseBob*)=0;
virtual void visit(Bob1*)=0;
virtual void visit(Bob2*)=0;
This is much better:
virtual void visit( const BaseBob &bob ) = 0;
then adding a visit() for each bob class.

// you can add Bob3
virtual ~BaseBob(){}
};

class Bob1: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob1 says hi to Bob1\n";
}
then do like:
void visit( const BaseBob &bob )
{
std::cout << "Bob" << getId() << " says hi to Bob" << bob.getId()
<< std::endl;
}
virtual void visit(Bob2* b)
{
cout<<"Bob1 says hi to Bob2\n";
}
};

class Bob2: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob2 says hi to Bob1\n";
}
virtual void visit(Bob2* b)
{
cout<<"Bob2 says hi to Bob2\n";
}
};

int main()
{
Bob1 b1;
Bob2 b2;
b1.accept(&b1);
b1.accept(&b2);
b2.accept(&b1);
b2.accept(&b2);
return 0;
}
May 10 '07 #7
On May 10, 6:15 am, desktop <f...@sss.comwrote:
I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().
Looks like a classic case of needing virtual functions.

virtual const string &getName () const = 0 ; // in Base

const string &bob1::getName () const // In derived.
{
return "bob1" ;
}

// repeated by class.

void meet_bob(BaseBob b1, BaseBob b2){
{
cout << b1.getName () << " says hi to " << b2.getName () << endl ;
}

May 10 '07 #8
"anon" <an**@no.nowrote in message
news:f1**********@el-srv04-CHE.srvnet.eastlink.de...
Branimir Maksimovic wrote:
>class Bob1;
class Bob2;

class BaseBob{
public:
virtual void accept(BaseBob*)=0;
virtual void visit(Bob1*)=0;
virtual void visit(Bob2*)=0;

This is much better:
virtual void visit( const BaseBob &bob ) = 0;
then adding a visit() for each bob class.
No, by doing that you remove the double dispatch simulation, which is the
whole point of the visitor pattern. You have two versions of a method, one
accepting Bob1* and one accepting Bob2*, and you want either one of them to
be called depending on the type of the argument (which has BaseBob* as it's
static type). Btw, by doing what you proposed, you're accept method is
completely obsolete, since that just calls the one visit() method that
exists, but you still have to implement it in every class.

As the topicstarter said, this problem is not about simply outputting a
string, but doing something particular depending on the dynamic types of 2
arguments (hence double dispatch), rather than just 1 (single dispatch,
which is supported via virtual methods)

http://en.wikipedia.org/wiki/Visitor_pattern

- Sylvester Hesp
May 10 '07 #9
Branimir Maksimovic wrote:
On May 10, 12:15 pm, desktop <f...@sss.comwrote:
>I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().

All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().

I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):

void meet_bob(BaseBob b1, BaseBob b2){

if (b1.getType == 1 && b2.getType == 1)
{
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2)
{
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3)
{
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2)
{
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3)
{
cout << "bob2 says hi to bob3" << endl;
}
...
...

}

This is not pretty. If I want to use a switch instead I still have to
make a switch for each case in the outer switch and then I have a design
with nested switches instead (which is even uglier).

Is there some better way to treat this kind of situation or is the use
of nested switches the best solution?

Yes. There is technique called double dispatch which does not use
switch or ifs. Somebody asked when is polimorphysm appropriate.
Whenever you have case that you have actions based on types,
is sure sign to use virtual functions.
Simple implementation of your problem is here:

#include <iostream>
#include <ostream>
using namespace std;

class Bob1;
class Bob2;

class BaseBob{
public:
virtual void accept(BaseBob*)=0;
virtual void visit(Bob1*)=0;
virtual void visit(Bob2*)=0;
// you can add Bob3
virtual ~BaseBob(){}
};

class Bob1: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob1 says hi to Bob1\n";
}
virtual void visit(Bob2* b)
{
cout<<"Bob1 says hi to Bob2\n";
}
};

class Bob2: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob2 says hi to Bob1\n";
}
virtual void visit(Bob2* b)
{
cout<<"Bob2 says hi to Bob2\n";
}
};

int main()
{
Bob1 b1;
Bob2 b2;
b1.accept(&b1);
b1.accept(&b2);
b2.accept(&b1);
b2.accept(&b2);
return 0;
}

Greetings, Branimir.
But does this code scale? If I suddenly have 1000 new bobs I would like
to add I need to add 1000 "visit" functions to EACH bob object which
seems as a lot of duplicate work.

It seems a bit unpractical to implement the visit functionality in the
Bob's so maybe a nested switch is still the best solution for a large
number of objects.
May 10 '07 #10
On 10 Maj, 19:52, desktop <f...@sss.comwrote:
Branimir Maksimovic wrote:
On May 10, 12:15 pm, desktop <f...@sss.comwrote:
I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().
All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().
I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):
void meet_bob(BaseBob b1, BaseBob b2){
if (b1.getType == 1 && b2.getType == 1)
{
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2)
{
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3)
{
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2)
{
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3)
{
cout << "bob2 says hi to bob3" << endl;
}
...
...
}
This is not pretty. If I want to use a switch instead I still have to
make a switch for each case in the outer switch and then I have a design
with nested switches instead (which is even uglier).
Is there some better way to treat this kind of situation or is the use
of nested switches the best solution?
Yes. There is technique called double dispatch which does not use
switch or ifs. Somebody asked when is polimorphysm appropriate.
Whenever you have case that you have actions based on types,
is sure sign to use virtual functions.
Simple implementation of your problem is here:
#include <iostream>
#include <ostream>
using namespace std;
class Bob1;
class Bob2;
class BaseBob{
public:
virtual void accept(BaseBob*)=0;
virtual void visit(Bob1*)=0;
virtual void visit(Bob2*)=0;
// you can add Bob3
virtual ~BaseBob(){}
};
class Bob1: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob1 says hi to Bob1\n";
}
virtual void visit(Bob2* b)
{
cout<<"Bob1 says hi to Bob2\n";
}
};
class Bob2: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob2 says hi to Bob1\n";
}
virtual void visit(Bob2* b)
{
cout<<"Bob2 says hi to Bob2\n";
}
};
int main()
{
Bob1 b1;
Bob2 b2;
b1.accept(&b1);
b1.accept(&b2);
b2.accept(&b1);
b2.accept(&b2);
return 0;
}
Greetings, Branimir.

But does this code scale? If I suddenly have 1000 new bobs I would like
to add I need to add 1000 "visit" functions to EACH bob object which
seems as a lot of duplicate work.

It seems a bit unpractical to implement the visit functionality in the
Bob's so maybe a nested switch is still the best solution for a large
number of objects.
If you have that many bobs then either you'd better hope that you
don't have as many different actions to be taken when they meet each
other, in which case you can use inheritance and implement the meeting
in a common base class.

If not I think you'd need a more generic framework which allows all
the needed actions to be taken, and the action taken will be
determined by a lookup in a database of rules which are written in
some other, higher level language.

--
Erik Wikström

May 11 '07 #11
On May 10, 7:52 pm, desktop <f...@sss.comwrote:
Branimir Maksimovic wrote:
On May 10, 12:15 pm, desktop <f...@sss.comwrote:
I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().
All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().
I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):
void meet_bob(BaseBob b1, BaseBob b2){
if (b1.getType == 1 && b2.getType == 1)
{
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2)
{
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3)
{
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2)
{
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3)
{
cout << "bob2 says hi to bob3" << endl;
}
...
...
}
This is not pretty. If I want to use a switch instead I still have to
make a switch for each case in the outer switch and then I have a design
with nested switches instead (which is even uglier).
Is there some better way to treat this kind of situation or is the use
of nested switches the best solution?
Yes. There is technique called double dispatch which does not use
switch or ifs. Somebody asked when is polimorphysm appropriate.
Whenever you have case that you have actions based on types,
is sure sign to use virtual functions.
Simple implementation of your problem is here:
#include <iostream>
#include <ostream>
using namespace std;
class Bob1;
class Bob2;
class BaseBob{
public:
virtual void accept(BaseBob*)=0;
virtual void visit(Bob1*)=0;
virtual void visit(Bob2*)=0;
// you can add Bob3
virtual ~BaseBob(){}
};
class Bob1: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob1 says hi to Bob1\n";
}
virtual void visit(Bob2* b)
{
cout<<"Bob1 says hi to Bob2\n";
}
};
class Bob2: public BaseBob{
public:
virtual void accept(BaseBob* b)
{
b->visit(this);
}
virtual void visit(Bob1* b)
{
cout<<"Bob2 says hi to Bob1\n";
}
virtual void visit(Bob2* b)
{
cout<<"Bob2 says hi to Bob2\n";
}
};
int main()
{
Bob1 b1;
Bob2 b2;
b1.accept(&b1);
b1.accept(&b2);
b2.accept(&b1);
b2.accept(&b2);
return 0;
}
Greetings, Branimir.

But does this code scale? If I suddenly have 1000 new bobs I would like
to add I need to add 1000 "visit" functions to EACH bob object which
seems as a lot of duplicate work.

It seems a bit unpractical to implement the visit functionality in the
Bob's so maybe a nested switch is still the best solution for a large
number of objects.
If you have 1000 bob objects and your actions are always same,
so you think you can handle this with one switch case,
that indicates that you need just one bob class not 1000.
Seems that you are mixing objects and classes.

#include <iostream>
#include <ostream>
using namespace std;

class Bob{
public:
Bob(unsigned id):id_(id){}
unsigned getId()const{return id_;}
void action(const Bob& b)
{
cout<<b.getId()<<" says hello to:"<<getId()<<'\n';
}
private:
unsigned id_;
};

int main()
{
Bob b1(1),b2(2);
b1.action(b2);
}

Greetings, Branimir.

May 11 '07 #12
On May 10, 7:52 pm, desktop <f...@sss.comwrote:
Branimir Maksimovic wrote:
On May 10, 12:15 pm, desktop <f...@sss.comwrote:
I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().
All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().
Yes. There is technique called double dispatch which does not use
switch or ifs. Somebody asked when is polimorphysm appropriate.
Whenever you have case that you have actions based on types,
is sure sign to use virtual functions.
Usually.
Simple implementation of your problem is here:
[...]
But does this code scale? If I suddenly have 1000 new bobs I would like
to add I need to add 1000 "visit" functions to EACH bob object which
seems as a lot of duplicate work.
If you have 1000 different bob types, and each combination of
bob types has a different action, then it will be a lot of work,
regardless of how you do it. That's a million different actions
that have to be written; it's a nested switch with a total of a
million different cases (which will probably exceed the resource
limits of your compiler).

In general, Branimir has shown you the canonical implementation
of double dispatch (except that I'd have used references instead
of pointers). Another alternative is to define a map:

std::map< std::pair< std::type_info*, std::type_info* >,
void (*)( bob*, bob* ),
TypeCmp >

This allows using free functions, rather than virtual member
functions, and does avoid having to modify the base class each
type you add a derived class. But it still requires writing n^2
functions (and the functions cannot access any member data).
And the calling sequence is a bit more complicated, since it
entails a map lookup using the typeid() of both objects.

A variant I sometimes use, if there is a generic action that I
can use for most combinations, is to have a simple array of
pointers to functions, visiting each in order. Each function
returns true if it works, false otherwise, and I loop over the
array until one works. Each special function starts something
like:

bool
specialnm( bob* b1, bob* b2 )
{
bobn* p1 = dynamic_cast< bobn* >( b1 ) ;
bonm* p2 = dynamic_cast< bobm* >( b2 ) ;
if ( p1 != NULL && p2 != NULL ) {
// special action...
}
return p1 != NULL & p2 != NULL ;
}

The last function in the array is the generic one, and always
returns true.
It seems a bit unpractical to implement the visit functionality in the
Bob's so maybe a nested switch is still the best solution for a large
number of objects.
Double dispatch works on types, not on objects. It's the number
of types and of distinct actions which counts.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 11 '07 #13
On May 10, 8:15 pm, desktop <f...@sss.comwrote:
....
Is there some better way to treat this kind of situation or is the use
of nested switches the best solution?
How extenisble does it need to be ? If you need extensibility, you
should not use a switch or nested if's at all.

The domain specific part of the question is how much do you need to
know about each type. A classic problem is shapes and one type of
thing you want to do to shapes is find out the points of lines of
intersection. If you have N shapes and you introduce shape N+1 then
you have N new methods to determine interestcion points, some of these
can degenerate into combinations of other methodologies but there is
little you can do but work though each one.

So, just how complex if your problem in particular ?

May 11 '07 #14
James Kanze wrote:
On May 10, 7:52 pm, desktop <f...@sss.comwrote:
>Branimir Maksimovic wrote:
>>On May 10, 12:15 pm, desktop <f...@sss.comwrote:
I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().
>>>All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().
>>Yes. There is technique called double dispatch which does not use
switch or ifs. Somebody asked when is polimorphysm appropriate.
Whenever you have case that you have actions based on types,
is sure sign to use virtual functions.

Usually.
>>Simple implementation of your problem is here:

[...]
>But does this code scale? If I suddenly have 1000 new bobs I would like
to add I need to add 1000 "visit" functions to EACH bob object which
seems as a lot of duplicate work.

If you have 1000 different bob types, and each combination of
bob types has a different action, then it will be a lot of work,
regardless of how you do it. That's a million different actions
that have to be written; it's a nested switch with a total of a
million different cases (which will probably exceed the resource
limits of your compiler).

In general, Branimir has shown you the canonical implementation
of double dispatch (except that I'd have used references instead
of pointers). Another alternative is to define a map:

std::map< std::pair< std::type_info*, std::type_info* >,
void (*)( bob*, bob* ),
TypeCmp >

This allows using free functions, rather than virtual member
functions, and does avoid having to modify the base class each
type you add a derived class. But it still requires writing n^2
functions (and the functions cannot access any member data).
And the calling sequence is a bit more complicated, since it
entails a map lookup using the typeid() of both objects.

A variant I sometimes use, if there is a generic action that I
can use for most combinations, is to have a simple array of
pointers to functions, visiting each in order. Each function
returns true if it works, false otherwise, and I loop over the
array until one works. Each special function starts something
like:

bool
specialnm( bob* b1, bob* b2 )
{
bobn* p1 = dynamic_cast< bobn* >( b1 ) ;
bonm* p2 = dynamic_cast< bobm* >( b2 ) ;
if ( p1 != NULL && p2 != NULL ) {
// special action...
}
return p1 != NULL & p2 != NULL ;
}

The last function in the array is the generic one, and always
returns true.
>It seems a bit unpractical to implement the visit functionality in the
Bob's so maybe a nested switch is still the best solution for a large
number of objects.

Double dispatch works on types, not on objects. It's the number
of types and of distinct actions which counts.

But each of my objects are instansiated from class. As I have learned
creating a new class is the same as creating a new type. So in my case
if I have 100 different objects I also have 100 classes = 100 types. Its
these 100 different classes that I make the double dispatch over.
May 11 '07 #15
On May 11, 1:31 pm, James Kanze <james.ka...@gmail.comwrote:
On May 10, 7:52 pm, desktop <f...@sss.comwrote:
In general, Branimir has shown you the canonical implementation
of double dispatch (except that I'd have used references instead
of pointers).
Well, I have adopted practice to use pointer if function has
non const parameter, and reference if parameter is const,
because of C programmers that moved to C++ as they
don't expect for function to change value of parameter
if it is passed without '&' operator.

Greetings, Branimir.
May 11 '07 #16
On 2007-05-11 16:00, desktop wrote:
James Kanze wrote:
>On May 10, 7:52 pm, desktop <f...@sss.comwrote:
[snip]
>>It seems a bit unpractical to implement the visit functionality in the
Bob's so maybe a nested switch is still the best solution for a large
number of objects.

Double dispatch works on types, not on objects. It's the number
of types and of distinct actions which counts.


But each of my objects are instansiated from class. As I have learned
creating a new class is the same as creating a new type. So in my case
if I have 100 different objects I also have 100 classes = 100 types. Its
these 100 different classes that I make the double dispatch over.
Yes, but an object is not a class, an object is an instance of a class
and all instances of a specific class have the same type (namely the class).

Consider the following:

// Declare a class Foo
class Foo {
int i;
public:
Foo(int i_) :i(i_) {}
};

// Create a vector storing objects of type Foo
std::vector<Foofoos;

// Create 100 Foo-objects
for (int i = 0; i < 100; ++i)
foos.push_back(Foo(i));

Here I have 1 class (Foo) which is the same as having one type, but I
have 100 distinct objects, all instances of the Foo class.

--
Erik Wikström
May 11 '07 #17
On May 11, 4:00 pm, desktop <f...@sss.comwrote:
James Kanze wrote:
On May 10, 7:52 pm, desktop <f...@sss.comwrote:
Branimir Maksimovic wrote:
Double dispatch works on types, not on objects. It's the number
of types and of distinct actions which counts.
But each of my objects are instansiated from class. As I have learned
creating a new class is the same as creating a new type. So in my case
if I have 100 different objects I also have 100 classes = 100 types. Its
these 100 different classes that I make the double dispatch over.
If you have 100 different types, then you have to write 10000
functions to handle every combination. There are no two ways
around that.

--
James Kanze (Gabi Software) email: ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 11 '07 #18
desktop wrote:
I have 3 types of objects: bob1, bob2 and bob3. Each object is
identified by a unique ID which gets returned by the function getId().

All bobs are descendants from class BaseBob which is an abstract class
that has the virtual function getId().

I then have a function that prints a special string when a bob meets
another bob (all combinations of bobs has to meet and since the are
schizophrenic they can also meet themselves):

void meet_bob(BaseBob b1, BaseBob b2){

if (b1.getType == 1 && b2.getType == 1)
{
cout << "bob1 says hi to bob1" << endl;
}
else if (b1.getType == 1 && b2.getType == 2)
{
cout << "bob1 says hi to bob2" << endl;
}
else if (b1.getType == 1 && b2.getType == 3)
{
cout << "bob1 says hi to bob3" << endl;
}
else if (b1.getType == 2 && b2.getType == 2)
{
cout << "bob2 says hi to bob2" << endl;
}
else if (b1.getType == 2 && b2.getType == 3)
{
cout << "bob2 says hi to bob3" << endl;
}
...
...

}

This is not pretty. If I want to use a switch instead I still have to
make a switch for each case in the outer switch and then I have a design
with nested switches instead (which is even uglier).

Is there some better way to treat this kind of situation or is the use
of nested switches the best solution?
It would appear to me you need some from of pattern and action object
lookup table, then a simple loop calling a member function could end
if the action is executed. This should be expandable and somewhat easier
to rearrange reliably. In brief you'll need an array/vector of base
object pointers to test for a particular id pattern, and an object that
can hold the test pattern and do the test based on how it's configured.
The code then just becomes a loop something like the following based on
using vectors for the list of testing objects and list of test objects

for (iTest = vTest.begin();iTest != vTest.end();++iTest)
{
if (iTest->IsDone(vObj))
{
break;
}
}

Any suggested improvements, errors or changes are welcome

JB
May 15 '07 #19

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: Fabrice Régnier | last post by:
Hi ;) Everything is in the title. the prob is "boo" is displayed ! Can you believe it ? Thanx for anyone who can help me. fabrice.
40
by: Steve Juranich | last post by:
I know that this topic has the potential for blowing up in my face, but I can't help asking. I've been using Python since 1.5.1, so I'm not what you'd call a "n00b". I dutifully evangelize on the...
145
by: Sidney Cadot | last post by:
Hi all, In a discussion with Tak-Shing Chan the question came up whether the as-if rule can cover I/O functions. Basically, he maintains it can, and I think it doesn't. Consider two...
2
by: MilanB | last post by:
Hi, Is there a way to force Visual C++ compiler (VS 2003) to give an error or notice in case of IF statment have "=" instead of "==" for example if(locationID = 10351) is passing, but I...
2
by: marsarden | last post by:
write code like: int main(void) { int a=10; if(a<20) {} } Compiler ok on dev-cpp . don't we have to add a ";" after if
37
by: jht5945 | last post by:
For example I wrote a function: function Func() { // do something } we can call it like: var obj = new Func(); // call it as a constructor or var result = Func(); // call it as...
33
by: Snis Pilbor | last post by:
With the "as if" rule in play, doesn't that effectively render the "register" keyword completely useless? Example: I make a silly compiler which creates code that goes out of its way to take a...
9
by: chutsu | last post by:
hi I got a simple program, and I was wondering how do you check if the string in an array = a string. For example if I put "APPLE" in array Array then how can I check it with a if statement. if...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.