Mapping operations to codes

D

Default User

I'm not sure how well that subject captured the topic. Anyway, this is a
branching from my previous topic "Fun with member-function pointers".

The general problem is that we have a number of "drivers" for instruments.
These are essentially an abstraction layer than translates control messages
from higher-level modules into something the particular hardware can
understand. The API for this is a system of operation codes and an
associated data word that has bits set or cleared for various options.

My current solution is to map the op codes to pointers to member functions
that handle the particular operation. There's also a requirement that each
driver be able to respond with a list of the supported op codes. The problem
is that it results in a lot of very similar code in each driver. I had
wanted to push down as much behavior as possible to the base class. My
initial thoughts (see other thread) look like they result in undefined
behavior, so no good.

Below is a small representation of the problem. It's not the real code
(which would be very large), so it's not necessary to suggest improvements
in layout or that sort of thing. Thoughts on attacking the general problem
would be welcome.


Brian


================= code =================

#include <iostream>
#include <map>
#define CALL_HANDLER(object,ptrToMember)((object).*(ptrToMember))

class tbase
{
public:

protected:
tbase()
{
}
virtual ~tbase()
{
}
};

class test : public tbase
{
public:
typedef int (test::*HandlerPtr)(int);

test() // sets up the map of handlers to op codes
{
handlers[0] = &test::H0;
handlers[4] = &test::H4;
}
virtual ~tbase()
{
}
virtual int H0(int data)
{
std::cout << "test::H0: " << data << "\n";
return data;
}
int H4(int data)
{
std::cout << "test::H4: " << data << "\n";
return data;
}
void write(int data, int code) // finds the handler and passes data
{
std::map<int, HandlerPtr>::iterator it;
it = handlers.find(code);
if (it != handlers.end())
{
// Found, call the function pointer
CALL_HANDLER(*this, it->second)(data);
}
else // unhandled code
{
// error handling
}
}
void list() // just prints supported op codes
{
std::map<int, HandlerPtr>::iterator it = handlers.begin();

std::cout << "Supported Op codes:\n";
while (it != handlers.end())
{
std::cout << it->first << "\n";
it++;
}
}
private:
std::map<int, HandlerPtr> handlers;
};

int main()
{
test t;
t.list();
t.write(123, 0);
t.write(456, 4);
return 0;
}
 
P

Paul

I'm not sure how well that subject captured the topic. Anyway, this is a
branching from my previous topic "Fun with member-function pointers".

The general problem is that we have a number of  "drivers" for instruments.
These are essentially an abstraction layer than translates control messages
from higher-level modules into something the particular hardware can
understand. The API for this is a system of operation codes and an
associated data word that has bits set or cleared for various options.

My current solution is to map the op codes to pointers to member functions
that handle the particular operation. There's also a requirement that each
driver be able to respond with a list of the supported op codes. The problem
is that it results in a lot of very similar code in each driver. I had
wanted to push down as much behavior as possible to the base class. My
initial thoughts (see other thread) look like they result in undefined
behavior, so no good.

Below is a small representation of the problem. It's not the real code
(which would be very large), so it's not necessary to suggest improvements
in layout or that sort of thing. Thoughts on attacking the general problem
would be welcome.

Brian

================= code =================

#include <iostream>
#include <map>
#define CALL_HANDLER(object,ptrToMember)((object).*(ptrToMember))

class tbase
{
public:

protected:
   tbase()
   {
   }
   virtual ~tbase()
   {
   }

};

class test : public tbase
{
public:
   typedef int (test::*HandlerPtr)(int);

   test() // sets up the map of handlers to op codes
   {
      handlers[0] = &test::H0;
      handlers[4] = &test::H4;
   }
   virtual ~tbase()
   {
   }
   virtual int H0(int data)
   {
      std::cout << "test::H0: " << data << "\n";
      return data;
   }
Why is the above virtual? just a mistake?
   int H4(int data)
   {
      std::cout << "test::H4: " << data << "\n";
      return data;
   }
   void write(int data, int code) // finds the handler and passes data
   {
      std::map<int, HandlerPtr>::iterator it;
      it = handlers.find(code);
It maybe worth while implementing this as a switch case block and do
away with the map altogether, you'd only need one function pointer.

switch(code){
case(123)
fnctptr = &test::H0;
break;
case(456)
fnctptr = &test::H4;
break;
default //catch errors;
}

this->*fnctptr(data);

It may look like more code, if there are alot of "op codes", but the
compiler may optimise this to be faster than a map search.
Are the "op codes" a pre defined set of integers?
If so what range etc?

      if (it != handlers.end())
      {
         // Found, call the function pointer
         CALL_HANDLER(*this, it->second)(data);
I dunno why you have a macro in there.
      }
      else // unhandled code
      {
         // error handling
      }
   }
   void list() // just prints supported op codes
   {
      std::map<int, HandlerPtr>::iterator it = handlers.begin();

      std::cout << "Supported Op codes:\n";
      while (it != handlers.end())
      {
         std::cout << it->first << "\n";
         it++;
      }
I think the above could probably be more efficient but again need to
know the details of the "op codes" before thinking about it. For
example you could just store a string containing all the ops codes,
and print it out.
   }
private:
   std::map<int, HandlerPtr> handlers;

};

