Replacement for "switch" that can use non-const values?

I

int2str

All,

What is a good way to replace a simple switch() statement that relies
on #define'ed magic numbers, with a mechanism that can work with
variable values?

Consider the following code:

#include <iostream>

#define FUNC_1 1000

unsigned GetInt()
{
return 3000;
}

const unsigned FUNC_2 = GetInt();

class Test
{
public:
void Func1()
{
std::cout << "Test::Func1()\n";
}

void Func2()
{
std::cout << "Test::Func2()\n";
}

void Dispatch( unsigned func )
{
switch ( func )
{
case FUNC_1:
Func1();
break;

case FUNC_2: // <-- ERROR!
/*
* switch.cpp:33: error: `FUNC_2' cannot
* appear in a constant-expression
*/
Func2();
break;
}
}
};

int main()
{
Test t;
t.Dispatch( FUNC_1 );
t.Dispatch( FUNC_2 );
}

I've tried a multitude of designs I could come up with to solve this
problem, but non seem elegant or portable when it comes to derived
classes etc. For example, I've tried a std::map with pointers to
member functions, which I liked, but I had trouble making that worked
for derived classes since I needed some template "magic" to get the
function pointers for any given class to work.

Lots of if/else would of course work, but that looks ugly as sin and
can get very, very repetitive, especially when there's a lot of
functions to dispatch.

So what would be a good design that can be applied cleanly to classes
of various level of inheritance?

Thanks,
Andre
 
I

Ian Collins

All,

What is a good way to replace a simple switch() statement that relies
on #define'ed magic numbers, with a mechanism that can work with
variable values?
Why should a switch depend on #define'ed magic numbers? Switch cases
use compile time constants.

A common technique for "dynamic dispatch" is to use a map of functors or
function pointers, indexed by what ever makes sense as the key for that
particular application.

A very simple example:

typedef void (*Fn)();

std::map<int, Fn> table;

void dispatcher( int n )
{
table[n]();
}
 
I

int2str

Why should a switch depend on #define'ed magic numbers? Switch cases
use compile time constants.

It doesn't. It's just what I am trying to replace.
A common technique for "dynamic dispatch" is to use a map of functors or
function pointers, indexed by what ever makes sense as the key for that
particular application.

A very simple example:

typedef void (*Fn)();

std::map<int, Fn> table;

void dispatcher( int n )
{
table[n]();

}

Yes, that's exactly what I'd like to do. Except that I need pointers
to member functions, which gets complicated (at least for me). I can't
come up with a clean setup.

All hints would be appreciated.

Thanks,
Andre
 
J

Jim Langston

Why should a switch depend on #define'ed magic numbers? Switch cases
use compile time constants.

It doesn't. It's just what I am trying to replace.
A common technique for "dynamic dispatch" is to use a map of functors or
function pointers, indexed by what ever makes sense as the key for that
particular application.

A very simple example:

typedef void (*Fn)();

std::map<int, Fn> table;

void dispatcher( int n )
{
table[n]();

}

Yes, that's exactly what I'd like to do. Except that I need pointers
to member functions, which gets complicated (at least for me). I can't
come up with a clean setup.

All hints would be appreciated.

A switch simply replaces an if...else block.

void Dispatch( unsigned func )
{
if ( func == FUNC_1 )
{
Func1();
break;
}
else if ( func == FUNC_2 )
{
Func2();
break;
}
}
 
I

Ian Collins

Why should a switch depend on #define'ed magic numbers? Switch cases
use compile time constants.

It doesn't. It's just what I am trying to replace.
A common technique for "dynamic dispatch" is to use a map of functors or
function pointers, indexed by what ever makes sense as the key for that
particular application.

A very simple example:

typedef void (*Fn)();

std::map<int, Fn> table;

void dispatcher( int n )
{
table[n]();

}

Yes, that's exactly what I'd like to do. Except that I need pointers
to member functions, which gets complicated (at least for me). I can't
come up with a clean setup.

All hints would be appreciated.
Pointers to members of the same class, or different classes?
 
I

int2str

