Valhalla Legends Forums Archive | Advanced Programming | Structures and inheritence

AuthorMessageTime
Arta
Is this safe?

[code]

struct SBase
{
   int i;
   char c;
};

struct SDerived: public SBase
{
   double d;
};

SBase Base;
SDerived Derived;

memcpy(&Base, (SBase*)&Derived, sizeof(SBase));

[/code]

The idea being that Base now contains the values i and c from Derived. This seems to work, but I wondered if there might be any complications I should be aware of before I go code things based on the idea.
February 19, 2004, 9:04 AM
iago
I wouldn't recommend using C functions (memcpy) with C++ constructs (inheritance). I don't think it's safe. I would recommend writing a function in your base class .clone, to rip off java:


[code]SBase::clone(SBase &dest)
{
dest.i = i;
dest.c = c;
}

SDerived::clone(SDerived &dest)
{
super(dest);
dest.d = d;
}[/code]

Then you can cast stuff and make copies and all the fun stuff. Creating a clone contructor is also a good approach:
SBase a = new SBase(oldSBase);

Those are both better ways, at least in my opinion.
February 19, 2004, 6:06 PM
Skywing
I think super is a VC extension, FWIW.
February 19, 2004, 6:20 PM
Kp
Alternately, you could implement operator= to let the classes intelligently copy data among themselves. For a generalized (but less automatically maintained) solution to the issue Skywing points out, replace super (dest) in SDerived::clone with SBase::clone(dest). It's less automatic since it will break or behave oddly if SBase ever stops being the direct parent of SDerived (even if the stop is because SBase got renamed).
February 19, 2004, 6:59 PM
Arta
Wouldn't a clone function or overloaded operator be rather slower than copying the structure? Although I understand your hesitation RE mixing C/C++, I guess my real question is how would that inheritence be implemented by the compiler? I'm not keen on adding a clone function or overloaded operator, since changing the structure would then require adding a statement to copy the new variable.
February 19, 2004, 8:20 PM
iago
[quote author=Skywing link=board=23;threadid=5363;start=0#msg45053 date=1077214809]
I think super is a VC extension, FWIW.
[/quote]

I was thinking in Java again, my apologies.

Arta - Are you trying to make something that needs ultra speed? I can't see there being much of a speed difference between assigning the variable manually and a memcpy.

The best way to find out is to try the memcpy, and see if it works. But be warned - different compilers may act differently for something like this.
February 19, 2004, 8:35 PM
Kp
If you're really determined to use memcpy, you might be able to defend against inheritance weirdness by having an inline member in the SDerived as follows:

SBase *base_part() { return this; }

Afaik, the compiler will automatically adjust such an invocation to ensure that the returned pointer really does point at the SBase component. Also, barring multiple inheritance, the compilers I've used always front-align the parent class so that the inlined function I gave doesn't need to add anything to the pointer. However, as iago said, there's no guarantee that all compilers will behave the way I observed.

Alternately, you could use static_cast<SBase*>(pSDerived). I've been using a fair amount of multiple inheritance in some of my code lately, and static_cast takes care of the type-checking + adjusts the pointer to be correct too! :)
February 19, 2004, 9:19 PM
K
[quote author=Kp link=board=23;threadid=5363;start=0#msg45078 date=1077225566]
Alternately, you could use static_cast<SBase*>(pSDerived). I've been using a fair amount of multiple inheritance in some of my code lately, and static_cast takes care of the type-checking + adjusts the pointer to be correct too! :)
[/quote]

Threadjack: I was under the impression that for conversions between base and derived pointer types that you should use reinterpret_cast?
February 19, 2004, 10:35 PM
Skywing
[quote author=K link=board=23;threadid=5363;start=0#msg45087 date=1077230118]
[quote author=Kp link=board=23;threadid=5363;start=0#msg45078 date=1077225566]
Alternately, you could use static_cast<SBase*>(pSDerived). I've been using a fair amount of multiple inheritance in some of my code lately, and static_cast takes care of the type-checking + adjusts the pointer to be correct too! :)
[/quote]

Threadjack: I was under the impression that for conversions between base and derived pointer types that you should use reinterpret_cast?
[/quote]
I think you only need to use reinterpret_cast for "strange" casts, like converting an int into a something*.
February 19, 2004, 10:56 PM
Myndfyr
[quote author=Arta[vL] link=board=23;threadid=5363;start=0#msg45013 date=1077181496]
Is this safe?

[code]

struct SBase
{
   int i;
   char c;
};

struct SDerived: public SBase
{
   double d;
};

SBase Base;
SDerived Derived;

memcpy(&Base, (SBase*)&Derived, sizeof(SBase));

[/code]

The idea being that Base now contains the values i and c from Derived. This seems to work, but I wondered if there might be any complications I should be aware of before I go code things based on the idea.
[/quote]

My only contribution to this is that it seems to be poor OOP programming practice. memcpy completely tears away any kind of privacy or internal order of the data. Using memcpy would be considerably faster for a base strucuture that is extremely large, but a two-item copy would probably not give you such a performance drain, even if you did many of them.

For code clarity and a clear OOP conscience, I would recommend rather using a clone member method as (I believe) iago suggested.
February 20, 2004, 2:12 AM
Adron
[quote author=Arta[vL] link=board=23;threadid=5363;start=0#msg45069 date=1077222007]
Wouldn't a clone function or overloaded operator be rather slower than copying the structure? Although I understand your hesitation RE mixing C/C++, I guess my real question is how would that inheritence be implemented by the compiler? I'm not keen on adding a clone function or overloaded operator, since changing the structure would then require adding a statement to copy the new variable.
[/quote]

You could use an inline clone function in the base, doing a memcpy if that's what it's supposed to do. Writing a memcpy to copy the base in the derived class is ugly.
February 20, 2004, 7:35 PM
Arta
hmmwell, according to one of my lecturers, this should always be safe. Private/public access isn't really an issue since everything is public anyway. Speed is indeed crucial, and, to be honest, I don't see it as being eww. I only ever use structs as 'dumb' types to store collections of data that have something in common, thus, I don't see any eww-ness with using memcpy to copy all the data. Besides which, this design fits so neatly into the system.

I think Kp's suggestion is a good one, but since my lecturer confirmed that the compiler will do that implicitly via the cast, it is perhaps overkill.
February 20, 2004, 8:57 PM
Adron
The eww thing comes when you keep using this method for a lot of classes, and eventually decide to add something to the base class which requires a deep copy. Then you have to go around looking for memcpy's and replacing them.

Writing an inline clone/copyconstruct/something function which calls memcpy in the base class will give you the same speed, with a nicer design.

I'm not saying that an appropriate memcpy is eww; what I'm saying is that having a derived class memcpy a base class that should be implemented and reimplementable in a separate file is eww. All that needs to happen for your scheme to be invalid is that you add a std::string or similar to your base class, and then you have to go around to all classes that use your base class and modify the code there, big eww.
February 20, 2004, 9:15 PM
Arta
I see what you mean. I think in this case, though, that would never be an issue.

The idea here is to separate two sets of information, but to keep them related. The data (regardless of if it's in the derived or base structure) is information about the state of a connected client. Some of this information must be sent, via TCP/IP, to another machine. That's what I've put in the base structure. The stuff in the derived structure is only ever needed by the machine to which the user is connected, and therefore need not be transmitted. The idea of derived that structure is to allow the server access to both sets of information through one pointer, whilst still allowing other parts of the server to isolate the two structures from each other as needed.

I'd never call memcpy on a class, that would totally suck, but since this will never contain functions, and will always just be a 'dumb' type, it seems ok to me - an exception to the rule. Otherwise I would totally agree with you.
February 21, 2004, 9:55 AM
Adron
What is the reason for deriving the class instead of just declaring an instance of the base class inside the other class?
February 21, 2004, 10:32 AM
Arta
I didn't want to have to go through another pointer - this has to remain compatible with existing code.
February 21, 2004, 11:32 AM
Kp
[quote author=Arta[vL] link=board=23;threadid=5363;start=15#msg45325 date=1077363149]
I didn't want to have to go through another pointer - this has to remain compatible with existing code.
[/quote]

[code]
struct User {
char name[16]; /* user's name, known to everyone */
};
struct LocalUser {
User blah; /* interserver data */
unsigned local_data_item1; /* add more as needed */
};[/code]

Thus, no pointers, and it does as Adron suggested.
February 21, 2004, 5:49 PM
Arta
With that, you'd have to do:

[code]
Local->User.name
[/code]

to get to name. I want to be able to do

[code]
Local->name
[/code]

in order to remain compatible with existing code.
February 21, 2004, 6:56 PM
iago
[quote author=Arta[vL] link=board=23;threadid=5363;start=15#msg45357 date=1077389817]
With that, you'd have to do:

[code]
Local->User.name
[/code]

to get to name. I want to be able to do

[code]
Local->name
[/code]

in order to remain compatible with existing code.
[/quote]

I'd imagine you could override operator-> if you wanted to. Although I've never tried overriding that one :)
February 21, 2004, 7:09 PM
Eibro
Icky icky, but it'd probably work
[code]struct User {
char name[16]; /* user's name, known to everyone */
operator char*() { return name; }
};[/code]
February 21, 2004, 8:07 PM
Kp
[quote author=Arta[vL] link=board=23;threadid=5363;start=15#msg45357 date=1077389817]With that, you'd have to do:

[code]
Local->User.name
[/code]

to get to name. I want to be able to do

[code]
Local->name
[/code]

in order to remain compatible with existing code.[/quote]

Yes, it would require a change. However, unless you use really eww variable names, it ought to be pretty easy to come up with substitution patterns that can do all the renaming for you. For instance, s/name/User.name/g. :)
February 22, 2004, 12:46 AM
Adron
[quote author=Kp link=board=23;threadid=5363;start=15#msg45408 date=1077410787]
Yes, it would require a change. However, unless you use really eww variable names, it ought to be pretty easy to come up with substitution patterns that can do all the renaming for you. For instance, s/name/User.name/g. :)
[/quote]

Or simply #define servinfo_name User.user_name a la s_addr.
February 22, 2004, 11:40 AM

Search