It's much better to write things in the way that lets you have the smallest amount of hand-written code. For example, if you're tempted to manually copy&paste&edit 20 copies of something -- totally evil! -- because you can't make that into a function or it's beyond the capabilities of macro processing (or your language doesn't have macros) *don't* *do* *that*
This is EXACTLY what I was saying above. Apparently others than brucehoult and golden_labels did not get it.
No, I strongly disagree with the others. If you have a nicely designed struct with 57 variables holding your state and you are constructing each of the 57 variables by writing 57 LoC each with various combinations of casting, bitshifting and masking, you are really doing it wrong. Yes, programming requires careful work, but repeated manual copy-paste-like (doesn't matter if you avoid the actual copy-pasting and rewrite manually) should still be avoided. Human brain is good in creative work, but does a lot of mistakes (say, several %) when repeating mechanical "simple" work. This is why automation exists.
Yes, you can solve the problem of being error-prone by rigorously always writing complete unit tests for such construction/deconstruction functions, but this is a lot of work just to test a mechanical piece which only exists due to lack of suitable language features. Also, unit testing does not easily reveal portability issues because arithmetic on C is non-portable (unless you Do It Right, but you can't
test against this just by simple unit tests. Maybe a good static analysis tool is available, I don't know, or you can automate running unit tests on gazillion compilers and platforms...)
If type-punning packed struct through union - a hack that exists on the borderline between the "standard C" and "real-world, constrained C" - is out of question and the arithmetic way is the only acceptable, there is really no other option than to auto-generate the structs and the parser from a common description language. This is an actually useful real-world solution I have nothing against. On microcontrollers and other well-definited restricted environments where endianness is fixed and all compilers used support packed attribute, I go the most writable, most readable, most maintainable, least risky way of using the structs directly, but I indeed do accept it isn't a generic solution either. Call it a "hack" if you wish, I don't care.
Finally, the whole Internet works based on C code sharing packed structs like this, and it works, given the programmer knows what they are doing, and what the limitations are. (For example, endianness conversion macros are used.)
And yes, I'm serious about "fail hard". The "hacky" way has approximately 2-4 possible problems (padding, alignment, endianness...) most of which are either trivially dealt with (just one correctly placed attribute needed!), or cause serious, easily noted error (instant bus fault). The arithmetic way seems easy on surface but has more possible traps, not limited to typing errors but also operator precedence, integer promotion rules, numerical ranges, implicit casting, all the implementation specified rules like signed integer overflow, and so on and so on. This is so risky that unit testing for
every input combination is highly recommendable.
I would never trust a programmer who claims they are so excellent they make zero mistakes in large sets of "simple" arithmetic operations. Take note, SiliconWizard. I would have expected better commentary from you with your excellent track record of your posts.
Actually why I do recommend the arithmetic version for beginners is to expose them to all the problems, it's highly likely they don't get it right the first time. I didn't, I hit different problems for years until finally nailing it, and after nailing it all that's left are simple accidents. Because arithmetics in C can't be avoided, better expose oneself to it.
Arithmetic version is not guaranteed to be portable either because in original C standard, type widths are implementation specified, and internal implicit conversions convert into these types, number literals are of varying widths... So you need to exactly do the right thing with explicit casting. So portability comes from careful work and understanding. Having seen some portability problems, I'd hazard a guess
most of the actual portability problems are related to type handling in arithmetic; NOT "low-level hacks". Latter are typically not everywhere but constrained somewhere, easily dealt with.
Sadly, I think there
is no generic solution.
What I don't like in this reoccurring discussion is the mental dishonesty of double stardards. If the programmer makes a mistake in the
direct struct usage pattern, it's a proof of the pattern being unusable and dangerous. But if the programmer makes a mistake with the arithmetic solution, it's just a problem of the programmer not being careful enough, and solution is to be more careful and not to make any mistakes.