Inheritance question

A

Andrea Crotti

I'm doing a project which has to manipulate some network data.
They suggested me to use the classical big buffer with the many memcpy,
but since we're in C++ I don't see why not using classes instead of
structs.

So I thought something like

--8<---------------cut here---------------start------------->8---
Packet:
char type;
const char *buffer;

Beacon : Packet
int nr;
...
--8<---------------cut here---------------end--------------->8---

1. what does the "sizeof" return on a class exactly?
And (can I/does it make sense to) overload it?

2. does it make sense to keep the pointer to the content in "Packet"?
Or would be better to create then another class
"PacketHeader" and then Packet is the sum of both

3. how would I pack the data?
something like?

void Packet::pack(const char *buffer) {
memcpy($first_field, ...)
}

Then of course the sanity checks for the dimension must be done
outside this

4. about inheritance, if I have a constructor like

Packet(char type, ..)
then I can implement it for example with
Packet::packet(char type) : type(type)
right?

But what should I then do then in Beacon?
Create one constructor with the same arguments seem to not work in
general...

Thanks a lot,
Andrea
 
V

Victor Bazarov

I'm doing a project which has to manipulate some network data.
They suggested me to use the classical big buffer with the many memcpy,
but since we're in C++ I don't see why not using classes instead of
structs.

So I thought something like

--8<---------------cut here---------------start------------->8---
Packet:
char type;
const char *buffer;

What's the size? Why don't you simply use 'vector said:
Beacon : Packet
int nr;
...
--8<---------------cut here---------------end--------------->8---

1. what does the "sizeof" return on a class exactly?

The size of the object, as compiler knows it. This is a compile-time value.
And (can I/does it make sense to) overload it?

You can't.
2. does it make sense to keep the pointer to the content in "Packet"?
Or would be better to create then another class
"PacketHeader" and then Packet is the sum of both

You know the algorithm you need to implement. We don't.
3. how would I pack the data?
something like?

void Packet::pack(const char *buffer) {
memcpy($first_field, ...)

What's the third argument to memcpy? Not sure what '$firs_field' is.
Weird syntax if you ask me.
}

Then of course the sanity checks for the dimension must be done
outside this

"Sanity checks"?
4. about inheritance, if I have a constructor like

Packet(char type, ..)
then I can implement it for example with
Packet::packet(char type) : type(type)
right?
Yes.

But what should I then do then in Beacon?

What's the role of it?
Create one constructor with the same arguments seem to not work in
general...

Uh... Not sure what problem you're facing. Can you rephrase?

V
 
A

Andrea Crotti

First I rephrase better the "problem":
I need to manipulate network data, and I was wondering what could be the
best design to create a general packet and its subclasses.

For example a beacon is a network packet with some specific
informations.
So should it BE a Packet where the "content" contains the additional
informations, or should it HAVE a PacketHeader?

Maybe the first one is better since many functions could be reused...
What's the size? Why don't you simply use 'vector<char>'?

Yes maybe vector is better then, that thing should just be a pointer.
Since internally for many functions I have to use a
char *buffer

I don't know if I have advantages using a vector here.
The size is also given to the constructor.
This would be Packet

--8<---------------cut here---------------start------------->8---
class Packet
{
private:
char type;
const char *buffer;
size_t size;

protected:
ostream& out;

public:
Packet(ostream& out);
Packet(ostream& out, char type, const char *buffer, int size);
virtual void pack(char *buffer);
friend ostream& operator<<(ostream& s, const Packet& c);
};

I meant, is it like doing the "sizeof" of a struct with the same fields
inside?
Or something more?

And by the way "sizeof(ostream&)" returns a huge value, so I guess it's
not the size of the reference but of the object itself (which makes
sense of course).
What's the third argument to memcpy? Not sure what '$firs_field'
is. Weird syntax if you ask me.

I meant the classical way in C to create big buffers, like (very stupid
and wrong example...):