It doesn't. It's just what I am trying to replace.
A common technique for "dynamic dispatch" is to use a map of functors or
function pointers, indexed by what ever makes sense as the key for that
particular application.
A very simple example:
typedef void (*Fn)();
std::map<int, Fn> table;
void dispatcher( int n )
{
table[n]();
}
Yes, that's exactly what I'd like to do. Except that I need pointers
to member functions, which gets complicated (at least for me). I can't
come up with a clean setup.
All hints would be appreciated.

Pointers to members of the same class, or different classes?

Same class.
 
H

hurcan solter

from the top of my head;

#include <iostream>
#include <map>


class A{
public:
void foo1(){std::cout<<"foo1\n";}
void foo2(){std::cout<<"foo2\n";}

};
typedef void(A::*fp)();
std::map<int,fp> fpmap;
void dispatch(A& a,int index)
{
(a.*fpmap[index])();
}
int main()
{
fpmap[1]=&A::foo1;
fpmap[2]=&A::foo2;
A a;
dispatch(a,1);
dispatch(a,2);

}

not very much different from ordinary function pointer really.
 
I

Ian Collins

Same class.
The just change the typedef to be a pointer to member type.

#include <map>

struct X {
void fn() {}
};

X x;

typedef void (X::*Fn)();

std::map<int, Fn> table;

void loader( int n, Fn fn) {
table[n] = fn;
}

void dispatcher( int n ) {
(x.*table[n])();
}

int main() {
loader( 1, &X::fn );
dispatcher( 1 );
}
 
J

James Kanze

Yes, but IMHO that's very ugly and repetitive.

It's actually better structured than the switch you started
with. It's definitly the classical solution.

If not, you can always use a map:

class DoIt
{
public:
virtual ~DoIt() {}
virtual void operator()() const = 0 ;
} ;

std::map< int, DoIt const* > myMap ;

But you have to define a separate derived class for each case,
and initialize the map somehow. It may be appropriate, but it
does a lot more than a switch, and if a switch was fully
appropriate, then the chained if/else if would probably be more
appropriate. (On the other hand, if the switch was being used
simply because in C, you didn't have any other mechanism, and
each case of the switch is just a single function call, then the
idiom using a map might be more appropriate. Done correctly,
there is no central point where all of the cases are enumerated.)
 
F

Frank Birbacher

Hi!

It doesn't. It's just what I am trying to replace.

Switch works with compile time constants. But FUNC_2 is not a compile
time constant in your example. Its value is not computed at compile time
but at run time.
Yes, that's exactly what I'd like to do. Except that I need pointers
to member functions, which gets complicated (at least for me). I can't
come up with a clean setup.

I tried some dynamic casting recently:

Work with boost::function<void (BaseClass*)> and put a function into it,
that dynamic_casts its argument to the derived class and then calls the
member function on it. Then you can have these boost::function instances
in a map or array/vector (when the key is an int).

Frank
 
I

int2str

[long post warning - lots of (bad?) code ahead...]

Same class.

The just change the typedef to be a pointer to member type.

#include <map>

struct X {
void fn() {}

};

X x;

typedef void (X::*Fn)();

std::map<int, Fn> table;

void loader( int n, Fn fn) {
table[n] = fn;

}

void dispatcher( int n ) {
(x.*table[n])();

}

int main() {
loader( 1, &X::fn );
dispatcher( 1 );

}

Ok, so I lied...
I actually do have different classes. Since I'm trying to make this
generic so I can apply the mechanism to various classes. Furthermore,
I'd like something where I can apply the mechanism to a base class,
but also use it in a derived class.

So here's what I came up with:

---------- FILE: dynamicdispatch.h ----------

Link to formatted source code here:
http://ironcreek.net/code/dynamicdispatch.h

#ifndef DYNAMICDISPATCH_H
#define DYNAMICDISPATCH_H

#include <map>

#define REGISTERMSG(msg, class, func) RegisterHandler( msg, new
MsgHandler<class>( this, &class::func ));

class IMsgHandler
{
public:
virtual unsigned Handle( unsigned, unsigned ) = 0;
};

