From: glenm@bnr.ca (Glen Martin)
Subject: Re: Discriminating Variant Records Without switch() ??
Reply-To: glenm@bnr.ca
Organization: Bell-Northern Research Ltd.
Date: Tue, 10 Nov 92 18:15:29 GMT

In article <BAB.92Nov10073324@se39.wg2.waii.com>, bab@se39.wg2.waii.com (Brian Button) writes:
...
|> As an example:
|> 
|> We're reading structures sent to us over a socket. Since these
|> structures are being sent and received on the same machine, we can
|> just memcpy the byte stream read from the socket into a big union, and
|> then switch based on the first field of the union to determine the type.
|> 
|> As a trivial example:
|> 
|> {
|> 	read( sd, buf, 16 );
|> 	memcpy(( char * )&union_of_structs, buf, 16 );
|> 	switch( union_of_structs.type )
|> 	{
|> 		case A:
|> 
|> 			.
|> 			.
|> 			.
|> 	}
|> }

Well, this is a bit of a can of worms.

It seems reasonable to use subclassing and virtual functions to
take the place of the switch. Sadly, that leaves us with the
problem of creating different classes depending on the data
read.

It would seem to me that a solution to this problem would be a
class dispatcher (or whatever you'd like to call it). Basically,
the central entity is responsible for creating objects based on
the data stream, having been given a list of tags and constructors
to use for this purpose.

Below is a skeleton of the idea. There are a lot of details I
haven't bothered with, but I hope this gives you the basic idea.
The benefit of an approach like this is that it avoids the case
statement, which is bad largely because of its hard-coded nature.
If I want to create a new class, all I need to do is inherit from
streamclass, implement the read and perform functions, and I'm
done.

The remaining hard-coded detail is the tags - somehow the two sides
of the pipe must agree on the tag values for each class. Maybe an
incrementing counter based on order of registration? Sounds icky.
Maybe the text of the class name? This area needs work still. It
would probably be best if the two sides could share the dispatch
and class code, as well as the static object declarations. Basically,
all of the following example except the main function. This would go
a long way toward assuring tag consistency.


class streamclass {
  public:
    virtual perform() = 0;
    virtual read() = 0;
};

class dispatch {
  public:
    register( tag, streamclass * );   // insert tag & pointer into table
    streamclass *read();
    dispatch() { num = 0; }
  private:
    int num;
    struct table_s {
      int tag;
      stramclass *staticObj;
    } table[MAXOBJS];
}

streamclass *dispatch::read() {
  struct table_s *tableEntry;
  streamclass *obj;

  // read a tag
  tag = ...

  // look up in table, find the entry
  tableEntry = ...

  obj = tableEntry->staticObj->read();
  return(obj);
}

class foo : public streamclass {
  public:
    foo() { register( <some unique tag value>, this ); }
    foo *read();             // build an object from the input stream
    void perform();
}

dispatch theDisplatcher;
foo      staticFoo;       // this is created just as something for
                          // theDispatcher to call a read function on.


void main()
{
  .
  .
  .
  streamclass *s;
  s = theDispatcher.read();
  s->perform();
  .
  .
  .
}

--------------------------------------------------------------------------------
Glen Martin              Bell-Northern Research          Ottawa, Ontario, Canada
Phone : (613) 763-7299   Fax: (613) 763-2626             Internet: glenm@bnr.ca
--------------------------------------------------------------------------------