--8<---------------cut here---------------start------------->8---
int x = 10; int y = 20;
char buff[10];
memcpy(buff, &x, sizeof(int));
memcpy(buff + sizeof(int), &y, sizeof(int));
--8<---------------cut here---------------end--------------->8---

I guess in the end I stil have to do something like this, but I can
encapsulate it better.

"Sanity checks"?

Well checking if the "packet" fits in the buffer I give as input to the
function for example.

Given this beacon

--8<---------------cut here---------------start------------->8---
class Beacon : public Packet
{
private:
wf_simtime_t timestamp; // timestamp of creation
serial_t nr;
serial_t snr;
// can be done also since it has a 0-arguments constructor
Node &sender;

public:
Beacon(ostream& out);
// Beacon(const Packet&);
// virtual ~Beacon();
};
--8<---------------cut here---------------end--------------->8---

I don't find what should I have in the Beacon constructor to make also
the Packet constructor "happy"...
 
Ö

Öö Tiib

First I rephrase better the "problem":
I need to manipulate network data, and I was wondering what could be the
best design to create a general packet and its subclasses.

There are no universal generic way how to "manipulate any data with
non-disclosed nature and context". No difference if the data travels
with network, resides in database or is carried around with floppy-
disks.

Only thing i can tell so far is that since you seemingly do not have
generic "packets" you will perhaps want to have Packet as abstract
class, then it is harder to construct pure plain packet (even by
accident). Rest of the packets you would likely want to derive
directly from it, then compiler will lament if you forgot to override
a pure virtual in Packet.
 
V

Victor Bazarov

First I rephrase better the "problem":
I need to manipulate network data, and I was wondering what could be the
best design to create a general packet and its subclasses.

For example a beacon is a network packet with some specific
informations.
So should it BE a Packet where the "content" contains the additional
informations, or should it HAVE a PacketHeader?

Perhaps I don't understand something... You state yourself that "a
beacon *is a* network packet". "Is-a" relationship is *usually*
modelled in C++ with the use of a public inheritance. It's so
commonplace that I am suspecting some kind of a trick here.

The "have" implementation either implies containment or private
inheritance. Conversion from 'beacon' to 'packet' is then either
prohibited or provided by you (by means of a type conversion function,
for example).
Maybe the first one is better since many functions could be reused...


Yes maybe vector is better then, that thing should just be a pointer.
Since internally for many functions I have to use a
char *buffer

You can always extract the pointer to char from a vector, just take the
address of the first element (if it's not empty).
I don't know if I have advantages using a vector here.

The advantage is that you don't have to maintain your own dynamic buffer.
The size is also given to the constructor.
This would be Packet

--8<---------------cut here---------------start------------->8---
class Packet
{
private:
char type;
const char *buffer;
size_t size;

protected:
ostream& out;

public:
Packet(ostream& out);
Packet(ostream& out, char type, const char *buffer, int size);
virtual void pack(char *buffer);
friend ostream& operator<<(ostream& s, const Packet& c);
};


I meant, is it like doing the "sizeof" of a struct with the same fields
inside?

Uh... Yes.
Or something more?

In what sense can it be "more"? The size of the struct is the sum of
the sizes of its members, plus some optional padding.
And by the way "sizeof(ostream&)" returns a huge value, so I guess it's
not the size of the reference but of the object itself (which makes
sense of course).

Yes, since references aren't objects (and don't occupy storage) when you
use one as the argument of 'sizeof', you in fact are getting the size of
the object the reference refers to.
What's the third argument to memcpy? Not sure what '$firs_field'
is. Weird syntax if you ask me.

I meant the classical way in C to create big buffers, like (very stupid
and wrong example...):

--8<---------------cut here---------------start------------->8---
int x = 10; int y = 20;
char buff[10];
memcpy(buff,&x, sizeof(int));
memcpy(buff + sizeof(int),&y, sizeof(int));
--8<---------------cut here---------------end--------------->8---

I guess in the end I stil have to do something like this, but I can
encapsulate it better.

