PDA

View Full Version : Casting Structs


BeyondCloister
2006.11.29, 07:05 AM
I've been scratching my head over some third party code I've got and just need a bit of a reality check over it. It will probably all make sense once I have tried to explain it here.

This is not the exact code but it goes something like this:


typedef struct {
int offset;
int size;

int element_id; /*!< Element number (zero based) */
int stype; /*!< Structure type: (DGNST_*) */
int level; /*!< Element Level: 0-63 */
int type; /*!< Element type (DGNT_) */
int attr_bytes; /*!< Bytes of attribute data, usually zero. */
unsigned char *attr_data; /*!< Raw attribute data */

int raw_bytes; /*!< Bytes of raw data, usually zero. */
unsigned char *raw_data; /*!< All raw element data including header. */
} DGNElemCore;

typedef struct {
DGNElemCore core;

int num_vertices; /*!< Number of vertices in "vertices" */
DGNPoint vertices[2]; /*!< Array of two or more vertices */

} DGNElemMultiPoint;


What is making me think twice is the way the above structures are being used.


DGNElemCore* psElement = readelementfromfile();
DGNElemMultiPoint* psMultiPointElment = (DGNElemMultiPoint*)psElement;


Okay, so this gets a pointer to a DGNElemCore object and then gets a pointer to the object cast as a DGNElemMultiPoint object.

DGNElemMultiPoint contains a DGNElemCore with some extra stuff tagged on to the end.

Am I correct in thinking that if the original psElement is used then the extra stuff added at the end of DGNElemMultiPoint will be unknown by whatever uses it?

What is puzzling me is that it is this DGNElemCore pointer being passed into other functions which I would expect to need to know about the extra stuff.

Sorry if this does not make sense but if it all did I would not have needed to post in the first place.

ThemsAllTook
2006.11.29, 10:38 AM
From what you describe, it sounds like the code expects DGNElemCore everywhere, and determines its actual type at runtime using the stype field. This is a pattern I've seen quite a few times, just implemented in a slightly different way. The way I've usually seen it done/done it is to keep a void * in the base struct which points to the additional information, but this approach has the advantage of only having to allocate one block of memory for the whole thing.

OneSadCookie
2006.11.29, 03:48 PM
If Alex's interpretation is correct, this is the usual way to do inheritance in C. If you ever had the misfortune of programming for the Classic Mac OS's Toolbox, there was a similar situation where the first member of a window was a color graphics context, and the first member of a color graphics context was a b&w graphics context, so you could just cast WindowPtr to either CGrafPtr or GrafPtr and everything would continue to work. There were also plenty of functions upgraded from the B&W-only days that took a GrafPtr, but internally would check to see if it wasn't a CGrafPtr and cast it back.