polymorphic base class pointers and template classes

A

awm129

I have a need to wrap several different (related) legacy structures in
thier own classes. This seems to scream template classes with the
structure type as a template parameter for the creation of a private
class member of the templated type.

Now, I would also like to use a polymorphic base class pointer with
this family of classes (for use with a factory) as well as include
some common functionallity in a base class. I would also like to
force the inclusion of this templated private member for each derived
class. This seems increadibly simple but I can't seem to quite wrap
my head around how this would work. I want to do something simlar to
this:

//
// base class to implement common functionallity
//
template<class T>
class Base
{
virtual T wrappedStruct = 0;
void foo();
}

//
// derived classes
//
template<class T>
class D1 : Base<T>
{
T wrappedStruct;
}

int main()
{
// assume Bar is a structure type
Bar s;

// now I want to use a polymorphic pointer to manage these things
Base* ptr = DFactory( s );
}


I dont think I can create an "untyped" (eg. without the <Type>) Base
pointer, but then how do I get my polymorphism to work? Does any of
this make sense? Any ideas? Thanks!
 
A

Alf P. Steinbach

* awm129:
I have a need to wrap several different (related) legacy structures in
thier own classes. This seems to scream template classes with the
structure type as a template parameter for the creation of a private
class member of the templated type.

Now, I would also like to use a polymorphic base class pointer with
this family of classes (for use with a factory) as well as include
some common functionallity in a base class. I would also like to
force the inclusion of this templated private member for each derived
class. This seems increadibly simple but I can't seem to quite wrap
my head around how this would work. I want to do something simlar to
this:

//
// base class to implement common functionallity
//
template<class T>
class Base

This would create a family of distinct base classes.

{
virtual T wrappedStruct = 0;

C++ does not support virtual data.

void foo();

What's this for?


Missing semicolon.

Did you intend for Base to have only private (inaccessible) members?


//
// derived classes
//
template<class T>
class D1 : Base<T>
{
T wrappedStruct;
}


int main()
{
// assume Bar is a structure type
Bar s;

// now I want to use a polymorphic pointer to manage these things
Base* ptr = DFactory( s );

Why do you want that?

What is the common functionality for the classes?

}


I dont think I can create an "untyped" (eg. without the <Type>) Base
pointer, but then how do I get my polymorphism to work? Does any of
this make sense? Any ideas? Thanks!

You could always use multiple inheritance, like

struct Base
{
virtual ~Base() {}
// whatever
};

struct MyLegacyClass: Base, LegacyClass
{
// Whatever
};

However, it may be a disservice to you to suggest anything at all, because while
you've described roughly a kind of technical solution, you haven't really
described the problem that this solution was meant to solve, in particular what
the object factory is meant to solve (and the object factory seems to the reason
for the wrapping, so if there's a better solution than the factory, which there
might well be, depending on what the problem is, then ignore the above).


Cheers & hth.,

- Alf
 
A

awm129

* awm129:








This would create a family of distinct base classes.


C++ does not support virtual data.

Yep, this was more to emphasize my desire to force derived classes to
have a member of the template type, I realize its not legal C++
What's this for?

so, rename foo() (and taht should be public: void foo();) to public:
void writeMessageToDatabase()
Missing semicolon.

Did you intend for Base to have only private (inaccessible) members?

no they will be at least protected, I seem to have omitted all
accessibility modifiers
Why do you want that?

See below
What is the common functionality for the classes?

The common functionality would be logging, printing, writting to a
database, etc
You could always use multiple inheritance, like

    struct Base
    {
        virtual ~Base() {}
        // whatever
    };

    struct MyLegacyClass: Base, LegacyClass
    {
        // Whatever
    };

Multiple inheritence might be what I need
However, it may be a disservice to you to suggest anything at all, because while
you've described roughly a kind of technical solution, you haven't really
described the problem that this solution was meant to solve, in particular what
the object factory is meant to solve (and the object factory seems to the reason
for the wrapping, so if there's a better solution than the factory, which there
might well be, depending on what the problem is, then ignore the above).

Cheers & hth.,

- Alf

--
Due to hosting requirements I need visits to <url:http://alfps.izfree.com/>.
No ads, and there is some C++ stuff! :) Just going there is good. Linking
to it is even better! Thanks in advance!- Hide quoted text -