If you put your 'ints' in a struct, you can simply memcpy the entire
struct (instead of doing the packing yourself).
Well checking if the "packet" fits in the buffer I give as input to the
function for example.

Given this beacon

--8<---------------cut here---------------start------------->8---
class Beacon : public Packet
{
private:
wf_simtime_t timestamp; // timestamp of creation
serial_t nr;
serial_t snr;
// can be done also since it has a 0-arguments constructor
Node&sender;

public:
Beacon(ostream& out);
// Beacon(const Packet&);
// virtual ~Beacon();
};
--8<---------------cut here---------------end--------------->8---

I don't find what should I have in the Beacon constructor to make also
the Packet constructor "happy"...

Now that you've explained it a bit better, I am thinking that there are
*numerous* libraries for network communication. Why do you think you
have to reinvent the wheel?

V
 
A

Andrea Crotti

Victor Bazarov said:
Perhaps I don't understand something... You state yourself that "a
beacon *is a* network packet". "Is-a" relationship is *usually*
modelled in C++ with the use of a public inheritance. It's so
commonplace that I am suspecting some kind of a trick here.

The "have" implementation either implies containment or private
inheritance. Conversion from 'beacon' to 'packet' is then either
prohibited or provided by you (by means of a type conversion function,
for example).

Yes sure it's a Is-a, I just wrote also has-a in because it would also
be a possibility, but of course it doens't make much sense.

I mean I did a similar thing in C some time ago and there I used only
structures so it's a bit different, there were no is-a relation possible...
The advantage is that you don't have to maintain your own dynamic
buffer.

Yes that's not bad, but actually I should always know how much I'm
putting in memory, so it should not be a problem.
But if it makes my life easier why not, could be better.
In what sense can it be "more"? The size of the struct is the sum of
the sizes of its members, plus some optional padding.

I meant more when you create a class
class X {
int x;
};

sizeof(new X()) == sizeof(int)
and is it guaranteed (more important)?
I meant the classical way in C to create big buffers, like (very stupid
and wrong example...):

--8<---------------cut here---------------start------------->8---
int x = 10; int y = 20;
char buff[10];
memcpy(buff,&x, sizeof(int));
memcpy(buff + sizeof(int),&y, sizeof(int));
--8<---------------cut here---------------end--------------->8---

I guess in the end I stil have to do something like this, but I can
encapsulate it better.

If you put your 'ints' in a struct, you can simply memcpy the entire
struct (instead of doing the packing yourself).

Yes well but I don't have a struct if I create the class, or should I
maybe keep both approaches.
What I thought was something like

class X {
int x;
int y;
char buffer[100];
};

and doing
X b;
b.pack(char *result);
it returns in that buffer everything packed, where the fields with
size > 1 byte are converted in network order and everything is packed
(with __packed__ attribute).

Would that be possible/does it make any sense?
Now that you've explained it a bit better, I am thinking that there
are *numerous* libraries for network communication. Why do you think
you have to reinvent the wheel?

V

It's not so simple, what I'm doing is writing code for some network
simulation on top of a framework that is on top of omnet++ and some
libraries to compile on real machines.

I have enough stuff to deal with already, and it's not really much the
part of abstraction I need, but I want to do it as well as possible of
course since also other people might use it...
 
A

Andrea Crotti

Öö Tiib said:
There are no universal generic way how to "manipulate any data with
non-disclosed nature and context". No difference if the data travels
with network, resides in database or is carried around with floppy-
disks.

Only thing i can tell so far is that since you seemingly do not have
generic "packets" you will perhaps want to have Packet as abstract
class, then it is harder to construct pure plain packet (even by
accident). Rest of the packets you would likely want to derive
directly from it, then compiler will lament if you forgot to override
a pure virtual in Packet.

Sure that's right.
But I also don't want to exaggerate with the abstraction, since are
mostly the algorithms that will have to be tested, the data structures
should remain the same.

And the algorithms independence part is maybe trickier, what is
normally used in c++?

Is there anything easier than plain pointers to functions?
Not sure templates would help here either...
 
A

Andrea Crotti

And another thing, supposing I have two functions with the prototype

--8<---------------cut here---------------start------------->8---
void f1(char *buf, int size);
void f2(const char *buf, int size);
--8<---------------cut here---------------end--------------->8---

now if I want instead pass a Packet to both how do I keep the const
correct?
Should I create a new superclass of Packet called ConstPacket that has
this constraint?

And in general, supposing I want to use the fields of a class like a
struct, with a lot of getting and setting.
Does it still make sense to keep them private and create all the
getter/setter methods?

Making them public would not be the same in this case?
 
J

James Kanze

I'm doing a project which has to manipulate some network data.
They suggested me to use the classical big buffer with the many memcpy,
but since we're in C++ I don't see why not using classes instead of
structs.
So I thought something like
--8<---------------cut here---------------start------------->8---
Packet:
char type;
const char *buffer;
Beacon : Packet
int nr;
...
--8<---------------cut here---------------end--------------->8---
1. what does the "sizeof" return on a class exactly?

The number of bytes an object of the class type occupies in an
array.
And (can I/does it make sense to) overload it?

You can't overload it; allowing this would break some important
invariants in the language.
2. does it make sense to keep the pointer to the content in "Packet"?
Or would be better to create then another class
"PacketHeader" and then Packet is the sum of both

Not sure, but generally, I'd think I'd make Packet a base class,
3. how would I pack the data?

By pack, I presume you mean marshall.
something like?
void Packet::pack(const char *buffer) {
memcpy($first_field, ...)
}

That doesn't work, except in a few very limited cases. You have
to actually marshal the data, depending on its defined format.
Then of course the sanity checks for the dimension must be done
outside this
4. about inheritance, if I have a constructor like
Packet(char type, ..)
then I can implement it for example with
Packet::packet(char type) : type(type)
right?

Yes. But if you're using inheritance, you probably don't need
the type.

I'd probably go with some sort of Buffer class to manage the raw
memory, to handle reading and writing, and perhaps to marshall
the basic types, then a Packet base class, which "owns" the
Buffer, once it has been created, and a derived class for each
type of packet. (To do this efficiently, you'll probably want
to dynamically allocate the buffer, and manage it using smart
pointers.)
But what should I then do then in Beacon?
Create one constructor with the same arguments seem to not work in
general...

You'll have to forward the arguments from the derived class
constructor. Normally, however, about the only argument I see
is the buffer.
 
A

Andrea Crotti

James Kanze said:
By pack, I presume you mean marshall.

I meant how to send data over the network.
Normally in C I use __packed__ structs and I have to make sure that
every type which is > 1 Byte has to be converted in network order before
being sent.

What's the best way to do that using classes instead?
Yes. But if you're using inheritance, you probably don't need
the type.

I'd probably go with some sort of Buffer class to manage the raw
memory, to handle reading and writing, and perhaps to marshall
the basic types, then a Packet base class, which "owns" the
Buffer, once it has been created, and a derived class for each
type of packet. (To do this efficiently, you'll probably want
to dynamically allocate the buffer, and manage it using smart
pointers.)

Ah I didn't think about a Buffer class, not a bad idea right but I'm
afraid to overcomplicate the design a little bit...
Well I'll start with something simpler and let's see if it's useful
 
J

Jorgen Grahn

I'm doing a project which has to manipulate some network data.
They suggested me to use the classical big buffer with the many memcpy,

I've never heard of that as something classical, so I don't
understand what "they" were refering to.
but since we're in C++ I don't see why not using classes instead of
structs.

Well, structs *are* classes ... and "big buffers" are neither.

I'm assuming your problem is not to "manipulate some network data" but
to implement one end of some protocol. And it's probably some
datagram-based protocol rather than streamed text over TCP.

My advice is:

- Don't focus on those messages. Focus on the /protocol/ and the
things the protocol manipulates. The /state/, if there is such a
thing.