template <typename T>
class MsgHandler : public IMsgHandler
{
public:
typedef unsigned (T::*HandlerFunc)( unsigned, unsigned );

MsgHandler( T* pobj, HandlerFunc handler )
: obj( pobj )
, func( handler )
{
}

unsigned Handle( unsigned p1, unsigned p2 )
{
return (obj->*func)( p1, p2 );
}

protected:
T *obj;
HandlerFunc func;
};

class DynamicDispatch
{
public:
~DynamicDispatch()
{
std::map<unsigned, IMsgHandler*>::iterator ii;
for( ii = func_map.begin(); ii != func_map.end(); ++ii )
delete ii->second;
}

void RegisterHandler( unsigned msg, IMsgHandler *p_handler )
{
func_map[ msg ] = p_handler;
}

unsigned HandleMessage( unsigned msg, unsigned p1, unsigned p2 )
{
std::map<unsigned, IMsgHandler*>::iterator ii =
func_map.find( msg );
if ( ii != func_map.end() )
return ii->second->Handle( p1, p2 );
return 0;
}

protected:
std::map<unsigned, IMsgHandler*> func_map;
};

#endif


---------- FILE: funcmap.cpp ----------

Link to formatted source code here:
http://ironcreek.net/code/funcmap.cpp

#include <iostream>
#include "dynamicdispatch.h"

const unsigned FIRST_EVENT = 1000;
const unsigned SECOND_EVENT = 2000;
const unsigned THIRD_EVENT = 3000;

class Dialog : public DynamicDispatch
{
public:
Dialog()
{
REGISTERMSG( THIRD_EVENT, Dialog, MyEvent );
}

unsigned MyEvent( unsigned, unsigned )
{
std::cout << "Dialog::MyEvent()\n";
return 0;
}
};

class MyDlg : public Dialog
{
public:
MyDlg()
{
REGISTERMSG( FIRST_EVENT, MyDlg, Func1 );
REGISTERMSG( SECOND_EVENT, MyDlg, Func2 );
}

unsigned Func1( unsigned p1, unsigned p2 )
{
std::cout << "MyDlg::Func1() p1=" << p1 << " p2=" << p2 <<
"\n";
return 0;
}

unsigned Func2( unsigned p1, unsigned p2 )
{
std::cout << "MyDlg::Func2() p1=" << p1 << " p2=" << p2 <<
"\n";
return 0;
}

void Test()
{
HandleMessage( FIRST_EVENT, 11, 22 );
HandleMessage( SECOND_EVENT, 11, 22 );
HandleMessage( THIRD_EVENT, 11, 22 );
}
};

int main()
{
MyDlg dlg;
dlg.Test();
}


Now, I am _reasonably_ happy with that code - except for one thing.
The macro (REGISTERMSG) I use to register the message handlers. But
the alternative syntax that the macro masks, is a bit hard on the eye
if repeated often...

So what do you guys think about this code and are there any
suggestions to make the REGISTERMSG() functionality not a macro, but
still as clean as possible?

Thanks,
Andre
 
J

Jim Langston

[long post warning - lots of (bad?) code ahead...]

(e-mail address removed) wrote:
Yes, that's exactly what I'd like to do. Except that I need pointers
to member functions, which gets complicated (at least for me). I
can't
come up with a clean setup.
All hints would be appreciated.
Pointers to members of the same class, or different classes?
Same class.

The just change the typedef to be a pointer to member type.

#include <map>

struct X {
void fn() {}

};

X x;

typedef void (X::*Fn)();

std::map<int, Fn> table;

void loader( int n, Fn fn) {
table[n] = fn;

}

void dispatcher( int n ) {
(x.*table[n])();

}

int main() {
loader( 1, &X::fn );
dispatcher( 1 );

}

Ok, so I lied...
I actually do have different classes. Since I'm trying to make this
generic so I can apply the mechanism to various classes. Furthermore,
I'd like something where I can apply the mechanism to a base class,
but also use it in a derived class.

So here's what I came up with:

---------- FILE: dynamicdispatch.h ----------

Link to formatted source code here:
http://ironcreek.net/code/dynamicdispatch.h

#ifndef DYNAMICDISPATCH_H
#define DYNAMICDISPATCH_H

#include <map>

