Feedback to "A Version Therapy"- Murphy's Law, July
2001
return to Murphy's Law
Dear Mr. Murphy,
I enjoyed your ESP article on firmware versioning. I wrote a
module to read/write configuration data for a pressure gauge.
I wish your article had come out before then as I discovered a
lot of what you point out. Since I wrote this code I have been
thinking about this problem and wondering how others have successfully
solve it. Every solution I had to opportunity to review seemed
insufficient. I wish I had thought of writing an article on this
given the amount of time I've spent researching it.
I agree that storing C++ objects is not worth the effort. This
is what I originally set out to do. In the end, I simply created
accessors to read and write NVRAM.
I ran across another option when I was getting my cell phone
upgraded. It assumes that you are using a PC-like computer to
flash a firmware upgrade. Simply write a routine to upload the
configuration data to the PC prior to the upgrade then download
the data back. This has it's other potential problems like what
to do if the connection fails during upload and download.
Also, I feel I must comment on your approach to the layout changing
because of compiler flag changes and the like. I feel your suggestions
are too reactive. I believe a developer should be more proactive
in their design and implementation. I believe most developers
will use a structure to store the layout as you show. Would it
not be better to surround the structure definition with a '#pragma
pack' to ensure the size right from the beginning? This way there
will be no surprises later on - after much time spent debugging.
Even if the compiler's pack statement is not portable to the next
compiler or compiler version, the developer will find it at compile
time, not run time. What are your thoughts?
Thanks again,
Mark Clayton
NetPlane Systems Inc
Dedham MA
Niall Murphy's reply to Mark Clayton
Your comment about using the #pragma pack is well considered,
but you do not have any guarantee that 2 compilers will treat
that progma the same way. Some compilers simply ignore pragma's
that can not handle, so you have to watch for warnings. Compilers
may have debug flags that supercede the #pragma. The bottom line
is that if anything changes then you can not be certain that the
layout will not change. If you generate some assembler and check
it to be sure the layout has not changed, then you can happily
change compilers, flags etc., but do not assume that something
like this #pragma means that this problem goes away.
Andy Gryc makes some good points about needless complexity,
and I wholeheartedly agree with him on all counts.
Hi Niall,
I enjoyed your article article that appeared in the July 2001
ESP. It in, you asked if users have had experience with placing
C++ objects in persistent store. I've got some experience in this,
and I can't recommend it.
I worked with another consultant on a project a couple of years
back. He was an OO evangelist, whereas I'm more of a moderate.
We had constant "battles" about the correct way to approach things:
he would always recommend the "pure" way, where I would recommend
a simple one. One example was that although the staff that would
inherit the code once we were finished implementing was trained
in C (not C++) he would insist that the "proper" way to do something
in his part was to use templates. Where they certainly have their
place, I feel that templates (and other non-obvious features of
the language) can inappropriately limit the skill level of future
maintainers of the software.
Unfortunately, he was also a little green in embedded systems,
and designed a C++ class hierarchy that lived in persistent store
(NVRAM). Of course, even though he applied discipline in maintaining
the class *data*, the virtual table entries on the objects would
change after every new link, which would cause old objects to
crash once invoked. He didn't really know how the objects were
layed out in memory (i.e. vtable pointers), and so didn't account
for this eventuality. Without having to rewrite a large amount
of his code, I ended up fixing the problem with a real kludge.
The base objects kept an enum that identified the type of object,
and each class derivation had to register itself with it's enum
value and a static pointer to a "factory" that provided the correct
vtable entry. On startup, the object list was walked, and the
vtable pointer was reset to the correct value based on the value
of the enum.
In contrast, the code that I wrote had a structure much like
what you suggested: the NVRAM contained C structs, and a C++ wrapper
was used to access them. Since it was very simple, it worked pretty
much the first time, without encountering any fundamental design
flaws.
In my opinion, the complexity required to manage C++ objects
that required virtual methods in a persistent store was not worth
the incremental benefit. Furthermore, the understandability of
the code, especially given the skill level of the successors of
the code, was severly compromised. I have yet to see a compelling
case for putting C++ objects in persistent store, *except* when
those objects are non-deriveable (i.e. "final" classes, like Date,
Currency, etc.).
--Andy Gryc
|