Boost logo

Boost :

From: Robert Ramey (ramey_at_[hidden])
Date: 2005-06-27 11:19:43


Vladimir Prus wrote:

> This behaviour will make the case that the current assertion is meant to
> catch just work.

I believe

a) that it currently works exactly as Peter thinks it should
b) that problems still can and will occur
c) that the trap is useful in detecting a significant and worthwhile portion
of those problems
d) at a small cost in convenience.

To illustrate my case, lets take peter's interesting example.

class construct_from
{
    ...
};

void main(){
    ...
    Y y;
    ar << construct_from(y);
}

I never considered this specific example and I would consider uncommon but
it is a legitimate and plausable usage of the library. First lets assume
that the trap is commented out.

1) as written above, it fails to compile on a conforming compler. This is
because the << operator takes a reference as its argument. So we make a
slight change to make it pass.

void main(){
    ...
    Y y;
    construct_from x(y);
    ar << x;
}

2) this compiles and executes fine. No tracking is done because
construct_from has never been serialized through a pointer. Now some time
later, the next programmer(2) comes along and makes an enhancement. He
wants the archive to be sort of a log.

void main(){
    ...
    Y y;
    construct_from x(y);
    ar << x;
    ...
    x.f(); // change x in some way
   ...
    ar << x
}

Again no problem. He gets to copies in the archive, each one is different.
That is he gets exactly what he expects and is naturally delighted.

3) Now sometime later, a third programmer(3) sees construct_from and says -
oh cool, just what I need. He writes a function in a totally disjoint
module. (The project is so big, he doesn't even realize the existence of
the original usage) and writes something like:

class K {
    shared_ptr<construct_from> z;
    template<class Archive>
    void serialize(Archive & ar, const unsigned version){
        ar << z;
    }
};

He builds and runs the program and tests his functionality and it works
great and he's delighted.

4) Things continue smoothly as before and a month goes by before its
discovered that when loading the archives made in thelast month (reading the
log). Things don't work. The second log entry is always the same as the
first. After a very long and acrimonius email exchanges, its discovered
that programmer (3) accidently broke programmer(2)'s code because by
serializing via a pointer, the behavior in an unrelate piece of code is
changed. Bad enough, but worse yet the data wasn't being saved and cannot
not be recovered. People are really upset and disappointed with boost (at
least the serialization system).

Now suppose the trap is turned on. How are things different?

1) Right away, the program traps at

ar << x;

The programmer curses (another %^&*&*( hoop to jump through). If he's in a
hurry (and who isn't) and would prefer not to const_cast - because it looks
bad, He'll just make the following change an move on.

const construct_from x(y);
ar << x;

Things work fine and he moves on.

2) Now programer 2 wants to make his change - and again (^&%*^%) another
annoy const issue;

const construct_from x(y);
ar << x;
...
x.f(); // change x in some way ; compile error f() is not const
...
ar << x

He mildly annoyed now he tries the following:

a) He considers making f() a const - but presumable that shifts the const
error to somewhere else. And his doesn't want to fiddle with "his" code to
work around a quirk in the serializaition system

b) He removes the "const" from "const construct_from above - damn now he
gets the trap. If he looks at the comment code where the
BOOST_STATIC_ASSERT occurs, he'll do one of to things

i) This is just B.S. Its making my life needlessly difficult and flagging
code that is just fine. So I'll fix this with a const cast and fire off a
complaint to the list and mabe they will fix it.
ii)Oh, this trap is suggesting that the default serialization isn't really
what I want. Of course in this particular program it doesn't matter. But
then the code in the trap can't really evaluate code in other modules (which
might not even be written yet). OK, I'll at the following to my
construct_from.hpp to solve the problem.

BOOST_SERIALIZATION_TRACKING(construct_from, track_never)

Program compiles with no casts and no traps and executes just as expected
(as above)

3) Now programmer (3) comes along and make his change. The behavior of the
original (and distant module) remains unchanged because the construct_from
trait has been set to track_never so we always get copies and the log is
always what we expect. His program also works as expected even thought it
saves/loads multipe copies.

//Alert: Ironic humor follows

On the other hand, Now he gets another trap - trying to save an object of a
class marked "track_never" through a pointer. So he goes back to
construct_from.hpp and comments out the BOOST_SERIALIZATION_TRACKING that
was inserted Now the second trap is avoided, But damn - the first trap is
popping up again. After much acrimonious email the situation is resolved so
to no one's real satisfcation.

// End humor alert

This second trap doesn't currently exist. But as I writing the above made
me think about it, I now believe it should also be implemented. So contrary
to my original intent - the above isn't funny after all - its serious. Now
(without the second trap) what is going happen is the following.

4) Actually programmer (3) changes are not going t function as
shared_ptr<construct_from> is not have a single raw pointer shared amonst
the instances but rather multiple ones. Things won't work. Of course this
won't be obvious until a month from now and we're back in the same boat.

This is my best attempt to illustrate why the trap (now traps?) are
important. Not perfect but better than nothing.

Vladimir started this thread with the question of whether the inconvenience
created by the traps was a worthwhile trade off for any benefit. I agree
that this is the fundamental question and have done my best to address it
here.

I believe that this scenario illustrate at least one case where it would.
The inconvenience is small an and actually leads to a more correct program.
Note that I didn't contrive this example, its Peter's example - I just took
it to its logical conclusions.

Robert Ramey


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk