views:

109

answers:

3

Is there a way to clone an existing struct with different member alignment in Visual C++?

Here is the background:

I use an 3rd-party library, which defines several structs. To fill up the structs, I pass the address of the struct instances to some functions. Unfortunately, the functions only returns unaligned buffer, so that data of some members are always wrong.

/Zp is out of choice, since it breaks the other parts of the program. I know #pragma pack modifies the alignment of the following defined struct, but I would rather avoid copying the structs into my code, for the definitions in the library might change in the future.

Sample code:

library.h:

struct am_aligned
{
  BYTE data1[10];
  ULONG data2;
};

struct untouched
{
  BYTE data1[9];
  int data2;
};

test.cpp:

#include "library.h"

// typedef alignment(1) struct am_aligned am_unaligned;

int APIENTRY wWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
  char buffer[20] = {};

  for (int i = 0; i < sizeof(unaligned_struct); i++)
  {
    buffer[i] = i;
  }

  am_aligned instance = *(am_aligned*) buffer;
  untouched useless;

  return 0;
}

am_unaligned is my custom declaration, and only effective in test.cpp. The commented line does not work of course. untouched should still has the default alignment.

instance.data2 is 0x0f0e0d0c, while 0x0d0c0b0a is desired.

Thanks for help!

A: 

You should use #pragma pack before including the headers of the library you use (and after, with push and pop). This way you won't have to define the structs by yourself.

#pragma pack(push, 1)
#include <libraryheader.h>
#pragma pack(pop)

// You can now use the struct defined in the library

Indeed, the library has been compiled using a given alignment. You cannot change this, it is already hard-coded in the binary compiled library. However, you have to instruct the compiler, when you compile your files, that the structs contained in the library, are to be defined and used with another alignment.

Didier Trosset
The header file contains hundreds of definitions. I only need to modify several of them.
Crend King
The different alignment will change the *binary* definition of the structs you want, but will it change the other structs? Or will it leave them unchanged?
Didier Trosset
I want all original structs from the library header file untouched, but clones specific structs to my implementation and only effective in my files.
Crend King
I just changed the question a bit. Hope is it clearer now. The `#pragma pack` approach is not acceptable. I use other default aligned structs defined in the library header in the same cpp file. The approach breaks the rest of the code, similar to /Zp.
Crend King
Enclosing the inclusion of the library header between pragma push/pop **is the only solution you have** if you don't want to redefine the structs. Note that this is very different from /Zp that will change the packing for all **your** definitions too.
Didier Trosset
Note that adding the pragma pack push/pop around `#include <library.h>` won't change the binary definition of untouched.
Didier Trosset
@Didier: there is something really surprising here. If the library was compiled using some (non standard) alignment, how does the library provider hope it will work if it does not include the above pragma in it's header ? Ie: having to do what you suggest is really surprising.
kriss
Library providers are often *just* programmers. They are humans. They don't think to **all** the details. And for them everything works as they compile all of their libraries, utilities, and test code with the same alignment.
Didier Trosset
@Didier: I hope they **always** are programmers, I guess libraries prepared by non programmers are likely to contain **more** errors ;-)What is suprising me here is that @Crend explained he has a problem with GetFontData and FreeType. I guess the problem is with the FreeType headers as packing problem in Windows standard librairies is very unlikely. If the problem is with freetype there is an easy option: recompile it with desired alignment. It is Open Source code.
kriss
A: 

Not sure, maybe __declspec(align( number )) struct{...} will help. More on MSDN

Alexander Malakhov
I tried this. Seems nothing is changed.
Crend King
A: 

I suspect the only problem is probably with the starting address of the buffer on stack that is not aligned.

Just providing an aligned buffer instead of a non aligned one should do the trick.

Several easy ways to do it:

  • malloc the buffer as mallocated address are ok for any alignment.
  • define your buffer as a buffer of long, it will be aligned (and copy using memcpy instead of for loop with cast to char *, it is faster anyway).
kriss
The problem is not with alignment with the stack. It is with alignment of the data2 member of struct am_aligned. This binary library uses no padding and data2 starts at offset 10, whereas the user program uses padding and data2 starts at offset 12.
Didier Trosset
@Didier: I understand well that, but there is obviously other things at hand. First the binary library clearly use some padding because otherwise with the above definition data2 would start at 9, not 10 in `untouched` struct. There seems to be some filtering of the real case by OP. Also the starting address of the buffer *must* be aligned to have any chance of the program above to work. If buffer start address is not aligned even if both original struct and untouched struct are of the same size it won't work.
kriss
Sorry. `untouched` is only an example in the sample code, not real case. My real work is actually use [GetFontData](http://msdn.microsoft.com/en-us/library/dd144885%28v=VS.85%29.aspx) to obtain font table. The table structure is already defined in FreeType header files. GetFontData returns raw byte array. Casting the byte array to a struct will fail due to the mentioned reason.I do not understand why the starting address of the buffer must be aligned.
Crend King
@Crend: I should check in stndard but I believe there is some unspecified behavior regarding casting a pointer to another pointer type with different alignment restriction, then copying the struct through assignment. Such assignment can be optimized if you suppose both source and destination memory have the same alignment constraints. Implementation I know of use either a memcpy or successive members individual assignents and here it should work anyway.
kriss
@Crend: I've found some reference material that should be clearer than I am: https://www.securecoding.cert.org/confluence/display/seccode/EXP36-C.+Do+not+convert+pointers+into+more+strictly+aligned+pointer+types
kriss