Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Between static & dynamic polymorphism...

Reply
Thread Tools

Between static & dynamic polymorphism...

 
 
rwf_20
Guest
Posts: n/a
 
      11-16-2005
I just wanted to throw this up here in case anyone smarter than me has
a suggestion/workaround:

Problem:

I have a classic producer/consumer system which accepts 'commands' from
a socket and 'executes' them. Obviously, each different command (there
are ~20 currently) has its own needed functionality. The dream goal
here would be to remove all knowledge of the nature of the command at
runtime. That is, I don't want ANY switch/cases or if/elses to
determine what command has been received, etc:

1. Accept command from socket.
2. Create generic 'command' object with command data.
3. Call 'execute' on command object.
4. Command-specific code runs.

So, there's two obvious ways that are _close_:

Using static polylmorphism:

template <unsigned long commandCode>
class base_command {
void execute();
.....
};

// command 1
void base_command<1>::execute() {
....command 1 specific code...
}
void base_command<2>::execute() {
....command 2 specific code...
}

int main() {
unsigned long commandCode = //read command in somehow
base_command<commandCode>().execute(); //obviously doesn't work
because the compiler doesn't know 'commandCode' at compile-time
}


Using dynamic polymorphism:

class base_command {
virtual void execute() = 0;
};

class command_1 : public base_command {
virtual void execute() {
...command 1 specific code...
}
};

class command_2 : public base_command {
.... etc...
};

int main() {
unsigned long commandCode = // read command in somehow
base_command* c = // some logic to determine subclass from (huge
switch/case)
}


So, I'd love to use static polymorphism, but I'm fairly certain this
can't and never will happen, due to no compile-time knowledge of the
command code. I'm currently implementing the dynamic method, but I
HATE the huge switch/case needed to determine command type. The
executable size should be the same in all cases, but what I'm really
looking for is a) cleanliness and b) efficiency. I'm on an embedded
system, so using polymorphism makes me sick to my stomach. But, I
guess it's either that or one class with an enormous if/else.

I've got some other crazy half-ideas like a map of constructor function
objects indexed by command code (boost probably has something nutty
that might half-support this). But for the most part I think I'm
stuck.

Unless anyone has any thoughts...


Thanks,
Ryan

 
Reply With Quote
 
 
 
 
Jonathan Mcdougall
Guest
Posts: n/a
 
      11-16-2005
rwf_20 wrote:
> I just wanted to throw this up here in case anyone smarter than me has
> a suggestion/workaround:
>
> Problem:
>
> I have a classic producer/consumer system which accepts 'commands' from
> a socket and 'executes' them. Obviously, each different command (there
> are ~20 currently) has its own needed functionality. The dream goal
> here would be to remove all knowledge of the nature of the command at
> runtime. That is, I don't want ANY switch/cases or if/elses to
> determine what command has been received, etc:


Look up "object factory" on the web and get Modern C++ Design by
Alexandrescu. Or simply define a std::map<id, function_pointer>.


Jonathan

 
Reply With Quote
 
 
 
 
Jeremy Jurksztowicz
Guest
Posts: n/a
 
      11-16-2005
Alternatively use boost::function<void (param_t)>.

Example:

typedef boost::function<void (void)> ConsumerCommand;
ConsumerCommand cmd;
bool gotIt = consumerQueue.getNext(cmd);
if(gotIt) cmd();

 
Reply With Quote
 
werasm
Guest
Posts: n/a
 
      11-16-2005

rwf_20 wrote:
> I just wanted to throw this up here in case anyone smarter than me has
> a suggestion/workaround:
>



I don't know whether I'm smarter, thats (smarter in terms of what)...
but

You need to abstract dynamic and static polymorphism from each other.
You want to be able to use dynamic polymorphism to execute via one
interface, but static polymorphism to call something specific (I don't
know whether this makes sense).

