Re: implementation details

From: MPowell (mpowell_jr_at_hotmail.com)
Date: 10/03/03


Date: 3 Oct 2003 13:35:31 -0700

tom_usenet <tom_usenet@hotmail.com> wrote in message >

> Following is some complete compileable code that might help. It
> basically shows how to enqueue all messages onto a queue, and how you
> might go about processing those messages. If you might need different
> processors for the messages in different circumstances, then the
> visitor pattern might be appropriate. This is rather a lot to take in
> if you are quite new to C++, but I hope it helps in any case.

Truly appreaciate the assistance. I'm starting to get a feel for what
a poster REALLY meant when he highlighted that C is not C++.
Theoretically I have to change my 'C' style design approach. This was
truly a lesson in implementation.

I was reading a post on recommended texts from a Mike Whaler and I've
since placed some orders.

Really despise having to continue/drag this post out, nonetheless I've
got a few more questions on this.

class CMDProcessor
{
public:
    void process(MSG2_STATUS& msg)
    {
        std::cout << "MSG2_STATUS processed\n";
    }
    :
    :
    :
};

I assume these process functions is where I need to do my 'double
buffering'?
The system like any another is riddled with asynchrous processing. In
other words assume I had this.
MSG2_STATUS msg2_stat [ 2 ];
Now fillQueue() and processQueue(); fills and processes a
"MSG2_STATUS'. I'll copy the data to msg2_stat [ 0 ]. Lets suppose
now that we're filling (fillQueue()) and processing (processQueue())
another MSG2_STATUS BUT while filling or processing a separate 60
Hertz task (which I have) is getting the most current MSG2_STATUS.
The 60 Hertz task will grab/read the data in msg2_status[ 0 ] while
fill (fillQueue()) and process (processQueue()) writes to msg2_status
[ 1 ].

Yes, there's the 'unlikely' event where I'll have a race condition
where they're both trying to read (the 60 Hertz) and write
(processQueue) to the same location but i've got a handle on that via
the mechanisms of the RTOS.

>
> template <class CMD>
> class CMDHolder: public CMDBase
> {
> public:
> CMDHolder(unsigned char const* buffer)
> {
> memcpy(&m_CMDStruct, buffer, sizeof m_CMDStruct);
> }
>
> virtual void process(CMDProcessor* processor)
> {
> processor->process(m_CMDStruct);
> }
>
> private:
> CMD m_CMDStruct;
> };
>
> class CMDFactory
> {
> public:
> static CMDFactory& instance()
> {
> static CMDFactory fact;
> return fact;
> }
>
> // Register the creator
> template <class T> void Register(int Id)
> {
> m_creators[Id] = CMDCreator<CMDHolder<T> >;
> }
> // unregister creator
> bool Unregister( const int& Id )
> {
> return m_creators.erase( Id ) == 1;
> }
>
> CMDBase* Create(unsigned char const* buffer)
> {
> MSG_HEADER const* msg_id = reinterpret_cast<MSG_HEADER
> const*>(buffer);
> map_t::const_iterator i = m_creators.find(msg_id->source);
>
> if (i == m_creators.end())
> {
> throw runtime_error("Invalid source");
> }
> else
> {
> //call create function through pointer.
> return (i->second)(buffer);

Get an exception here in Visual C.NET. Not sure if a C style try
catch handler would be ideal

> }
> }
> private:
> typedef CMDBase* (*CMDCreator_t)(unsigned char const*);
>
> template <class T>
> static CMDBase* CMDCreator(unsigned char const* buffer)
> {
> return new T(buffer);
> }
>
> typedef std::map<int, CMDCreator_t> map_t;
> map_t m_creators;
> };
>
> void register_types()
> {
> CMDFactory::instance().Register<MSG1_CMD>(0);
> CMDFactory::instance().Register<MSG1_STATUS>(1);
> CMDFactory::instance().Register<MSG2_STATUS>(2);
> }
>
> size_t ReadData(unsigned char* buffer, size_t size)
> {
> //generate sample data
> static bool doMsg1 = false;
> void* data;
> size_t actualSize;
> MSG1_STATUS msg1 = {};
> MSG2_STATUS msg2 = {};

Interesting I had to change above to.
MSG1_STATUS msg1;
MSG2_STATUS msg2;

VC.NET complier complained that 'local function definitions are
illegal

> if (doMsg1)
> {
> msg1.header.source = msg1_status;
> data = &msg1;
> actualSize = sizeof msg1;
> }
> else
> {
> msg2.header.source = msg2_status;
> data = &msg2;
> actualSize = sizeof msg2;
> }
> if (size < actualSize)
> throw runtime_error("buffer too short");
> memcpy(buffer, data, actualSize);
> doMsg1 = !doMsg1;
> return actualSize;
> }
>
> CMDProcessor processor;
>
> void processQueue()
> {
> while (!CMDQueue.empty())
> {
> CMDBase* cmd = CMDQueue.front();
> CMDQueue.pop();
> cmd->process(&processor);
> delete cmd;
> }
> }
I suspect I understand the simplistic version of queues. The
cmd->process and delete cmd should be placed outside the while loop??
Each element that gets 'popped' off the queue results in a call to
cmd-process/delete cmd. so now assume two bytes.

BYTE1. while !empty 1 results in : <- pop .. cmd->process(byte1)
BYTE2. while !empty 2 results in : <- pop .. cmd->process(byte2)
&process is in effect a pass through to
processor->process(m_CMDStruct);?
I missed how the CMD type was passed in?

Thanks again.



Relevant Pages