4

In C++ I have a base class Packet and then a lot of children APIPacket, DataIOPacket etc. Now I want to store an incoming packet and since I don't know the type I store this in a variable:

Packet packet;
packet = DataIOPacket();

But now DataIOPacket has a function getAnalogData(); I can't do:

packet.getAnalogData();

Since packet doesn't have this function. In java I think this is possible since the actual type of the object stored in packet is not lost (is this correct?). But in C++ my DataIOPacket is narrowed into a Packed and loses it's functions that haven't been declared in Packet.

You could make a virtual function in Packet for every function in every child. But for me this would mean a lot of functions in Packet which in most cases should not be called. It has no use calling getAnalogData() on an APIPacket.

How is this problem solved? I can't find the answer but I feel a lot of people must encounter it.

You could do something with typecasting back to DataIOPacket and APIPacket but this doesn't really seem a clean solution either.

Are there maybe libraries that solve my problem?

Rgds,

Roel

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
Silver
  • 1,075
  • 3
  • 12
  • 37
  • 1
    [Related](http://stackoverflow.com/q/15306367/1174378) – Mihai Todor Mar 14 '13 at 16:50
  • 2
    I changed `Packet packet();` to `Packed packet;` so that it defines an object (rather than a function prototype), to match its description. That doesn't fix the immediate problem, that when you store an object of a derived type as a `Packet` object the derived object gets sliced to a `Packet` object. `packet` is **not** an object of the derived type. The code should store a pointer or reference to `Packet`, not an object. – Pete Becker Mar 14 '13 at 17:04

3 Answers3

4

This is possible in java and in c++ too. you need to do a dynamic_cast to check for the type.

 Packet* packet;
packet = new DataIOPacket();

      DataIOPacket dio* = dynamic_cast<DataIOPacket*>(packet); 
            if (dio != 0)
            {
             dio->DoSomeChildMethodStuff();
            }
omer schleifer
  • 3,897
  • 5
  • 31
  • 42
  • 1
    What does the `!-` operator do? – Mihai Todor Mar 14 '13 at 16:53
  • 5
    It's like != , only better :) . thanks,I've corrected my answer – omer schleifer Mar 14 '13 at 16:54
  • So this is completely typesafe? How could I not find this.. I asked a lot of C++ developpers and nobody gave me this answer After googling I have to ask if the original packet should also be a pointer? Because in my example DataIOPacket is put into a Packet and I am guessing it has now taken the size of a Packet and thrown everything else away. – Silver Mar 14 '13 at 16:58
  • 1
    @RoelStorms That's because this is not the nicest thing to do. I guess you must have some code design issue if you really need to do such a thing. Check out the link I posted in the other comment and after you go through it, you might consider redesigning your code. – Mihai Todor Mar 14 '13 at 17:00
  • What do you mean type safe? if packet is actually of type DataIOPacket, you will get a pointer of this type with value. otherwise the pointer will have 0 (null) in it. and by the way, you should probably use packet as a pointer: Packet* packet; as for the design issue , I totally agree with @MihaiTodor – omer schleifer Mar 14 '13 at 17:00
  • @MihaiTodor Will do. Packet being a pointer I also figured. It doesn't even work when packet isn't a pointer I guess? Because now DataIOPacket which is larger is put into Packet and so the specific functions of DataIOPacket are lost? edit: Pete Becker answered this last. – Silver Mar 14 '13 at 17:05
  • I could keep a list for each type of packet that I receive so no typecasting has to be done. And my main thread will also know which type a Packet has since every Packet in a certain list has only 1 type. This might be better since now I would have to check what type a Packet is before typecasting and before calling specific functions. I think that wraps it up, thanks all! – Silver Mar 14 '13 at 17:13
  • @RoelStorms: Note that solution provided with this answer will NEVER end up with `DoSomeChildMethodStuff` being called since it is affected by object slicing. See my answer. – LihO Mar 14 '13 at 17:18
  • Agrred, I also suggested that packet should be a pointer , will correct my answer. – omer schleifer Mar 14 '13 at 17:21
1

in C++ my DataIOPacket is narrowed into a Packet and loses it's functions that haven't been declared in Packet

It happens because you assign the object of type DataIOPacket to the object of type Packet, which results in this object being sliced (see What is object slicing?).

What you are actually looking for is a way how you could find out in run-time, whether the object that you are working with has been created as an instance of DataIOPacket. In other words you are looking for Run-Time Type Identification (RTTI).

To avoid slicing, you need to have a reference or a pointer to the object. Type of this object will be then identified in run-time:

Packet* packet;
packet = new DataIOPacket();

now packet is a pointer to the object of type DataIOPacket (run-time), but the type of the pointer is Packet* (compile time). In order to invoke a method that is specific for DataIOPacket class on this object, the compiler needs to know that this pointer points to the object of type that provides that method. A proper way of down-casting a pointer to the polymorphic type is by using dynamic_cast, which returns NULL in case that this object can't be cast to this type:

Packet* packet;
packet = new DataIOPacket();
DataIOPacket* dataIOPacket = dynamic_cast<DataIOPacket*>(packet);
if (dataIOPacket)
    dataIOPacket->getAnalogData();

Note that this is also possible with objects with automatic storage duration:

DataIOPacket packet;
Packet* pPacket = &packet;

DataIOPacket* dataIOPacket = dynamic_cast<DataIOPacket*>(pPacket);
if (dataIOPacket)
    dataIOPacket->getAnalogData();

In this case the type of the packet is the crucial factor that decides whether dynamic_cast will succeed or not. The object has to be created as an instance of DataIOPacket in order to call the getAnalogData method on it.

Community
  • 1
  • 1
LihO
  • 41,190
  • 11
  • 99
  • 167
  • Why should packet be declared on heap? Is the answer of Pete Beckers not sufficient? Making packet into a pointer and defining a DataIOPacket on stack? – Silver Mar 14 '13 at 18:20
  • 1
    @RoelStorms: Sorry for the confusion, in this case it doesn't matter whether the object is on the stack or on the heap. I've edited my answer to make things clear. – LihO Mar 14 '13 at 19:16
0

Ok I figured out like some people suggested, that having to dynamic cast suggests my structure is wrong. So I am going to change things that each type of packets gets it's own storage space. I am storing packets where I read them in and they are then passed on to a main thread that does all the processing. Indeed it makes more sense that you keep the type. My main thread doesn't want to figure out which type the packets have since it has a different way to deal with different packets. If you would ever need the behavior I described in my question I think dynamic cast is the way to go. But before doing so you should really ask yourself if you shouldn't change the structure. And using a dynamic cast will not work on objects since they are undergoing slicing. It only works on pointers.

Silver
  • 1,075
  • 3
  • 12
  • 37