You can never bind the sender of your command to a receiver type, as it
may want to send msgs to arbitrary receivers. For this reason, your
base should use dynamic polymorphism (I'll be brief).

struct BaseCmd
{
void execute() = 0;
BaseCmd* clone() const = 0; //I know of better clone implementations

};

class Client //Going to call Cmd.execute
{
void associate( const BaseCmd& cmd ){ cmd_ = cmd.clone(); }
//...
BaseCmd* cmd_;
};

template <class T>
class MyCmd : public BaseCmd
{
//Implements execute.
};

Now we win by creating 1 command that represents all type T's, but
Client is oblivious as the BaseCmd is not type dependent. Winning both
ways by abstracting dynamic and static parts.

For more information, you can also refer to Herb Sutters article
"Elegant function call wrappers".

Kind regards,

Werner

 
Reply With Quote
 
rwf_20
Guest
Posts: n/a
 
      11-16-2005
Thanks for the suggestions, all.

I've got something along these lines now. While not a complete
solution, I do like the top-level syntax, which is the general goal.

// base_command.h
#include <boost/function.hpp>

typedef boost::function<void (void)> exeFunction;

std::map<unsigned long, exeFunction> constructorMapG;

#define CMDCODE_ACQUIRE 0x1
#define CMDCODE_ABORT 0x3
#define CMDCODE_LASERTEST 0x18
#define CMDCODE_FIRELASER 0x19

template <unsigned long commandCode>
class base_command {
public:
static void execute() { printf("in base_execute()\n"); }
};

void base_command<CMDCODE_FIRELASER>::execute() {
printf("in fire_laser execute()\n");
}
void base_command<CMDCODE_ACQUIRE>::execute() {
printf("in acquire() execute()\n");
}
void base_command<CMDCODE_ABORT>::execute() {
printf("in abort() execute()\n");
}
void base_command<CMDCODE_LASERTEST>::execute() {
printf("in laser_test() execute()\n");
}

void registerConstructors() {
constructorMapG[CMDCODE_ABORT] = base_command<CMDCODE_ABORT>::execute;
constructorMapG[CMDCODE_ACQUIRE] =
base_command<CMDCODE_ACQUIRE>::execute;
constructorMapG[CMDCODE_LASERTEST] =
base_command<CMDCODE_LASERTEST>::execute;
constructorMapG[CMDCODE_FIRELASER] =
base_command<CMDCODE_FIRELASER>::execute;
}

typedef std::map<unsigned long, exeFunction>::const_iterator mapItr;

exeFunction getMeAnExecute(const unsigned long code) {
mapItr m = constructorMapG.find(code);
if (m != constructorMapG.end()) return (*m).second;
else // throw exception denoting invalid command
}


int main() {
unsigned long cmdCode = // get command from socket
getMeAnExecute(cmdCode)();
}

Any more thoughts on this or another solution are welcome.

Thanks,
Ryan

 
Reply With Quote
 
Puppet_Sock
Guest
Posts: n/a
 
      11-16-2005
rwf_20 wrote:
> I have a classic producer/consumer system which accepts 'commands' from
> a socket and 'executes' them. Obviously, each different command (there
> are ~20 currently) has its own needed functionality. The dream goal
> here would be to remove all knowledge of the nature of the command at
> runtime. That is, I don't want ANY switch/cases or if/elses to
> determine what command has been received, etc:

[snip]

Ok, the discussion that followed this was great, and I will need to
be learning from that.

I'm just wondering, is 20 entries in a switch/case really that
horrible?
For that matter, would 100 entries be really that horrible? The time
spent by your code looking up the correct entry in a switch/case is
not going to be a large fraction of total run time. The cases can
easily
be made quite brief by making them function calls or some such.
Switch/case methods are easy to design, easy to document, easy to
debug, easy to maintain.

Especially if you use some variation of the standard map so that
you don't need to worry about converting the incoming msgs to
integers.

So, I'm wondering what it is about switch/case that bothers you so
much.
It makes me think I must be missing something important.
Socks

 
Reply With Quote
 
rwf_20
Guest
Posts: n/a
 
      11-16-2005
> So, I'm wondering what it is about switch/case that bothers you so much.
> It makes me think I must be missing something important.


Not really, no. My problem boils down to preference, really. What you
say is true; a 100 switch/case block is not a big deal at all. In
fact, this is what I've had previously in my scenario.

What motivated my post is that, for N commands, the switch case is at
least N extra lines of specialized code just to determine which one of
N specialized functions to call! Sure, you can wrap it up cleanly,
etc...but I've already written T::execute() N times -- I felt I
deserved some way to avoid creating T in N different ways . What
sucks is that there is _almost_ a way, if you could only instantiate a
template with a variable (I know, I know, this is impossible(?)).

Ryan

 
Reply With Quote
 
Jonathan Mcdougall
Guest
Posts: n/a
 
      11-16-2005

rwf_20 wrote:
> > So, I'm wondering what it is about switch/case that bothers you so much.
> > It makes me think I must be missing something important.

>
> Not really, no. My problem boils down to preference, really. What you
> say is true; a 100 switch/case block is not a big deal at all. In
> fact, this is what I've had previously in my scenario.


IMO, event a single test on an object identity is a big deal. It should
be avoided when possible.

> What motivated my post is that, for N commands, the switch case is at
> least N extra lines of specialized code just to determine which one of
> N specialized functions to call! Sure, you can wrap it up cleanly,
> etc...but I've already written T::execute() N times -- I felt I
> deserved some way to avoid creating T in N different ways .


The problem here, I guess, is not the hierarchy, but the object
creation. You get an ID at runtime and need to create the appropriate
object. One you get the object, it is only a matter of calling a
virtual function.

Creating the object is usually achieved using a map of id=>creator. The
creator can be a function, another clonable object or whatever. Then,
you must make sure every creator has a unique id and finally register
them all in the map.

> What
> sucks is that there is _almost_ a way, if you could only instantiate a
> template with a variable (I know, I know, this is impossible(?)).


That does not make sense. It's like trying to have the address of a
variable as a compile-time constant. A variable is a run-time entity. A
template is a compile-time entity. At run-time, no "template" exist,
only concrete functions. At compile-time, no "variable" exist, only
names. Templates are a way in C++ to "automatically" create a family of
functions at compile-time. It's syntactic sugar.


Jonathan

 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      11-16-2005
rwf_20 wrote:

> I just wanted to throw this up here in case anyone smarter than me has
> a suggestion/workaround:
>
> Problem:
>
> I have a classic producer/consumer system which accepts 'commands' from
> a socket and 'executes' them. Obviously, each different command (there
> are ~20 currently) has its own needed functionality. The dream goal
> here would be to remove all knowledge of the nature of the command at
> runtime. That is, I don't want ANY switch/cases or if/elses to
> determine what command has been received, etc:
>
> 1. Accept command from socket.
> 2. Create generic 'command' object with command data.
> 3. Call 'execute' on command object.
> 4. Command-specific code runs.
>
> So, there's two obvious ways that are _close_:
>
> Using static polylmorphism:
>
> template <unsigned long commandCode>
> class base_command {
> void execute();
> ....
> };
>
> // command 1
> void base_command<1>::execute() {
> ...command 1 specific code...
> }
> void base_command<2>::execute() {
> ...command 2 specific code...
> }
>
> int main() {
> unsigned long commandCode = //read command in somehow
> base_command<commandCode>().execute(); //obviously doesn't work
> because the compiler doesn't know 'commandCode' at compile-time
> }

[snip]
>
> So, I'd love to use static polymorphism, but I'm fairly certain this
> can't and never will happen, due to no compile-time knowledge of the
> command code. I'm currently implementing the dynamic method, but I
> HATE the huge switch/case needed to determine command type. The
> executable size should be the same in all cases, but what I'm really
> looking for is a) cleanliness and b) efficiency. I'm on an embedded
> system, so using polymorphism makes me sick to my stomach. But, I
> guess it's either that or one class with an enormous if/else.