int main()
{
   test t;
   t.list();
   t.write(123, 0);
   t.write(456, 4);
   return 0;
You also want to think about copying and assignments?

If you have only one set of pre-defined "op-codes" you don't really
want to be copying that map across multiple objects.
I think alot of this depends on these "op codes", Are they a
predefined set?

HTH
 
V

Victor Bazarov

I'm not sure how well that subject captured the topic. Anyway, this is a
branching from my previous topic "Fun with member-function pointers".

The general problem is that we have a number of "drivers" for instruments.
These are essentially an abstraction layer than translates control messages
from higher-level modules into something the particular hardware can
understand. The API for this is a system of operation codes and an
associated data word that has bits set or cleared for various options.

My current solution is to map the op codes to pointers to member functions
that handle the particular operation. There's also a requirement that each
driver be able to respond with a list of the supported op codes. The problem
is that it results in a lot of very similar code in each driver. I had
wanted to push down as much behavior as possible to the base class. My
initial thoughts (see other thread) look like they result in undefined
behavior, so no good.

The maps don't seem to be needed per instance of class ('test') but
rather per class (i.e. they should probably be static). That's all I
have so far. I would probably try rewriting the code so that 'write'
and 'list' functions were static as well.

Currently there seems to be no need in using the 'this' to call the H0
or H4 members - you're not using any instance specific data here.
Hence, there is no need for H0 or H4 to be non-static either. Hence
your "pointer to member" statement is unsubstantiated. Each "method"
for dealing with "code" could just as well be static...

Please supply a more complete design that would prove the need to use
non-static driving methods.

It would be nice to see the need for more than one 'test'. Perhaps the
'main' should define two different "drivers" (or even two instances of
each of the two different "drivers") to demonstrate the need for keeping
the maps of members in the instance, and not per class. So far even if
you are trying to use non-static data in those H0 and H4 member
functions, the *map* still doesn't have to be per instance.
Below is a small representation of the problem. It's not the real code
(which would be very large), so it's not necessary to suggest improvements
in layout or that sort of thing. Thoughts on attacking the general problem
would be welcome.


Brian


================= code =================

#include<iostream>
#include<map>
#define CALL_HANDLER(object,ptrToMember)((object).*(ptrToMember))

class tbase
{
public:

protected:
tbase()
{
}
virtual ~tbase()
{
}
};

class test : public tbase
{
public:
typedef int (test::*HandlerPtr)(int);

test() // sets up the map of handlers to op codes
{
handlers[0] =&test::H0;
handlers[4] =&test::H4;
}
virtual ~tbase()
{
}
virtual int H0(int data)
{
std::cout<< "test::H0: "<< data<< "\n";
return data;
}
int H4(int data)
{
std::cout<< "test::H4: "<< data<< "\n";
return data;
}

Not sure how it plays into your goals, so is there a reason why one is
virtual and the other isn't?
void write(int data, int code) // finds the handler and passes data
{
std::map<int, HandlerPtr>::iterator it;
it = handlers.find(code);
if (it != handlers.end())
{
// Found, call the function pointer
CALL_HANDLER(*this, it->second)(data);
}
else // unhandled code
{
// error handling
}
}
void list() // just prints supported op codes
{
std::map<int, HandlerPtr>::iterator it = handlers.begin();

std::cout<< "Supported Op codes:\n";
while (it != handlers.end())
{
std::cout<< it->first<< "\n";
it++;
}
}
private:
std::map<int, HandlerPtr> handlers;
};

int main()
{
test t;
t.list();
t.write(123, 0);
t.write(456, 4);
return 0;
}

V
 
D

Default User

Victor Bazarov said:
The maps don't seem to be needed per instance of class ('test') but rather
per class (i.e. they should probably be static). That's all I have so
far. I would probably try rewriting the code so that 'write' and 'list'
functions were static as well.

I fear I haven't given enough information, and my example was too sketchy.

1. There will be a number of different drivers, one for each instrument, all
of with derive from the base class.
2. The driver objects will be contained by other, higher-level ones. These
higher level "device" modules are derived from a common base class
themselves. That base class has a pointer to the driver base class. The
derived class objects will populate that pointer with the particular driver
that they need.
3. The "device" objects will call the small set of public methods using
polymorphism. Some methods are in the base class, others specific to a
particular device. Mostly these route op codes and data to the driver, or
retrieve data pulled from the instrument.
4. The drivers will have class attributes that contain state information
about the particular instrument associated with the driver. The op code
handlers can't be static. Here my example really failed. Mentally add
several attributes, if you would.



Brian
 
V

Victor Bazarov

I fear I haven't given enough information, and my example was too sketchy.

1. There will be a number of different drivers, one for each instrument, all
of with derive from the base class.

That's your design. That's not the problem definition, though.
2. The driver objects will be contained by other, higher-level ones. These
higher level "device" modules are derived from a common base class
themselves.

OK. A device consists of (or contains) a collection of drivers. Let's
go with that.
That base class has a pointer to the driver base class.

A *single* pointer? How does that work with the collection (see above)?
The
derived class objects will populate that pointer with the particular driver
that they need.

A single pointer? Is that some kind of an "active" driver? From where
do those get populated? Is there a repository of driver objects?
3. The "device" objects will call the small set of public methods using
polymorphism.

Whose methods? Driver's?
Some methods are in the base class, others specific to a
particular device. Mostly these route op codes and data to the driver, or
retrieve data pulled from the instrument.

"The" instrument? The "active" one? There is only one instrument at a
time? Can there be the need to retrieve data from a driver/instrument
that is not active?
4. The drivers will have class attributes that contain state information
about the particular instrument associated with the driver. The op code
handlers can't be static. Here my example really failed. Mentally add
several attributes, if you would.

You're explaining your design instead of explaining the problem. Can
one device have drivers that serve the same operation? If not, then
each driver (op server or whatever you decide to call it) is unique at
the *device* level, and there seems to be no sense to map its operation
to its function at the level of the *driver*. Place a master map at the
device level.

V
 
D

Default User

Victor Bazarov said:
That's your design. That's not the problem definition, though.

Unfortunately, not being a start-from-scratch exercise, we have to go with
the design as it is so far. Again, my attempts at explanation have had
serious gaps, and I apologize for that.
You're explaining your design instead of explaining the problem. Can one
device have drivers that serve the same operation? If not, then each
driver (op server or whatever you decide to call it) is unique at the
*device* level, and there seems to be no sense to map its operation to its
function at the level of the *driver*. Place a master map at the device
level.

The central system will hit certain conditions that require service from an
instrument. At that time, there will be a device code that indicates which
device should handle the situation, an operation code that indicates what to
do, and a data word with specific information. None of that can be changed.
The design of most of this is well along and can't be changed. For good or
for ill.

At the driver level, I need to map operation codes to driver functions. The
original code had large case statements. I've been breaking that into
smaller "handlers" for the various op codes.



Brian
 
V

Victor Bazarov

[..]
At the driver level, I need to map operation codes to driver functions. The
original code had large case statements. I've been breaking that into
smaller "handlers" for the various op codes.

I still don't see the need to keep a map at the driver level and per
instance, sorry. It can only be necessary if two drivers of the *same*
type could report different pointers-to-member for the same op code, in
certain conditions (say, depending on their states). Perhaps it's just
me, and somebody will step up and offer a brilliant solution within your
limitations.

V
 
D

Default User

Victor Bazarov said:
[..]
At the driver level, I need to map operation codes to driver functions.
The
original code had large case statements. I've been breaking that into
smaller "handlers" for the various op codes.

I still don't see the need to keep a map at the driver level and per
instance, sorry. It can only be necessary if two drivers of the *same*
type could report different pointers-to-member for the same op code, in
certain conditions (say, depending on their states).

Where would you want to map them?


Brian
 
P

Paul

[..]
At the driver level, I need to map operation codes to driver functions..
The
original code had large case statements. I've been breaking that into
smaller "handlers" for the various op codes.
I still don't see the need to keep a map at the driver level and per
instance, sorry.  It can only be necessary if two drivers of the *same*
type could report different pointers-to-member for the same op code, in
certain conditions (say, depending on their states).
Even with these conditions its not necessary to store a map per
instance.
Where would you want to map them?
There are a few different options. :)
 

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

Staff online

Members online

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top