Help with runtime polymorphism

Started by
7 comments, last by nivlekio 15 years, 3 months ago
Hi all I am a bit confused with runtime polymorphism I understand the basics of polymorphism but i dont really know how to use runtime polymorphism in my situation. I have a bunch of packets which are made from classes. All the classes have headers classes which contain the packet id, sender id and some other info.

class CUnitPosPacket PlayerPosPacket;
class CChatPacket PlayerChatPacket;
class CUserStatsPacket UserStatsPacket;
class CMapPacket MapPacket;

send(socket,(const char *)PlayerPosPacket,sizeof(PlayerPosPacket),0);

My problem is the recieving part normally when you send and recieve data you use structs and the recieving side uses a union which fills the recived data to the approriate struct in the union. I was wondering how would I use runtime polymorphism to make a union like object for my classes? to do something like this.

class CRecvPacket m_RecvPacket;

recv(socket,(char *)m_RecvPacket,sizeof(m_RecvPacket),0);

switch(m_RecvPacket.m_Header.PacketID)
{
case UNITPOSPACKET:
// doing stuff with data
m_RecvPacket.PlayerPosPacket.x;
m_RecvPacket.PlayerPosPacket.y;
m_RecvPacket.PlayerPosPacket.z;
break;

case CHATPACKET:
// doing stuff with data
m_RecvPacket.PlayerChatPacket.szChatBuffer;
break;

case USERSTATSPACKET:
// doing stuff with data
m_RecvPacket.UserStatsPacket.iWins;
m_RecvPacket.UserStatsPacket.iLosses;
m_RecvPacket.UserStatsPacket.iDisconnects;
break;
}

Thanks in advance for ur help
Advertisement
Give your class a type conversion operator. If CRecvPacket has a member "operator T () const;" you can implement it as

