Boost logo

Boost :

Subject: Re: [boost] [Serialization] Bizarre bug
From: Robert Ramey (ramey_at_[hidden])
Date: 2009-08-10 14:06:10


Jarl Lindrud wrote:
> Robert Ramey <ramey <at> rrsd.com> writes:
>
>>> -----------------------------------------
>>>
>>> * If one program writes archives like this:
>>>
>>> ar & vec; // vec is a std::vector<char>
>>>
>>> , it is then impossible to reliably load that archive in another
>>> program, unless one is aware of the entire compile-time content of
>>> the first program.
>>
>> Nope. The only time a problem can occur if one does
>>
>> ar & vec; // vec is a std::vector<char>
>>
>> In the saving program and
>>
>> ar & p_vec; // vec is a * std::vector<char>
>>
>> in the loading progam.
>
> Robert, that's manifestly incorrect. I have appended 3 well-formed
> programs, A1, A2, and B, to this post. B can read archives from A1,
> but it *cannot* read archives from A2. If you don't believe me, then
> please run the programs yourself. The only difference between A1 and
> A2 is compile-time instantiation, of code that is never executed.

Here's my version of the three programs. This is the way I would
expect most users use the library.

A1:
struct X {
    vector<char> m_v;
    template<class Archive>
    save(Archive & ar, const unsigned int version) const {
        ar << m_v;
    }
    template<class Archive>
    load(Archive & ar, const unisgned int version){
        ar >> m_v;
    }
};

int main(){
    X x(...);
    std::ofstream os("file.txt")
    boost::archive::text_oarchive oa(os);
    oa << x;
}

A2:

// compiled by never invoked
void dummy(){
    vector<char> * pv = new vector<char>();
    std::ofstream os("file.txt")
    boost::archive::text_oarchive oa(os);
    oa << x;
}

int main(){
    X x(...);
    std::ofstream os("file.txt")
    boost::archive::text_oarchive oa(os);
    oa << x;
}

OK - A2 produces an archive that cannot be read.
I would argue that including the dummy just to
provoke this "bug" is just a pathological case and
basically constitutes an error - albiet a subtle one.

A much more interesting scenario in this vein is the following:

C: Sometime later, the definition of X is changed to

struct X {
    vector<char> * m_pv;
    template<class Archive>
    save(Archive & ar, const unsigned int version) const {
        ar << m_pv;
    }
    template<class Archive>
    load(Archive & ar, const unisgned int version){
        // uh - oh need to support old archives !
        if(version < 1){
            m_pv = new vector<char>()
            ar >> *m_pv
        }
        else
            ar >> m_pv;
    }
};

BOOST_CLASS_VERSION(X, 1)

This WOULD be a problem and would fail to read
old archives. I'm willing to consider this a legitimate
concern.

Still, it's a very infrequent cases.

So in short, I believe that the only way to have such
a problem is include dummy code which I don't
consider a huge problem.

> Do you consider there to be any errors in either A1, A2 or B?

A1 serializes vector<char> directly rather than as a member of
a class. Not illegal - be extremely unusual.
A2 includes dummy code solely to provoke this "bug". I can't
think of a situation where inclusion of such code would be
legitimate. And it's not easy to do either as in the majority of
cases one uses ar & rather than ar << in which cast, the
saving and loading are always in sync.

B - I didn't look at it - I presume its' correct.

>
> If you were to implement a generic IPC marshalling layer, you would
> end up with the kind of code that A1, A2, and B contain.

I'm not seeing that.

> IPC serialization is a bit different from file-based serialization, in
> that serialization and deserialization generally take place in
> different programs.

Agreed - but I don't think it's relevant. The relevant issue is that
serialization and deserialization use the same data schema - and this
must be true for the system to work at all.

> Another wrinkle in IPC serialization, is that one program may contain
> only serialization code for a type T (and not deserialization), while
> another program may contain only deserialization code for type T (and
> not serialization). If that makes the programs ill-formed in your
> view,

It doesn't

> then I guess one would have to conclude that B.Ser. is not an
> appropriate choice for IPC applications.

Hence, I would not draw such a conclusion.

The real issue is my choice of implementation_level "object_serializable"
in combination with "track_selectively" for a few specific types.

Did I make the wrong choice? Maybe - but this
is a very narrow issue related to these types
- collections of primitives. For these types serialization
implementation is not expected to change and
for which considerations of efficiency are greater
than usual.

In any case, this has got me thinking about how
to avoid this. As I write this, I'm think maybe the
best would be to emit a compile time warning when
ever anyone tries to serialize a pointer to a type
whose serialization traits are both "object_serializable"
and "track_selectively". I'll think about this.

Robert Ramey
>
> Regards,
> Jarl
>


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