#define REGISTERMSG(msg, class, func) RegisterHandler( msg, new
MsgHandler<class>( this, &class::func ));

class IMsgHandler
{
public:
virtual unsigned Handle( unsigned, unsigned ) = 0;
};

template <typename T>
class MsgHandler : public IMsgHandler
{
public:
typedef unsigned (T::*HandlerFunc)( unsigned, unsigned );

MsgHandler( T* pobj, HandlerFunc handler )
: obj( pobj )
, func( handler )
{
}

unsigned Handle( unsigned p1, unsigned p2 )
{
return (obj->*func)( p1, p2 );
}

protected:
T *obj;
HandlerFunc func;
};

class DynamicDispatch
{
public:
~DynamicDispatch()
{
std::map<unsigned, IMsgHandler*>::iterator ii;
for( ii = func_map.begin(); ii != func_map.end(); ++ii )
delete ii->second;
}

void RegisterHandler( unsigned msg, IMsgHandler *p_handler )
{
func_map[ msg ] = p_handler;
}

unsigned HandleMessage( unsigned msg, unsigned p1, unsigned p2 )
{
std::map<unsigned, IMsgHandler*>::iterator ii =
func_map.find( msg );
if ( ii != func_map.end() )
return ii->second->Handle( p1, p2 );
return 0;
}

protected:
std::map<unsigned, IMsgHandler*> func_map;
};

#endif


---------- FILE: funcmap.cpp ----------

Link to formatted source code here:
http://ironcreek.net/code/funcmap.cpp

#include <iostream>
#include "dynamicdispatch.h"

const unsigned FIRST_EVENT = 1000;
const unsigned SECOND_EVENT = 2000;
const unsigned THIRD_EVENT = 3000;

class Dialog : public DynamicDispatch
{
public:
Dialog()
{
REGISTERMSG( THIRD_EVENT, Dialog, MyEvent );
}

unsigned MyEvent( unsigned, unsigned )
{
std::cout << "Dialog::MyEvent()\n";
return 0;
}
};

class MyDlg : public Dialog
{
public:
MyDlg()
{
REGISTERMSG( FIRST_EVENT, MyDlg, Func1 );
REGISTERMSG( SECOND_EVENT, MyDlg, Func2 );
}

unsigned Func1( unsigned p1, unsigned p2 )
{
std::cout << "MyDlg::Func1() p1=" << p1 << " p2=" << p2 <<
"\n";
return 0;
}

unsigned Func2( unsigned p1, unsigned p2 )
{
std::cout << "MyDlg::Func2() p1=" << p1 << " p2=" << p2 <<
"\n";
return 0;
}

void Test()
{
HandleMessage( FIRST_EVENT, 11, 22 );
HandleMessage( SECOND_EVENT, 11, 22 );
HandleMessage( THIRD_EVENT, 11, 22 );
}
};

int main()
{
MyDlg dlg;
dlg.Test();
}


Now, I am _reasonably_ happy with that code - except for one thing.
The macro (REGISTERMSG) I use to register the message handlers. But
the alternative syntax that the macro masks, is a bit hard on the eye
if repeated often...

So what do you guys think about this code and are there any
suggestions to make the REGISTERMSG() functionality not a macro, but
still as clean as possible?

I can't understand why you would want to switch from using if...else to this
kludge. You thought if...else was ugly and repetitive but this isn't?
 
J

Jorgen Grahn

Yes, but IMHO that's very ugly and repetitive.

- You lose the word "case" and gain "else if"
- You lose the word "break" and gain a few curly brackets
- You repeat the name of the variable (you may want to create
a const variable with a short name, if the expression in switch(...)
was long and unwieldy or expensive).

No big deal if you format it sensibly, IMHO.

/Jorgen
 
I

Ian Collins

Jorgen said:
- You lose the word "case" and gain "else if"
- You lose the word "break" and gain a few curly brackets
- You repeat the name of the variable (you may want to create
a const variable with a short name, if the expression in switch(...)
was long and unwieldy or expensive).

No big deal if you format it sensibly, IMHO.
You forgot

- You loose the simple optimisation of a jump table with appropriate
case labels.
 