- Show quoted text -- Hide quoted text -

- Show quoted text -


Thanks for the response! Here is a bit more background on what I'm
trying to do:

These legacy structures are a bunch of different messages. I'm
reading these off a pipe and they come in randomly. I want to pass
these to a factory to create the correct type of object and then I
want to pass these messages to several different handlers. These
handlers should only process the messages for which they are
interested in and just ignore the others. I had planned to have each
handler inherit from a base handler that knows how to process the
generic message type (type Base in my example above). Each derived
handler would have overloaded processing methods for the message types
that they are interested in. That way I can call Process( Base* s )
on a derived handler and all generic messages are passed through while
certain message types are caught and processed by an overloaded form
of Process() in the derived handler. The only place the messages need
to be handled differently based on type is inside the handlers, I want
to treat them gernerically outside the handlers; thus my desire for a
base class pointer. This may be easier to see with more psudo code,
but I can't post any at the moment. Does this clarify my intents at
all? Thanks again.
 
B

Bart van Ingen Schenau

Thanks for the response! Here is a bit more background on what I'm
trying to do:

These legacy structures are a bunch of different messages. I'm
reading these off a pipe and they come in randomly. I want to pass
these to a factory to create the correct type of object and then I
want to pass these messages to several different handlers. These
handlers should only process the messages for which they are
interested in and just ignore the others. I had planned to have each
handler inherit from a base handler that knows how to process the
generic message type (type Base in my example above). Each derived
handler would have overloaded processing methods for the message types
that they are interested in. That way I can call Process( Base* s )
on a derived handler and all generic messages are passed through while
certain message types are caught and processed by an overloaded form
of Process() in the derived handler. The only place the messages need
to be handled differently based on type is inside the handlers, I want
to treat them gernerically outside the handlers; thus my desire for a
base class pointer. This may be easier to see with more psudo code,
but I can't post any at the moment. Does this clarify my intents at
all? Thanks again.

Yes, it does clarify. And it also gives an opportunity to point out that
your design has a few pitfalls you need to look out for.
The first one is that overload resolution is done on the *static* (or
declared) type. In order to be able to call the correct overload of the
processing functions, if you only have a pointer to the base class
available, you need a double-dispatch mechanism.

See the output from this program for what I mean:

#include <iostream>
using namespace std;

struct MessageA {};
struct MessageB {};

class MessageBase;
template <class T> class Message;

void Process(const MessageBase*);
void Process(const Message<MessageA>*);

class MessageBase
{
public:
virtual ~MessageBase() {}
virtual void call_Process() = 0;
};

template <class T>
class Message : public MessageBase, public T
{
Message() : MessageBase(), T() {}

public:
virtual void call_Process()
{
Process(this);
}
static Message<T>* create() { return new Message<T>(); }
};

void Process(const MessageBase*)
{
cout << " Process(const MessageBase*)" << endl;
}

void Process(const Message<MessageA>*)
{
cout << " Process(const Message<MessageA>*)" << endl;
}

int main()
{
MessageBase* message1 = Message<MessageA>::create();
MessageBase* message2 = Message<MessageB>::create();

cout << "Calling \"Process\" directly" << endl;
Process(message1);
Process(message2);

cout << "Calling \"Process\" indirectly" << endl;
message1->call_Process();
message2->call_Process();
}

Bart v Ingen Schenau
 
A

awm129

Yes, it does clarify. And it also gives an opportunity to point out that
your design has a few pitfalls you need to look out for.
The first one is that overload resolution is done on the *static* (or
declared) type. In order to be able to call the correct overload of the
processing functions, if you only have a pointer to the base class
available, you need a double-dispatch mechanism.

See the output from this program for what I mean:

#include <iostream>
using namespace std;

struct MessageA {};
struct MessageB {};

class MessageBase;
template <class T> class Message;

void Process(const MessageBase*);
void Process(const Message<MessageA>*);

class MessageBase
{
public:
  virtual ~MessageBase() {}
  virtual void call_Process() = 0;

};

template <class T>
class Message : public MessageBase, public T
{
  Message() : MessageBase(), T() {}

public:
  virtual void call_Process()
  {
    Process(this);
  }
  static Message<T>* create() { return new Message<T>(); }

};

void Process(const MessageBase*)
{
  cout << "  Process(const MessageBase*)" << endl;

}

