When inspecting that structure in the debugger (what you characterized as a "canard") there would be 8 fields, instead of 4 and, the programmer would have to mentally weed out the invalid data. Much simpler and _cleaner_ to have the standalone definitions above and
But you see them categorized by each layout:
type
TTestA = record
A1: Integer;
A2: Double;
A3: Char;
end;
TTestB = record
B1: Int64;
B2: Single;
B3: Char;
end;
TTest = record
case Boolean of
True: (A: TTestA);
False: (B: TTestB);
end;
Hovering over it gives
t = record TTEST {
A = {
A1 = 0,
A2 = 0,
A3 = 0 #0},
B = {
B1 = 0,
B2 = 0,
B3 = 0 #0}}
So the programmer "mentally weed out the invalid data." means look at the subsection that is relevant. It is neatly ordered and makes it very easy to spot what you are looking for. But if thats to hard, simply hover over the respective member, like if I hover over t.A rather than over t:
t.A = record TTESTA {
A1 = 0,
A2 = 0,
A3 = 0 #0}
Oh look it's only the relevant information. The same information your absolute would give.
It gives you all the information in one place, and if you only need a certain subset you can easiely look at it by addressing it.
Btw, just as a side note, using a variant record for pointers to data is imho kinda pointless, you should instead of having a variant record with two different pointers, have a pointer to a variant record containing the data. This way you skip one indirection step
I use a debugger not just to debug code. I use it as a testing tool and, writing a program that is easy to debug, is _always_ one of the driving considerations in how I choose to write the code. I likely spend about 30% of the time I spend editing, testing code in the debugger.
I mean this is nice if it's possible, but not possible for all projects, for example, if you are on a project that takes long to compile, like 10 minutes (the delta compilation, not even the full project), you are going to think twice if you are starting a debugger session just for something small. Sure I must say that I had this quite infrequently with pascal, only when I was hacking around in the compiler/RTL it took ages to compile even for small changes, but if you have complicated buildsystems you think twice about any time you want to run it.
As far as OptionalHeader32 and OptionalHeader64 "not belonging together" as you put it, anyone who has basic knowledge of the PE format _knows_ they belong together and, while I have no problem hovering over a 3 character identifier, it is definitely much easier to place the cursor over OptionalHeader32/64 than it is to place it over "Hxx". On that note, I sometimes name indexes using 3 characters instead of just "i" because it is easier to put the cursor over them (and also because 3 characters is usually sufficient to indicate what the index is being used for - instead of a generic "i")
I don't know what kind of cursor aiming issues you have. I have absolutely no problem hovering over a 3 char identifier. Also I love your "anyone who has basic knowledge of the PE format _knows_ they belong together", you should not write code for people who are already familiar with everything, you should write code such that it is easy to understand ieven for someone having never worked with anything similar before. Those that already know a lot won't suffer from the additional information provided by the code, but those that do not know much about it will temendously be helped by it.
Clear naming or otherwise, if you join those two THUNK definitions into a variant, you'll end up with a soup of data and superfluous tags to make the matter even worse.
I hope you don't mean with "soup of data and superfluous tags" a clearly structured data serialization as the one shown above where each member of the variant record is neatly organized, because by soup I don't think about clearly structured and logically ordered as this clearly is.
Yes, they are Apples and Oranges because, it's not the fields they have in common that matters is the differences between versions. Mixing those fields that don't belong together into a variant makes it much more likely that a field that doesn't apply be used, not to mention that the fields they have in common may not be at the same offsets, which is a critical difference. Just because the field(s) has/have the same name, doesn't mean they belong there (because of the different offsets.) Mixing things like that is asking for trouble and making things complicated.
Thats why you give your fields meaningful names and a hierachical structure using the variant records. Let's take your example, the closest thing between two configurations I found was StaticLinks for Vista and VistaStaticLinks for Win7. Sure on the face value easy to mistake them, but if you write it out (I was so free to give it some meaningful names):
OptionalHeader.PE64Header.VersionSpecifics.Vista.StaticLinks
vs
OptionHeader.PE64Header.VersionSpecifics.Win7.VistaStaticLinks
It's very hard to confuse, you literally have the current version you are talking about written write there in the code. So you want to know which version you are currently looking at, easy, it is a PE64 for Windows 7. All this information right there in the code, no Debugger needed. You can't get confused there
Well structured code speaks for itself
Sounds like you're getting carried away on that one. It's simple, the worst case is: one definition per Windows version and only when the definitions differ. It is only in rare cases that there will be more than 4 or 5 definitions.
You stated that you already have 8 versions, and from my experience, if there is 8 of something, it is probably going to be 9 at some point and then 10 etc.
And about bugs, mixing fields that apply to one version with fields that only apply to a different version in the same structure (using variants of course) is literally asking for bugs. With separate definitions, the compiler will flag any attempt at using a field that does not apply to the version, that alone is reason enough to have separate definitions. It is irrelevant that 75% of the fields may be the same, what's relevant is that, in that case, there is 1 chance in 4, to use the _wrong_ field and, opening that door is simply bad design.
What are you talking about? The code is clear, let's say I'm handling a Windows Vista PE and have the following code:
OptionHeader.PE64Header.VersionSpecifics.Vista.VistaStaticLinks
The compiler will say to me that there is no VistaStaticLinks in Vista, because it is defined only for Win7. You get the exact same error checking, as you are encapsulating your data and the Vista fields are only accessible through the vista member, while the Win7 fields are clearly separated in their Win7 member.