Saving C structures to file -- best practices

But true.

So what happens when the compiler changes the struct layout so the length changes? Exactly the same thing as if you had simply used the plain struct in the first place!

It's not that simply writing a bit copy of the struct to a file is a great idea, But your wrapper does not appear to improve on it at all. It just obfuscates what is going on and introduces a gratuitous extra layer when accessing it.

--

John Devereux
Reply to
John Devereux
Loading thread data ...

To which you reply with _three_ paragraphs of nothing constructive at all. Pott. Kettle. Black?

And what else, exactly did you expect to get for barging in three weeks after this discussion ran out, offering as a "solution" essentially the very same approach the OP described as the _problem_ he wanted a solution for:

?

No, it's not. It's not a single bit better than a perfectly straightforward

fwrite(&struct, sizeof(struct), 1, fp);

Actually, neither your union nor the direct binary fwrite achieves any of that. A file written like this by one platform will typically _not_ be readable by the same code running on another platform. Which makes it the worst practice, and the opposite of what the OP asked for.

Come to think of it: since you obviously didn't understand the OP's question, nor any of the replies he got, why did you answer?

Reply to
Hans-Bernhard Bröker

The same way your approach does: by the sizeof operator which exists for exactly that purpose.

So you basically agree that your own offering is a hack. Interesting you should get so wound up about defending a hack.

How exactly did you reach the conclusion that I considered this "the best" in any way, from me saying this about it:

In case you didn't get it: that "worst practice" I was talking about (and the OP, too) is direct binary copying of entire C data structures to the outside world. And the "worst possible to express" it is the union hack.

The reason the Union hack is even worse than direct memcpy or fwrite is that it directly invokes undefined behaviour. That's about the worst kind of mistake a C program can contain while still being compilable.

Reply to
Hans-Bernhard Bröker

Late again :-), but not much time for usenet recently. Because the association between the array and struct is defined, you can add or remove elements, change compiler and it still works. Discuss ?.

I find it usefull, as it formalises the association between the struct and the data to save. The compiler guarantees that the array is at least as large as the structure. If you do something like:

fwrite(&struct, sizeof(struct), 1, fp);

what you are actually saying is that a struct is an array, which is sloppy and makes the association loose at best. Formalising the association into a union makes the intent clear to the compiler and to anyone you has to maintain the code. It's a small point, but I use such techniques all the time because I know that it will always work and yes, a bit more overhead, but trivial in practice...

Regards,

Chris

Reply to
ChrisQ

No, both techniques (your one and the "plain struct" approach) fail in an identical way when changing the number of elements or when a new compiler decides to re-arrange them. The elements will be in different locations. If you read an old structure with the new program (after a firmware update say) it will be corrupted. Wrapping the struct in a union does absolutely nothing to correct this!

Not sure what you mean here. How would your approach be any different? The fwrite function appears to assume an array of elements (optionally with just one member as above). If you mean that the call above says that each element is *itself* an array, I disagree. The function signature is

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream)

So the "elements" are generic objects of length , not specifically arrays. Of course everything is an "array of bytes" in the end but note that the first parameter is *not* "unsigned char*" (nor "U8*" either).

I agree with the principle of making the intent clear - but I disagree that you are doing that here! :)

--

John Devereux
Reply to
John Devereux

It still works not a bit better than without the union. In particular, you still have to spell out the sizeof(variable) or sizeof(type) whenever you hand this data to a generic "save this data" function.

And no, it does most decidedly _not_ just "still work" when you change compilers. The data format created by this is every bit as platform-dependent as that of a plain fwrite(&struct,...). Because it's the same.

It formalizes an association between two things one of which serves no actual purpose at all. I doesn't gain you anything on the one association that actually needs to be established in this context: that between the struct itself and sizeof(struct). You'll end up writing

fwrite(&u.array, 1, sizeof(u.array), fp);

when you could have written

fwrite(&s, 1, sizeof(s), fp);

instead. And you really think _that_ is an improvement?

Reply to
Hans-Bernhard Bröker

If you need to exchange data between systems, or if file format must be constant, then it is a problem. The obvious solution is to encapsulate all format conversion ops into a separate module and limit access to the data structure(s) through function calls in that module. That simplifies the management of risk for future maintenance. That's how it would typically be done here and would also do it the long way round. ie: Do the conversion from struct element(s) to an array element by element, line by line. Then add plenty of commentary in the module and docs for future maintainers.

A good system design should always separate internal and external data formats via conversion modules, but how often is that done in practice ?. Typically, the internal struct is declared global, then peeked / poked from everywhere and we wonder why the resulting system is a nightmare to maintain and full of sleeping bugs.

Many embedded systems don't have the luxury of a clib style file system (ie: only flash or cmos ram) and the internal "file system" is designed / optimised to suit the app. Typically, the format needs no correlation between versions, so changes to the structure have no effect at all. Using a union to alias types involves some overhead, but it does make the implied conversion clear. I would use such techniques for any slightly dodgy conversion as it makes the intent clear to the reader and compiler. After good design, perhaps clarity should be the next most important thing. Good code should flow down the page, with no pauses to do mental gymnastics re the programmers intent :-).

One of the reasons to avoid the standard clib is that all the types are wrong and casts are nearly always required. Far better to develop your own libs over projects / years. I know half the world runs embedded linux now, but can't shake off the perhaps neanderthal view that unix is a better fit and unsurpassed on the desktop. Going against the grain I know, but...

Regards,

Chris

Reply to
ChrisQ

Perhaps not a good example. I would do:

vFormatSave (&struct);

With the structure defined in the associated module header file, hiding all the format conversion away from the functions that use and save it. ie: separate form and function. There would be an abstraction layer under that as well, to factor out any hardware specific stuff.

Many (most ?) self contained embedded systems don't need this consistency, but would still encapsulate the conversion and data structure access and would probably use the union struct / array as described.

It all depends on the application...

Regards,

Chris

Reply to
ChrisQ

Well, fine, but this has nothing to do with the "plain struct" vs "wrapping in a union" argument. Nobody here is holding either up as Gold Standard of programming style.

AFAIK the only "overhead" caused is in the mind of the poor programmer. The compiler ought to treat it the same as the plain struct.

I agree actually (about avoiding clib on microcontroller projects). It wasn't me that used fwrite() as an example, I had to look it up in the man page to find out what the parameters meant!

--

John Devereux
Reply to
John Devereux

And while doing so, if you're going to use that union hack you're so fond of, you'll end up having to copy your data into the union before you can use that union to --- copy the data to the actual output. How is copying twice better than copying once?

No, they wouldn't, because their creators know that it serves no purpose at all.

Reply to
Hans-Bernhard Bröker

Good. It took all of 7 weeks, but finally you understood what the problem stated by the OP was.

No, it doesn't. It just causes undefined behaviour, and doesn't offer any noticeable advantage to balance that flaw.

A person who needs a layer of syntactic overhead just to decipher an idiom as simple as a pointer to some data structure being passed to a generic data transport function should probably not be doing embedded C. It's gratuitous overhead like that that may very well make the difference between a project tightly fitting the limited resources of given hardware, and brutal failure.

Reply to
Hans-Bernhard Bröker

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.