How to do proper alignment in structs?

That sounds really nice. And the _pad[1] issue can easily be resolved with something like _pad?[1] or any number of alternatives.

I am confused about why I am not seeing the error as described.

I just tried to simulate this. I added 4-byte member to the ThemeSpace struct (DNA_userdef_types.h). Before doing so I get no errors at all. After doing so I am shown 113 errors, none that seems to point near my error:

But, as mentioned, it is precisely this behavior that I now recognize as “I must have misaligned something”. LOL

msvc only looks for errors that are formatted out in it’s own special syntax, the error is visible in your output window (view -> Output or Ctrl-W O) we could detect we’re building with msvc and tweak the output so it would be visible in this window as well.

also, bf_dna has clearly failed, the fact that it still happily tries to build bf_blenloader annoys me, but unsure how to fix that.

Ah… makes sense. I wouldn’t have thought to look at the Output window once getting so many errors.

we could do something like this

but it’s gonna require a little bit of restructuring of makesdna, since it be reaaaaaaal nice to have the file/line number information available.

3 Likes

Damn, that is beautiful…

Yeah, that’s nice. It’d be nice to be able to see the align errors in the Error List window instead of swim through Output

Wait, what the heck is this error supposed to mean ?

1>------ Build started: Project: bf_dna, Configuration: Debug x64 ------
2>------ Build started: Project: bf_collada, Configuration: Debug x64 ------
3>------ Build started: Project: buildinfo, Configuration: Debug x64 ------
4>------ Build started: Project: locales, Configuration: Debug x64 ------
3>Generating buildinfo.h_fake, buildinfo.h
1>Generating dna.c, dna_type_offsets.h, dna_verify.c
1>Align pointer error in struct (size_native 8): TreeFilterElement *prop_val_comparator_max
1>Align pointer error in struct (size_64 8): TreeFilterElement *prop_val_comparator_max
1>Sizeerror 8 in struct: TreeFilterElement (add 2 bytes)
1>Sizeerror 4 in struct: TreeFilterElement (add 2 bytes)
1>C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\VC\VCTargets\Microsoft.CppCommon.targets(209,5): error MSB6006: "cmd.exe" exited with code 1.
1>Done building project "bf_dna.vcxproj" -- FAILED.

My TreeFilterElement was already properly aligned, and I went ahead and tested commenting out char _pad1[6]; just to see what would happen. The error message seems to be saying that I only need to add 2 bytes to the struct. It neither gives the correct location nor the correct number of bytes to add. Of course, this is a “Align pointer error”.

Here’s my struct:

typedef struct TreeFilterElement {
  struct TreeFilterElement *next, *prev;
  ListBase *superlist, *sublist, *list;

  int apply_num_subtrees; 

  short struct_delete_switch;
  char struct_name_comparator[64]; 
  short prop_name_delete_switch;
  char prop_name_comparator[64]; 

  /* property value filter operation stuff */
  short prop_val_delete_switch;
  short fot;
  short op_min; 
  char _pad0[2];
  void *prop_val_comparator_min; 
  //char _pad1[6];    
  short op_max;
  void *prop_val_comparator_max;

  short elem_name_comparator_delete_switch;
  char elem_name_comparator[64];

  short proptype_delete_switch;
  short proptype_comparator;
  char _pad2[2];

} TreeFilterElement;

Align pointer error in struct (size_native 8): TreeFilterElement *prop_val_comparator_max

