Conversion Error with struct to DWORD [SOLVED]

Started by
5 comments, last by pDK 18 years, 4 months ago
Hey everyone, Simple problem. I made a struct:
struct WPOINT
{
	WORD x;
	WORD y;
};
And I am attempting to use it for an argument that calls for a DWORD:
DirectDraw.h
int EventSprite(refID SpriteID, DWORD EventType, DWORD EventArgument);

CWord.cpp
	WPOINT m_MouseCursorPoint;

	g_pDirectDraw->EventSprite(idCursorSprite,
				   0x0000 /* TODO - DrawEvents */,
				   (DWORD)m_MouseCursorPoint);
And I am getting this back as a compile ERROR:
error C2664: cannot convert parameter 3 from 'WPOINT' to 'DWORD'
I know there is a simple way to fix this issue. My struct (WPOINT) and DWORD should technically have the same memory footprint, right? [edited for sanity] [Edited by - pDK on December 19, 2005 1:32:17 AM]
Advertisement
In this specific case, it probably does have the same footprint, yes. But in general such an assumption is extremely dangerous because of padding between members.

There are several ways around this. One way is to provide an explicit conversion:
struct WPOINT{	WORD x;	WORD y;        operator DWORD() {            return (x << 16) | y; //for instance        }};

This is less likely to choke with more complicated types where padding is more likely to cause issues.

CM
Wonderful! I actualy just found another method just as you posted:
inline DWORD PtoDW(WPOINT p){	return *((DWORD*)&p);}


What are the advantages between the two? Does this run into the padding issue? (Also, what can I search to find more information about this padding issue; I am unfimilar with it).
Your method still has the problem of padding.

The basic idea behind padding is that processors like it when memory is aligned to specific values. For example, many processors today like values aligned to 32-bits. If you have a piece of data that spans one of these alignment boundries then the processor might issue an exception. The padding ensures that the fields of the struct are properly aligned.
Quote:Original post by pDK
Wonderful! I actualy just found another method just as you posted:
inline DWORD PtoDW(WPOINT p){	return *((DWORD*)&p);}


What are the advantages between the two? Does this run into the padding issue? (Also, what can I search to find more information about this padding issue; I am unfimilar with it).

In a nutshell, memory access if fastest if objects lie on certain bounaries. Because of this, the compiler is encouraged to align structures such that its members fall on those boundaries. So the following two objects may actually have different sizes:
struct one {   char a;   char b;   int c;}struct two {   char a;   int b;   char c;}

In VC++, sizeof(one) == 8 while sizeof(two) == 12.

Your second method looks at the address of your class, and pretends its pointing to a DWORD. If the class happens to be larger than a DWORD, then you'll lose all the information after the first four bytes. In your case, sizeof(WPOINT) is probably four anyhow, so that's no big deal. But if you tried to convert between the two classes above [or if the compiler decided to add some padding between your members], it wouldn't work anymore. They look like they should be the same size, but they aren't.

CM
Quote:Original post by pDK
Wonderful! I actualy just found another method just as you posted:
inline DWORD PtoDW(WPOINT p){	return *((DWORD*)&p);}


What are the advantages between the two? Does this run into the padding issue? (Also, what can I search to find more information about this padding issue; I am unfimilar with it).


For correctness, Conner Mc Cloud's recommendation is best.

With your solution there are two issues:

1) Padding because of alignment: Some machines (for instance Itanium 64 bit) require all data accesses to by aligned to their type boundary. For instance, if you have the following struct
struct X{    char A;    char B;    int32 C;};

The size of this struct might be two bytes more than what you expect. This is because all accesses to 32 bit integer C must be aligned to the 4 byte boundary. So the compiler usually ends up padding the space between B and C.

Notice that if the struct was
struct X{    int32 C;    char A;    char B;};


the size would be 6 bytes, as you expect.

The way to get around padding by the compiler is to tell the compiler to not pack is to use the pragma pack. For instance by using #pragma pack(1)

MSDN link on Data Alignment

2) Byte Order or Endianness: Some machines store lower order bits of types at lower memory locations. In your case if WORD is 16 bit, you might have a problem.. For instance if you store 0xDEAD in the WORD x and 0xCAFE in the word y and read back using the memory location of the struct into a DWORD you might end up with 0xCAFEDEAD instead of the 0xDEADCAFE which you expect.

Wikipedia link on Endianness

in C++,a struct is just another class, so you have to provide a conversion operator to do type conversions like (DWORD)WPOINT. That is the reason your compile failed initially.

[EDIT] beaten by 2 posts!
Thanks everyone.

So using Connor's method, we are pretty much forcing the thread to compact the struct and order it the way we want it to...

Makes sense.

This topic is closed to new replies.

Advertisement