You can trick a template into generating the code for the if/else:

#include <iostream>


template <unsigned long N>
void print ( void ) { std::cout << N << '\n'; }

struct eval {

typedef unsigned long enum_type;
static enum_type const first = 0;
static enum_type const last = 50;

typedef int value_type;

template < unsigned long N >
static
int function ( void ) {
print<N>();
return 0;
}

};

template < typename eval >
struct tpl_bin_search {

typedef typename eval::enum_type Enum;

template < Enum first, Enum last >
static
typename eval::value_type alg ( Enum val ) {
if ( first == last ) {
return( eval::template function<first>() );
}
if ( (Enum)( first + ( last-first )/2 ) < val ) {
return( tpl_bin_search< eval >::template
alg< (Enum)( last - ( last-first )/2), last >( val ) );
} else {
return( tpl_bin_search< eval >::template
alg< first, (Enum)( first + ( last-first )/2) >( val ) );
}
}

};

template < typename eval >
typename eval::value_type bin_search ( typename eval::enum_type val ) {
return( tpl_bin_search< eval >::template
alg< eval::first, eval::last >( val ) );
}

int main ( void ) {
bin_search<eval>( 4 );
}


Best

Kai-Uwe Bux
 
Reply With Quote
 
rwf_20
Guest
Posts: n/a
 
      11-16-2005
> Creating the object is usually achieved using a map of id=>creator. The
> creator can be a function, another clonable object or whatever. Then,
> you must make sure every creator has a unique id and finally register
> them all in the map.


Yes, this is what I have.

> That does not make sense. It's like trying to have the address of a
> variable as a compile-time constant. A variable is a run-time entity. A
> template is a compile-time entity. At run-time, no "template" exist,
> only concrete functions. At compile-time, no "variable" exist, only
> names. Templates are a way in C++ to "automatically" create a family of
> functions at compile-time. It's syntactic sugar.


Yes, I understand why it can't work. As the subject implies, a
completely clean solution would lie somewhere in between static &
dynamic. I'm sure this could be achieved with a variety of compiler
hacks, but I'm also sure the results would blur the (measurable)
benefits of each pure method.

Ryan

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
FAQ 7.17 What's the difference between dynamic and lexical (static) scoping? Between local() and my()? PerlFAQ Server Perl Misc 0 04-15-2011 04:00 AM
FAQ 7.17 What's the difference between dynamic and lexical (static) scoping? Between local() and my()? PerlFAQ Server Perl Misc 0 01-06-2011 05:00 PM
Difference between static final members and final static members(if any)? JFCM Java 4 02-07-2006 11:32 AM
lvalue difference between static and non static member functions tkrogc@gmail.com C++ 9 02-06-2006 02:06 PM
VPN between 2 Cisco routers (1 static, 1 dynamic) with access from stat --> dynamic over ISDN Hans-Peter Walter Cisco 3 01-21-2004 02:12 PM



Advertisments