TreeFilterElement *prop_val_comparator_max needs to be aligned to 8 bytes, it’s currently not (but it’s not telling you how much to add, so you’ll either have to do the math, or just blindly try char _pad0[1];char _pad0[7]; one of them ough to do the trick.

1>Sizeerror 8 in struct: TreeFilterElement (add 2 bytes)
1>Sizeerror 4 in struct: TreeFilterElement (add 2 bytes)

The total size of the struct needs to be a multiple of both 4 and 8, add char _pad[2]; to the end of the structure. (but this is gonna change depending on the padding you add for the other field, so just fix one of them at a time.

1 Like

Well, that cleared that up.

Thanks

I explained it here as well, starting to wonder, are you just messing with me at this point?

What? No, your answer wasn’t for an “Align pointer error”. My last posted error and your answer was a little bit different than the other error and your answer.

I did not know that Sizeerror 8 ,Sizeerror 4 meant that the total size of the struct had to be a multiple of 8 and 4. Also, from my perspective, why would I easily understand that a lack of information in an error was simply just a lack of a developer bothering to write the error in makedna.exe ? Yeah, I would have eventually figured it out but it would be a lot better for a developer to just spill it plainly. Shoot, I would figure everything out eventually without asking questions.

Plus, I figure that this thread might as well be thorough for the next guy

Each property in an SDNA struct needs to be manually aligned based on its size. Alignment is based on sizes used by a 64bit system (8 byte pointers) which then also aligns to sizes of a 32 bit system.

This mostly relates to allowing each struct to be written to and read back from disk as one chunk, while maintaining cross platform compatibility on all supported plaforms. Note that while compilers have options to align struct members to suit the processor, blender insists on manual alignment so that the alignment always matches on every platform.

I’m sure there is (was?) a better explanation somewhere, but the following two wiki archive pages cover this issue.

https://archive.blender.org/wiki/index.php/Dev:Source/Architecture/SDNA_Notes/

https://archive.blender.org/wiki/index.php/Dev:Source/Data_Structures/DNAStructs/

@LazyDodo

I need help understanding this. I thought I had it figured out, until I changed a couple settings and hour later with tons of changes still get errors.

This works and I thought I knew why until I started changing things.

struct Object *object;
struct Collection *collection;

char operation;
char collection_operation;  
char solver;
char bm_flag;
char flags;  
char operand_display;  
short operand_type;     # Assuming up to this point it adds up to 8?

  
float double_threshold;     # Only 4 so we need to add 4?
char _pad1[4]; 

Doesn’t work

struct Object *object;
struct Collection *collection;

char operation;
char collection_operation;  
char solver;
char bm_flag; 
short operand_display;  
short operand_type;     # Should add up to 8?

  
float double_threshold;     # Only 4 so we need to add 4?
char _pad1[4]; 

still doesn’t work

struct Object *object;
struct Collection *collection;

char operation;
char collection_operation;  
char solver;
char bm_flag;
char _pad0[4];      # Only 4 so we need to add 4?
 
short operand_display;  
short operand_type;
char _pad1[4];     # Only 4 so we need to add 4?
  
float double_threshold;     # Only 4 so we need to add 4?
char _pad2[4]; 

this still doesn’t work

struct Object *object;
struct Collection *collection;

char operation;
char collection_operation;  
char solver;
char bm_flag;
char _pad0[4];      # Only 4 so we need to add 4?
 
short operand_display;  
short operand_type;  
float double_threshold;     # Should add up to 8?

Edit:
This works ,but don’t understand why. Is it because I put float at top, then short and then char?

struct Object *object;
struct Collection *collection;

float double_threshold;
short operand_display;  
short operand_type; 

char operation;
char collection_operation;  
char solver;
char bm_flag;

char _pad0[4];

Best to look at the build output, makesdna will tell you what it wants you to do.

makesdna isn’t communicating very well. Had char _pad0[1] said to add 1 byte and I had to go to all the way to char _pad0[7] before there was no error? That makes no sense.

Without a repro or something to look at there’s not much to do about this beyond going 'heh… weird"

Here’s a couple examples.

#1

  struct Object *object;

  struct Collection *collection;

  float double_threshold;   

  char operation;  

  char solver;

  char flag;

  char od_flag;    

  char bm_flag;

gives

Sizeerror 8 in struct: BooleanModifierData (add 1 bytes)
Sizeerror 4 in struct: BooleanModifierData (add 1 bytes)

#2

  struct Object *object;
  struct Collection *collection;

  float double_threshold;   

  char operation;  
  char solver;
  char flag;
  char od_flag;    
  char bm_flag;
       
  char _pad0[1];

or

  struct Object *object;
  struct Collection *collection;

  float double_threshold;   

  char operation;  
  char solver;
  char flag;
  char od_flag;    
  char bm_flag;
       
  char _pad0;

now wants more?

Sizeerror 8 in struct: BooleanModifierData (add 2 bytes)
Sizeerror 4 in struct: BooleanModifierData (add 2 bytes)

It does seem to me that those error messages aren’t very helpful, as you say. You need to pad out to 8 byte boundaries. You are there with the first 4 chars after the float. Then the next char forces a need for 7 more to get you back to an 8 byte boundary.
In this particular case, maybe there’s a way of combining the bits in flag and od_flag and thereby eliminate the need for any padding?

you’re not making it very easy to help you by stripping away all the context of what you’re doing.

here’s the structure that i have in master

typedef struct BooleanModifierData {
  ModifierData modifier;

  struct Object *object;
  char operation;
  char solver;
  char flag;
  char bm_flag;
  float double_threshold;
} BooleanModifierData;

You’re not supposed to re-order the fields, since it will as you have noticed give you alignment issues also ModifierData modifier; seems to be missing?

What is it that you are trying to accomplish?