void Process(const Message<MessageA>*)
{
  cout << "  Process(const Message<MessageA>*)" << endl;

}

int main()
{
  MessageBase* message1 = Message<MessageA>::create();
  MessageBase* message2 = Message<MessageB>::create();

  cout << "Calling \"Process\" directly" << endl;
  Process(message1);
  Process(message2);

  cout << "Calling \"Process\" indirectly" << endl;
  message1->call_Process();
  message2->call_Process();

}

Bart v Ingen Schenau

Yes, it does clarify. And it also gives an opportunity to point out that
your design has a few pitfalls you need to look out for.
The first one is that overload resolution is done on the *static* (or
declared) type. In order to be able to call the correct overload of the
processing functions, if you only have a pointer to the base class
available, you need a double-dispatch mechanism.

See the output from this program for what I mean:

#include <iostream>
using namespace std;

struct MessageA {};
struct MessageB {};

class MessageBase;
template <class T> class Message;

void Process(const MessageBase*);
void Process(const Message<MessageA>*);

class MessageBase
{
public:
virtual ~MessageBase() {}
virtual void call_Process() = 0;

};

template <class T>
class Message : public MessageBase, public T
{
Message() : MessageBase(), T() {}

public:
virtual void call_Process()
{
Process(this);
}
static Message<T>* create() { return new Message<T>(); }

};

void Process(const MessageBase*)
{
cout << " Process(const MessageBase*)" << endl;

}

void Process(const Message<MessageA>*)
{
cout << " Process(const Message<MessageA>*)" << endl;

}

int main()
{
MessageBase* message1 = Message<MessageA>::create();
MessageBase* message2 = Message<MessageB>::create();

cout << "Calling \"Process\" directly" << endl;
Process(message1);
Process(message2);

cout << "Calling \"Process\" indirectly" << endl;
message1->call_Process();
message2->call_Process();

}

Bart v Ingen Schenau

Excellent point! I had assumed some sort of special processing would
be needed in the handlers to get to the correct overload, but this
illustrates the point quite clearly. I didn't think about overload
resolution being based on the static type... This may throw my design
out the window altogether.

As a side note, I ended up abandoning templates for the derived
classes. It turns out each and every message type would need its own
specialization anyway which kinda defeats the point of the templates.
So now I have 20 some odd similar yet different message classes.

I want this, but I don't think it can work (forgive any typos as I
don't have a compiler on this machine):


struct LegacyMessageA {};

class MessageBase
{
public:
virtual ~MessageBase() {}
};

class MessageA : public MessageBase
{
public:
MessageA(const MessageA msg) : MessageBase() //assume msg is copied
to m_msg
// assume accessors into m_msg
// assume common functionality for all messages
private:
LegacyMessageA m_msg
};

class HandlerBase
{
public:
HandlerBase(){}
virtual ~HandlerBase(){}
void process( MessageBase* msg );
protected:
void _doProcess( MessageBase* msg );
};

HandlerBase::process( MessageBase* msg )
{
// this is only ever going to call _doProcess( MessageBase* msg )
// as opposed to the proper overload for the actual type of msg.
// I want it to call the proper overload in the derived handler!

this->_doProcess( msg )
}

HandlerBase::_doProcess( MessageBase* msg )
{
//do nothing
}

class MessageAHandler : HandlerBase
{
public:
DerivedHandler() : HandlerBase() {}
private:
_doProcess( MessageA* msg );
};

MessageAHandler::_doProcess( MessageA* msg )
{
// special handling for type MessageA.
// I don't think this ever gets called as all messages go
// to MessageBase::_doProcess( MessageBase* msg )
}

int main()
{
LegacyMessageA msg;
MessageBase* message1 = new MessageA( msg );
HandlerBase* handler = new MessageAHandler();

handler->process( message1 );

delete message1;
delete handler;

return 0;
}


Can this scheme work at all? It all seemed so elegant yesterday!
 
B

Bart van Ingen Schenau

Excellent point! I had assumed some sort of special processing would
be needed in the handlers to get to the correct overload, but this
illustrates the point quite clearly. I didn't think about overload
resolution being based on the static type... This may throw my design
out the window altogether.

As a side note, I ended up abandoning templates for the derived
classes. It turns out each and every message type would need its own
specialization anyway which kinda defeats the point of the templates.
So now I have 20 some odd similar yet different message classes.

