Jump to content
  • Advertisement
Sign in to follow this  
Mononofu

Virtual templated functions not possible - Workaround?

This topic is 3787 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Tonigh, I ran into a problem: I can't find a simple way how I could implement the message passing system I would like to use. I want to use templated MessageReciever and MessageSender classes, which are connected with each other by a communication manager. Every class which wants to communicate has to implement 3 basic functions: // Tells the manager class which messages this class will send virtual std::string getSenderTopic() { return ""; } // Tells the manager class which messages this class wants to recieve virtual std::string getRecieverWishes() { return ""; } // Adds a Reciever of 'topic' to the list of recievers of the specific Sender template< class Type> virtual void addReciever(std::string topic, MessageReciever< Type >* reciever); They are all virtual since every class will implement them differently, most important because I can't create a std::map which can store all of my MessageReciever classes, without knowing how it will be templated. So I thought I would let every class implement these functions by itself, so it can store the MessageReciever and MessageSender classes easily. (ie.: MessageReciever<std::string> stringReciever; ) But since I can't use virtual templates ( WHY ? What's the problem with them? ) I have to find a workaround. I thought for hours, but didn't find a solution yet, so I'm asking you for help. If you need more specific code, just drop me a note. Edit: I have to use templates since I want to be able to create arbitary communication channels, without changing the code of my framework.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Mononofu

But since I can't use virtual templates ( WHY ? What's the problem with them?


Templates are instantiated at compile-time. Vritual functions however must exist in base class.

Consider:
struct Handler {
template < class T >
virtual void onMessage(Message msg);
};
Then:
struct Derived : public Handler {
virtual void onMessage(MessageA msg);
virtual void onMessage(MessageB msg);
virtual void onMessage(MessageC msg);

template < class T>
virtual void onMessage(Message msg);
};

Since these functions are virtual, they must exist in base class, yet base class doesn't know about these specializations until they are made.

Even worse, every single instance must create a vtable entry, quickly meaning there could be hundreds, even thousands entries all in base class - which never defined them.


The usual solution is this:

struct Base {
virtual void invoke() = 0;
};

template < class Whatever >
struct Derived : public Base {
virtual void invoke() {
Whatever w;
w.foo();
}
};


You cannot avoid virtual call. You can use function pointer if you wish, but that's still the same thing, it may just occasionally be slightly more useful, at the expense of more verbose code.

Share this post


Link to post
Share on other sites
Does this solve my problem?

You know, I need to have templated function arguments as well, ie. I need to pass your 'w' object into this function aswell. And I need to store this objects till the class has time to check its messages.

So I would need void invoke(Whatever w) instead of void invoke(). So I can't avoid using templates in the base class.

Are there any other solutions to my problem? I still couldn't think of anything.

Edit:
I need a way to pass arbitary objects (std::string, custom structs / classes, etc.) between different objects asynchronously. (ie between different threads)

Share this post


Link to post
Share on other sites
Quote:
Original post by Mononofu

Are there any other solutions to my problem? I still couldn't think of anything.


Going through boost::bind would be a good start.

There's also boost::channel (google for sourceforge, it wasn't accepted).

Other than that
template < class P1, class P2 >
struct Channel {
void operator()(P1 p1, P2 p2);

void addReciever(boost::function<void(P1,P2)> callback);
};
which then results in something like


void cb(int, float);
...

Channel<int, float> if_channel;
if_channel.addListener(&cb);
if_channel(10, 3.14f);


You just need to specialize the channel by parameters used. The virtual dispatch here is hidden in boost::function.

If you want to define your receiver type more compactly, then you can use the Channel for that:
  typedef Handler<P1,P2> HandlerType;
...
//And in user code
typedef Channel<int, float> IFChannel;

IFChannel::HandlerType myHandler;

Share this post


Link to post
Share on other sites
Problem: Not the channel itself handles the recievers, but a common Manager. Why? Well, I need different types of recievers depending how the class containing the reciever is executed relative to the sender (same thread, synched, etc. ).

I will think about your answers tomorrow, since its kinda late now (atleast where I life).

Share this post


Link to post
Share on other sites
Quote:
Original post by Mononofu
Problem: Not the channel itself handles the recievers, but a common Manager. Why? Well, I need different types of recievers depending how the class containing the reciever is executed relative to the sender (same thread, synched, etc. ).


None of this requires a "manager".

But do look at boost::channel. It does all that already.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mononofu
template< class Type> virtual void addReciever(std::string topic, MessageReciever< Type >* reciever);
This won't work [as you have clearly seen], but what you are trying to do is actually a pretty common thing. What you're looking for is known as "Multiple dispatch", and C++ doesn't support it natively. This means you're going to have to be implementing a hackish workaround. You can search on these very forums for "Multiple dispatch" to yeild quite a number of results that shows a discussion from people in your exact same boat, but the short of the matter is that you're going to have to be implementing this functionality yourself.

The 'easy' way to do it is to replace
template<class Type> virtual void addReciever(std::string topic,MessageReciever<Type> reciever
with
virtual void addReciever(std::string topic,BaseMessageReciever reciever)
and derive a sort of generic templated type from BaseMessageReciever like
template<class Type>MessageReciever : public BaseMessageReciever
and keep some sort of type value that you can hit with an associative lookup [or a big switch statement], and cast to the appropriate type. It's ugly, but pretty much everything you'll be doing will be roughly of this flavor.

Short of this, you'll be implementing a 2 dimensional v-table, much like the 1-d one that is currently used for the virtual function lookups already, and chew up all the values you pass around in a way similar to how the compiler currently does. It's nasty, but that is the way of things in C++ land.

That is, IF you insist on having multiple dispatch.

It turns out that most things that appear to require multiple dispatch can be refactored into a different form that do not need it.

For example, if you want to register a reciever of a certain type as in your above example, you pass to it a reciever that takes a sort of message base class, that also includes some function that returns a value representing the type of message this reciever is interested in accepting. The first thing that is done in this reciever's 'accept message' virtual function is to cast this message to it's appropriate type, and use it that way. You can get a lot of this for free with the implementation of a base reciever, with a templatized generic reciever that instates it's own virtual function for handling the specific type:
class BaseMessageType
{
static int msgType;//redefine for each message type to hide the previous type
virtual int MyMsgType()
{
return msgType;
}
...
};
class BaseReciever
{
virtual void RecieveMsg(BaseMessageType msg) = 0;
virtual int MessageThatIRecieve() = 0;
};
template<class Type>
class TReciever : BaseReciever
{
virtual void Recieve(T msg){something}
virtual void RecieveMsg(BaseMessageType msg)
{
Recieve((T)msg);
}
virtual int MessageThatIRecieve()
{
return T.msgType;
}
};
When you send a message, you call the messages 'MyMsgType' function, hit that with an associative check for the reciever for that type, and call the reciever's function. You get a very loose definition of type safety, but really this is a pretty shaky deal.

[oh, and it's receiver, but it's your code.]

[Edited by - Drigovas on August 2, 2008 5:33:01 PM]

Share this post


Link to post
Share on other sites
Thanks for all your answers, now I almost got it working! Especially you, Drigovas, and thanks for the correction of Receiver, I didn't even notice that. (Probably because in german, you would 'ie' like the 'ei' in receiver ^^ )

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!