- Don't insist on run-time polymorphism for the messages. That's a
whole lot of complexity for eliminating what might be just one
switch ... case:

while(1) {
char buf[1000];
recv(socket, buf, sizeof buf);
RxMessage msg(buf); // no copying needed
handle(msg);
}

void handle(const RxMessage& rx)
{
switch(msg.type) {
...
}
}

/Jorgen
 
A

Andrea Crotti

Jorgen Grahn said:
Well, structs *are* classes ... and "big buffers" are neither.

Maybe you meant "aren't" classes? Otherwise I don't get the neither...>
I'm assuming your problem is not to "manipulate some network data" but
to implement one end of some protocol. And it's probably some
datagram-based protocol rather than streamed text over TCP.

My advice is:

- Don't focus on those messages. Focus on the /protocol/ and the
things the protocol manipulates. The /state/, if there is such a
thing.

- Don't insist on run-time polymorphism for the messages. That's a
whole lot of complexity for eliminating what might be just one
switch ... case:

while(1) {
char buf[1000];
recv(socket, buf, sizeof buf);
RxMessage msg(buf); // no copying needed
handle(msg);
}

void handle(const RxMessage& rx)
{
switch(msg.type) {
...
}
}

What I have to do is to take the raw data, see what it is and pass it to
the next level.
This also in the other direction. I don't really implement network
protocols but more routing protocols (with omnet also).

So what I thought was something like

--8<---------------cut here---------------start------------->8---
Packet p = Packet::createPacket(buffer);
--8<---------------cut here---------------end--------------->8---

where that thing will internally create an object of the right type with
a switch case like

--8<---------------cut here---------------start------------->8---
static Packet Packet::createPacket(buffer) {
switch (buffer[1])
case 0:
// create the object on the rest of the buffer
return Beacon(buffer[1:]);
...
--8<---------------cut here---------------end--------------->8---

The the "handlePacket" function would be always called in the same way
but behaving differently.

I'm not sure either is worthy because I have to see how many things are
actually equal between the different packets, but well I think I should try...
 
J

Jorgen Grahn

Maybe you meant "aren't" classes? Otherwise I don't get the neither...>

No, I meant "structs *are* classes, but 'big buffers' are neither
structs nor classes".

I could explain better, but you snipped the context which would make
that possible.
....

What I have to do is to take the raw data, see what it is and pass it to
the next level.

Do you mean "layer"? I have to admit that I don't believe in layers
in the actual code. They are usually just a sign of over-design.
This also in the other direction. I don't really implement network
protocols but more routing protocols (with omnet also).

Sorry for continuing with the semantics, but routing protocols are
surely network protocols. But I see what you mean; you're not
implementing e.g. an UDP server application, so that event loop isn't
very helpful.
So what I thought was something like

--8<---------------cut here---------------start------------->8---
Packet p = Packet::createPacket(buffer);
--8<---------------cut here---------------end--------------->8---

where that thing will internally create an object of the right type with
a switch case like

--8<---------------cut here---------------start------------->8---
static Packet Packet::createPacket(buffer) {
switch (buffer[1])
case 0:
// create the object on the rest of the buffer
return Beacon(buffer[1:]);
...
--8<---------------cut here---------------end--------------->8---

Hm, what's the relation between the types Beacon and Packet?
This isn't Java; you're not returning some kind of reference,
but an actual object.
The the "handlePacket" function would be always called in the same way
but behaving differently.

I'm not sure either is worthy because I have to see how many things are
actually equal between the different packets, but well I think I should try...

I think you should do an experiment *without* factory functions,
dynamic memory allocation and run-time polymorphism first. There are
too many people already whose got reaction to any problem is an
abstract factory ... that /will/ also work, but it might be a waste of
your time, CPU time, and the power of C++.

/Jorgen
 

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

Similar Threads

Good design question 2
Inheritance - style question 3
Request data is empty 0
inheritance 1
Adding adressing of IPv6 to program 1
Simple inheritance-template question 17
Lexical Analysis on C++ 1
Buffering object 8

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top