I want this, but I don't think it can work (forgive any typos as I
don't have a compiler on this machine):

It can work. Look up the Visitor design pattern, which is basically what
you need to implement.
It helps if you have an unchanging set of Message classes, which you
seem to have.
struct LegacyMessageA {};
You need a forward declaration of HandlerBase:
class HandlerBase;
class MessageBase
{
public:
virtual ~MessageBase() {}
Here you need a pure-virtual function to make a 'call back' to the
handler:
virtual void call_doProcess(HandlerBase*) = 0;
};

class MessageA : public MessageBase
{
public:
MessageA(const MessageA msg) : MessageBase() //assume msg is copied
to m_msg
// assume accessors into m_msg
// assume common functionality for all messages
Each concrete Message class must implement the call_doProcess to call
that function in the handler:
virtual void call_doProcess(HandlerBase* handler);
private:
LegacyMessageA m_msg
};

class HandlerBase
{
public:
HandlerBase(){}
virtual ~HandlerBase(){}
void process( MessageBase* msg );
protected:
HandlerBase must have the _doProcess functions public (unless you want
to juggle with friend declarations) and provide a default implementation
for each concrete Message class:
public:
virtual void _doProcess( MessageBase* msg );
virtual void _doProcess( MessageA* msg)
{ _doProcess(static_cast said:
};

HandlerBase::process( MessageBase* msg )
{
// this is only ever going to call _doProcess( MessageBase* msg )
// as opposed to the proper overload for the actual type of msg.
// I want it to call the proper overload in the derived handler!
Here you must ask the message to call _doProcess for you:
msg->call_doProcess(this);
this->_doProcess( msg )
}

HandlerBase::_doProcess( MessageBase* msg )
{
//do nothing
}

Implementation for the call_doProcess function can come here:
void MessageA::call_doProcess(HandlerBase* handler)
{
handler->_doProcess(this);
}
class MessageAHandler : HandlerBase
{
public:
DerivedHandler() : HandlerBase() {}
private:
_doProcess( MessageA* msg );
};

MessageAHandler::_doProcess( MessageA* msg )
{
// special handling for type MessageA.
// I don't think this ever gets called as all messages go
// to MessageBase::_doProcess( MessageBase* msg )

As this now overrides a function of the base class, it can get called by
MessageA::call_doProcess.
}

int main()
{
LegacyMessageA msg;
MessageBase* message1 = new MessageA( msg );
HandlerBase* handler = new MessageAHandler();

handler->process( message1 );

delete message1;
delete handler;

return 0;
}


Can this scheme work at all? It all seemed so elegant yesterday!

Yes. With the changes I indicated it becomes a bit less elegant (the
HandlerBase must know about all the specific messages), but it will
work.

Bart v Ingen Schenau
 
A

awm129

It can work. Look up the Visitor design pattern, which is basically what
you need to implement.
It helps if you have an unchanging set of Message classes, which you
seem to have.




You need a forward declaration of HandlerBase:
 class HandlerBase;


Here you need a pure-virtual function to make a 'call back' to the
handler:
    virtual void call_doProcess(HandlerBase*) = 0;> };


Each concrete Message class must implement the call_doProcess to call
that function in the handler:
    virtual void call_doProcess(HandlerBase* handler);



HandlerBase must have the _doProcess functions public (unless you want
to juggle with friend declarations) and provide a default implementation
for each concrete Message class:
  public:
    virtual void _doProcess( MessageBase* msg );
    virtual void _doProcess( MessageA* msg)


Here you must ask the message to call _doProcess for you:
    msg->call_doProcess(this);



Implementation for the call_doProcess function can come here:
void MessageA::call_doProcess(HandlerBase* handler)
{
  handler->_doProcess(this);

}



As this now overrides a function of the base class, it can get called by
MessageA::call_doProcess.









Yes. With the changes I indicated it becomes a bit less elegant (the
HandlerBase must know about all the specific messages), but it will
work.

Bart v Ingen Schenau


I had never heard of the visitor design pattern and it seems to very
much fit what I want to do. I had resigned myself to using RTTI and a
giant switch in HandlerBase but I like the double dispatch through the
Message much much better. Thank you very much for the help and
suggestions, I'm looking forward to getting this thing implemented!
 

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

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top