G

Gianni Mariani

Jim Langston wrote:
....
I can't understand why you would want to switch from using if...else to this
kludge. You thought if...else was ugly and repetitive but this isn't?

Using the methodology described here, you can extend the system without
needing to change the dispatch code. Centralized dispatchers (if/else
or switch) can become very large and confusing very quickly with a
moderate number of cases.

Dynamic dispatchers also conform to the "code is in one place" rule. It
means if you need a new case in your switch statement, you don't have to
change the code other than the one place where you define all the
aspects of that new case. I had worked on one system that when a new
message type was added, 11 different files needed to be "fixed".
Needless to say it was a very tedious and error prone thing to do.
Using the dynamic dispatch mechanism, you would only need to add a new
file containing all the mechanisms you need.

Think of systems like driver modules in a kernel or protocol handlers etc.

You can take this to extremes where the dynamic selection of the case is
actually a complex process and it allows you to eliminate considerable
chunks of if/else garbage. This I used in a system where the processing
was dependent on a large number of dynamic factors.

C++ also allows you to use the technique statically with templates using
partial specializations. i.e. you can change the behaviour of the code
simply by adding a specialization of a class. I've used that in a DXF
file reader which was one of those "worked first time" miracles. The
nice thing with the DXF file reader architecture, I don't even need to
add new code body, all I have to do is add a new specialized class. The
code also reflects closely to the documentation which means that it is
easy to verify that the code is doing what the documentation expects.
The corresponding if/else or switch code would have been a nightmare to
maintain and validate.
 
J

Jim Langston

Gianni Mariani said:
Jim Langston wrote:
...

Using the methodology described here, you can extend the system without
needing to change the dispatch code. Centralized dispatchers (if/else or
switch) can become very large and confusing very quickly with a moderate
number of cases.

Dynamic dispatchers also conform to the "code is in one place" rule. It
means if you need a new case in your switch statement, you don't have to
change the code other than the one place where you define all the aspects
of that new case. I had worked on one system that when a new message
type was added, 11 different files needed to be "fixed". Needless to say
it was a very tedious and error prone thing to do. Using the dynamic
dispatch mechanism, you would only need to add a new file containing all
the mechanisms you need.

Think of systems like driver modules in a kernel or protocol handlers etc.

You can take this to extremes where the dynamic selection of the case is
actually a complex process and it allows you to eliminate considerable
chunks of if/else garbage. This I used in a system where the processing
was dependent on a large number of dynamic factors.

C++ also allows you to use the technique statically with templates using
partial specializations. i.e. you can change the behaviour of the code
simply by adding a specialization of a class. I've used that in a DXF
file reader which was one of those "worked first time" miracles. The nice
thing with the DXF file reader architecture, I don't even need to add new
code body, all I have to do is add a new specialized class. The code also
reflects closely to the documentation which means that it is easy to
verify that the code is doing what the documentation expects. The
corresponding if/else or switch code would have been a nightmare to
maintain and validate.

I whole heartedly agree that dispatch systems have their place. I have used
them before myself in fact. But I would by no means use one just to replace
a branch of if...else if with a dispatch system. It is, IMO, harder to
follow the flow of code using dispatch system than a switch statement or
if...else block, especially depending on the method the dispatch system
uses.
 
I

Ian Collins

Jim said:
I whole heartedly agree that dispatch systems have their place. I have used
them before myself in fact. But I would by no means use one just to replace
a branch of if...else if with a dispatch system. It is, IMO, harder to
follow the flow of code using dispatch system than a switch statement or
if...else block, especially depending on the method the dispatch system
uses.
With Dynamic dispatch you can add cases to an existing dispatcher at
both compile and run time without updating its code, something a switch
or if/else block can never do.

Software design isn't a one shoe fits all practice, use the most
appropriate solution for the problem at hand.
 
J

Jorgen Grahn

You forgot

- You loose the simple optimisation of a jump table with appropriate
case labels.

Yes, of course. But it was my impression that he was prepared to give
that up, mostly complaining about the ugliness and repetitiveness.

/Jorgen
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,197
Latest member
ScottChare

Latest Threads

Top