How to avoid runtime switch/case code based on data type?

F

Fei Liu

Hi Group, I've got a problem I couldn't find a good solution. I am
working with scientific data files in netCDF format. One of the
properties of netCDF data is that the actual type of data is only known
at run time. Therefore a lot of template based trick isn't too useful.
Considering

datafile
float x(3) 3.5, 2.5, 8.9
double y(3) 2.7, -2.3, 1.2
int z(3) 5, 2, 3

Suppose in my code, I need to write a summation function and return the
result, I could do

template <class T>
T sum(T * data, size_t n);

This is all good and dandy, but in the calling subroutine I am stuck
because at compile I have no priori knowledge of actual data type
contained in the data file, thus I must rely on switch/case code based
on what may be contained in the data file during run time. Consider:

void sum_caller(){
mytype type = datafile.getline(i).type
switch(type){
case float_type:
float ret = sum(datafile.getline(i).data);
do_something(ret);
case double_type:
float ret = sum(datafile.getline(i).data);
...
....
}

I cannot declare 'template <class T> void sum_caller();' because only
within sum_caller, the type information is available. (you could argue
I can declare sum_caller template based, but that's not the point. At
some point, the type information becomes available and appropriate
action can be taken). Is there a design pattern or implementation
strategy that can help me with this dilemma and thus avoid switch/case
based code altogether? Thanks!
 
M

mlimber

Fei said:
Hi Group, I've got a problem I couldn't find a good solution. I am
working with scientific data files in netCDF format. One of the
properties of netCDF data is that the actual type of data is only known
at run time. Therefore a lot of template based trick isn't too useful.
Considering

datafile
float x(3) 3.5, 2.5, 8.9
double y(3) 2.7, -2.3, 1.2
int z(3) 5, 2, 3

Suppose in my code, I need to write a summation function and return the
result, I could do

template <class T>
T sum(T * data, size_t n);

This is all good and dandy, but in the calling subroutine I am stuck
because at compile I have no priori knowledge of actual data type
contained in the data file, thus I must rely on switch/case code based
on what may be contained in the data file during run time. Consider:

void sum_caller(){
mytype type = datafile.getline(i).type
switch(type){
case float_type:
float ret = sum(datafile.getline(i).data);
do_something(ret);
case double_type:
float ret = sum(datafile.getline(i).data);
...
...
}

I cannot declare 'template <class T> void sum_caller();' because only
within sum_caller, the type information is available. (you could argue
I can declare sum_caller template based, but that's not the point. At
some point, the type information becomes available and appropriate
action can be taken). Is there a design pattern or implementation
strategy that can help me with this dilemma and thus avoid switch/case
based code altogether? Thanks!

Check out chapter 8 of _Modern C++ Design_ by Andrei Alexandrescu. A
Loki factory is intended to fill just such a gap and will likely do
what you need (well, with a little effort on your part). You can get
the latest source code for it from

http://sourceforge.net/projects/loki-lib/

Cheers! --M
 
N

Neil Cerutti

Hi Group, I've got a problem I couldn't find a good solution. I am
working with scientific data files in netCDF format. One of the
properties of netCDF data is that the actual type of data is only
known at run time. Therefore a lot of template based trick isn't too
useful. Considering

datafile
float x(3) 3.5, 2.5, 8.9
double y(3) 2.7, -2.3, 1.2
int z(3) 5, 2, 3

It looks like you need a parser, rather, and it's already been
written. A visit to the CVS repository for NCO seems like the best
place to start.
 
F

Fei Liu

Neil said:
It looks like you need a parser, rather, and it's already been
written. A visit to the CVS repository for NCO seems like the best
place to start.

--
Neil Cerutti
I pulled into a lay-by with smoke coming from under the bonnet.
I realized the car was on fire so took my dog and smothered it
with a blanket. --Insurance Claim Blooper

How does a parser solve my problem?
 
P

Phlip

Fei said:
Hi Group, I've got a problem I couldn't find a good solution. I am
working with scientific data files in netCDF format. One of the
properties of netCDF data is that the actual type of data is only
known at run time. Therefore a lot of template based trick isn't too
useful. Considering

datafile
float x(3) 3.5, 2.5, 8.9
double y(3) 2.7, -2.3, 1.2
int z(3) 5, 2, 3

mlimber's loki suggestion might work.

In general, faced with A> a boundary between the outside world and a
program, and B> a switch based on type codes, you need polymorphism. Create
a Class Factory that converts each typed object into a wrapper object with
virtual methods. Return a reference to an abstract base class declaring
those methods, and call them. Each one will convert the data in its own
special way, and the calling code can be very clean.
 
M

ma740988

In general, faced with A> a boundary between the outside world and a
program, and B> a switch based on type codes, you need polymorphism. Create
a Class Factory that converts each typed object into a wrapper object with
virtual methods. Return a reference to an abstract base class declaring
those methods, and call them. Each one will convert the data in its own
special way, and the calling code can be very clean.

What would be the right 'search term' - if you will for this. I'd like
to look this up on the web (find an example or two), since I tend to go
the switch route on a continuum. In my heart of hearts I know there's
a polymorphic solution.
 
M

ma740988

In general, faced with A> a boundary between the outside world and a
program, and B> a switch based on type codes, you need polymorphism. Create
a Class Factory that converts each typed object into a wrapper object with
virtual methods. Return a reference to an abstract base class declaring
those methods, and call them. Each one will convert the data in its own
special way, and the calling code can be very clean.

What would be the right 'search term' - if you will for this? I'd like
to look this up on the web (find an example or two), since I tend to go
the switch route on a continuum. In my heart of hearts I know there's
a polymorphic solution.
 
D

Daniel T.

"Fei Liu said:
Hi Group, I've got a problem I couldn't find a good solution. I am
working with scientific data files in netCDF format. One of the
properties of netCDF data is that the actual type of data is only known
at run time. Therefore a lot of template based trick isn't too useful.
Considering

datafile
float x(3) 3.5, 2.5, 8.9
double y(3) 2.7, -2.3, 1.2
int z(3) 5, 2, 3

Suppose in my code, I need to write a summation function and return the
result, I could do

template <class T>
T sum(T * data, size_t n);

This is all good and dandy, but in the calling subroutine I am stuck
because at compile I have no priori knowledge of actual data type
contained in the data file, thus I must rely on switch/case code based
on what may be contained in the data file during run time. Consider:

void sum_caller(){
mytype type = datafile.getline(i).type
switch(type){
case float_type:
float ret = sum(datafile.getline(i).data);
do_something(ret);
case double_type:
float ret = sum(datafile.getline(i).data);
...
...
}

I cannot declare 'template <class T> void sum_caller();' because only
within sum_caller, the type information is available. (you could argue
I can declare sum_caller template based, but that's not the point. At
some point, the type information becomes available and appropriate
action can be taken). Is there a design pattern or implementation
strategy that can help me with this dilemma and thus avoid switch/case
based code altogether? Thanks!

Switch statements are not evil, what is evil is having multiple switch
statements in your code, all switching based on the same type
information. If you have that, then create an ABC, use one switch to
create the derived object and don't worry about it. KISS
 
M

ma740988

Howdy Daniel,
Switch statements are not evil, what is evil is having multiple switch
statements in your code, all switching based on the same type
information. If you have that, then create an ABC, use one switch to
create the derived object and don't worry about it. KISS
"KISS". Indeed. I think though I mirror the multiple switch statement
based on same type information you alluded to. So now consider an
enumerated type with a list of algorithms'.

enum algorithm_type { BEM, IFFT, FFT, INVALID = -1 };

This type forms is part of a composite type such that:
struct algorithm_details {
// stuff
// later
algorithm_type pcp_type;
};

Upon receipt of the header (+ data) from an external source, I do:
switch ( algorithm_type )
{
case BEM : // perform field compuations on the data using
Maxwells equations.
break;
case IFFT: // do an inverse FFT
break;
case FFT: // do an FFT
};
etc.

Something tells me that a more viable solution would be as follows:
class algorithm {
public: algorithm() {}
virtual void do_bem( /*whatever */ ) = 0;
// and so on.
};

// later
class foo : public algorithm {
public:
foo () : algorithm() {}
void do_bem( /* whatever */ ) {}
~virtual foo { void cleanup(); }
void cleanup () { /* what goes up must come down - tear down */ }
};

Ultimately what it amounts to is the mathematics ( matrix mess ) is so
involved that your 'case statements ' becomes large. Of ocurse the
alternative is to call member functions based on the type. In any
event, I think you see where I'm going .. Your thoughts.
 
D

Daniel T.

"ma740988 said:
Howdy Daniel,

"KISS". Indeed. I think though I mirror the multiple switch statement
based on same type information you alluded to. So now consider an
enumerated type with a list of algorithms'.

enum algorithm_type { BEM, IFFT, FFT, INVALID = -1 };

This type forms is part of a composite type such that:
struct algorithm_details {
// stuff
// later
algorithm_type pcp_type;
};

Upon receipt of the header (+ data) from an external source, I do:
switch ( algorithm_type )
{
case BEM : // perform field compuations on the data using
Maxwells equations.
break;
case IFFT: // do an inverse FFT
break;
case FFT: // do an FFT
};
etc.

Something tells me that a more viable solution would be as follows:
class algorithm {
public: algorithm() {}
virtual void do_bem( /*whatever */ ) = 0;
// and so on.
};

// later
class foo : public algorithm {
public:
foo () : algorithm() {}
void do_bem( /* whatever */ ) {}
~virtual foo { void cleanup(); }
void cleanup () { /* what goes up must come down - tear down */ }
};

Ultimately what it amounts to is the mathematics ( matrix mess ) is so
involved that your 'case statements ' becomes large. Of ocurse the
alternative is to call member functions based on the type. In any
event, I think you see where I'm going .. Your thoughts.

The problem is, you have the header (+data) from an external source. In
order to use a virtual method on a base class you first need to make an
object of some derived class. The type of object you need to make is
based on the very type your doing the switch on anyway.

In other words, you must do at least one switch on the type (either to
do the calculation or create the object.) It's having more than one
switch on type that could be a problem.
 
F

Fei Liu

Daniel said:
The problem is, you have the header (+data) from an external source. In
order to use a virtual method on a base class you first need to make an
object of some derived class. The type of object you need to make is
based on the very type your doing the switch on anyway.

This is the gist of the problem. Things are not known at compile time
and variables of proper type can only be declared at run time based on
external information...
 
M

ma740988

This is the gist of the problem. Things are not known at compile time
and variables of proper type can only be declared at run time based on
external information...
Thanks. By the way Fei, I might have overstepped my bounds there. In
that Daniel wasn't responding to me but for some reason I didn't
realize that until after it's too late.

My apologies.
 

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,995
Messages
2,570,228
Members
46,817
Latest member
AdalbertoT

Latest Threads

Top