CRecvPacket :: operator T () const {    T t;    switch (this->foo) {        case A:             t.firstpartofunion = this->bar;             break;    // ...    return t;}


Now you can pass an instance of CRecvPacket to a function argument of type T. You can overload the recv function and CRecvPaclet::operator T can be virtual.
Ok kool is this "operator T () const;" buisness a part of polymorphism or stl?

Thanks for the info btw :D
Quote:Original post by nivlekio
Ok kool is this "operator T () const;" buisness a part of polymorphism or stl?
Neither one, it is a part of the C++ language (google for 'operator overloading'). I am however not sure if it would help you in any way here - what is wrong with the switch statement you are using currently?

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by swiftcoder
Neither one, it is a part of the C++ language (google for 'operator overloading'). I am however not sure if it would help you in any way here - what is wrong with the switch statement you are using currently?


Well atm when I recieve the data i copy it to a char array from the recv function, then i memcpy the data from the char array to the header class then i use the ID packet variable in a switch statement. Then in the case statement i memcpy the char array to the appropriate class.

I heard that its not recommened to memcpy data into classes and also i heard memcpy is a c function and you should generally not mix c with c++, so i want to avoid any potential bugs when i memcpy data in to a class and i want to keep to good coding standards by only using c++ functions.

This is an example of what have atm

class CPacketHeader RecvHeaderChecker;class CUnitPosPacket RecvPlayerPosPacket;class CChatPacket RecvPlayerChatPacket;class CUserStatsPacket RecvUserStatsPacket;char RecvBufffer[256];memcpy_s(&RecvHeaderChecker,sizeof(RecvHeaderChecker),&RecvBufffer,sizeof(RecvHeaderChecker));switch(RecvHeaderChecker.PacketID){case UNITPOSPACKET:memcpy_s(&RecvPlayerPosPacket,sizeof(RecvPlayerPosPacket),&RecvBufffer,sizeof(RecvPlayerPosPacket));break;case CHATPACKET:memcpy_s(&RecvPlayerChatPacket,sizeof(RecvPlayerChatPacket),&RecvBufffer,sizeof(RecvPlayerChatPacket));break;case USERSTATSPACKET:memcpy_s(&RecvUserStatsPacket,sizeof(RecvUserStatsPacket),&RecvBufffer,sizeof(RecvUserStatsPacket));break;}


Also i cant use the union with my classes as they have virtual functions in them. So im looking for an alternative way. You said the way spraff recommened might not work, if it does not work how should i go about it?
Quote:Original post by nivlekio
Well atm when I recieve the data i copy it to a char array from the recv function, then i memcpy the data from the char array to the header class then i use the ID packet variable in a switch statement. Then in the case statement i memcpy the char array to the appropriate class.

I heard that its not recommened to memcpy data into classes and also i heard memcpy is a c function and you should generally not mix c with c++, so i want to avoid any potential bugs when i memcpy data in to a class and i want to keep to good coding standards by only using c++ functions.
memcpy() is indeed very dangerous when applied to C++ classes, so instead use std::copy().

Quote:Also i cant use the union with my classes as they have virtual functions in them. So im looking for an alternative way. You said the way spraff recommened might not work, if it does not work how should i go about it?
It will work, and probably well enough for what you are doing, but from a performance standpoint, you aren't reducing the number of copies made (just hiding them inside casts).

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Kool thanks ill look up std::copy().

But I am still lost with runtime polymorphism how can I use it like the union structure? but in this case for my different types of classes?
memcpy() is just fine, if you know what you're doing. However, memcpy() doesn't work for copying into storage for an object that's not POD (that has virtual functions, for example).

Note that a template with type "T" is not runtime polymorphism; it's static polymorphism. Also, a switch() is not runtime polymorphism. Virtual functions are what are used in C++ to implement runtime polymorphism.

If you want to use polymorphism to create packets, then you can for example use a class factory pattern:

class Packet {  public:    virtual bool Unpack(void const *&data, size_t size) = 0;    virtual bool Pack(void *&data, size_t size) = 0;    virtual void Dispatch(PacketDispatchTarget *t) = 0;};template<typename T> class PacketBase : Packet{  public:    PacketBase() {}    PacketFactory *Factory() { return PacketFactory<T>::instance_; }};class PacketFactoryBase {  public:    PacketFactoryBase(int id) {      assert(Factories()[id] == NULL);      Factories()[id] = this;    }    static PacketFactory *PacketFactoryForId(int id) {      if (Factories().find(id) == Factories().end()) return 0;      return Factories()[id];    }  protected:    int id_;    virtual Packet *MakePacketBase() = 0;    static map<int, PacketFactory *> &Factories() {      static map<int, PacketFactory *> it;      return it;    }};template<typename T, int Id> class PacketFactory {  public:    PacketFactory() : PacketFactoryBase(Id) {      instance_ = this;    }    enum { ID = Id; }    static PacketFactory *instance_;    T *MakePacket() { return static_cast<T *>(PacketFactoryBase::MakePacketBase()); };  protected:    Packet *MakePacket() { return new T(); }}class SignInPacket : public PacketBase<SignInPacket> {  public:    SignInPacket(char const *name, char const *password) :       name_(name), password_(password) {}    bool Unpack(void const *&data, size_t &size) {      return unpack_string(data, size, name_) &&        unpack_string(data, size, password_);    }    bool Pack(void *&data, size_t &size) {      return pack_string(data, size, name_) &&        pack_string(data, size, password_);    }    void Dispatch(PacketDispatchTarget *t) {      ... do whatever ...    }    std::string name_;    std::string password_;};... more packet types ...PacketFactory<SignInPacket, 1> signInFactory;PacketFactory<SayHelloPacket, 2> sayHelloFactory;...


After you have set this up, you can use both static (compile-time) and dynamic (runtime) polymorphism.

When you want to pack up a packet to a byte array, put an integer into the byte array for the ID of the packet, and then call Packet->Pack(data, size) to put it into the array. On the other end, read the integer out and call PacketFactoryBase::PacketFactoryForId() to get the appropriate packet factory; call that to create a new packet, and call Unpack(data, size) on that packet to re-inflate it from data. You can then, for example, dispatch the packet to a dispatch target that you define (or do whatever else you want with it).

This is obviously just a loose illustration that won't quite compile, but it should hopefully be clear enough to show how you can do packet creation, sending, receiving and dispatching entirely without "if" or "switch" statements, which is what using polymorphism is all about!

[Edited by - hplus0603 on January 1, 2009 11:59:28 AM]
enum Bool { True, False, FileNotFound };
Ok thanks hplus0603 for the info, I dont understand some of your code but ill look up into it and try somethnig like it when I have fully understood the concept hehe.

Thanks

This topic is closed to new replies.

Advertisement