Opened 13 years ago

Closed 13 years ago

#4044 closed Bugs (invalid)

reset_object_address with library-created objects

Reported by: Richard Hazlewood Owned by: Robert Ramey
Milestone: Boost 1.43.0 Component: serialization
Version: Boost 1.42.0 Severity: Problem
Keywords: reset_object_address heap allocated Cc:

Description

The archive reset_object_address method does not work for objects created by the serialization library.

This appears to be the issue as discussed here: http://lists.boost.org/boost-users/2008/11/42417.php It looks like the issue was never addressed.

The code below demonstrates. Is this considered to be the desired behaviour?

I would've thought the object tracking should be applicable, regardless of how the object is constructed.

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <sstream>

struct Ob
{
    Ob() : m_(1) {}

    int m_;

    template <typename A>
    void serialize(A &a, const unsigned)
    {
        a & m_;
    }
};

int main(int, char *[])
{
    std::string serText;
    {
        std::ostringstream str;
        boost::archive::text_oarchive oa(str);
        Ob *p = new Ob;
        oa & p & p; // Serialize two instances; serialization lib will track second
        delete p;
        serText = str.str();
    }

    {
        std::istringstream str(serText);
        boost::archive::text_iarchive ia(str);
        Ob *p = 0;
        ia & p;
        Ob *newp = new Ob(*p);
        ia.reset_object_address(newp, p); // newp should now be used for future p uses
        delete p; p = 0; // Let's explicitly get shot of this
        ia & p;
        assert(p == newp); // Traps
        delete newp;
    }

    return 0;
}

Change History (7)

comment:1 by Robert Ramey, 13 years ago

It's very hard to discern what you're actually trying to do here. But since, I've come this far I'll ask what you think of the following example - derived from yours.

int main(int, char *[])
{
    std::string serText;
    {
        std::ostringstream str;
        boost::archive::text_oarchive oa(str);
        Ob *p = new Ob;
        Ob *p2 = p;
        oa & p & p2; // Serialize two instances; serialization lib will track second
        delete p;
        serText = str.str();
    }

    {
        std::istringstream str(serText);
        boost::archive::text_iarchive ia(str);
        Ob *p = 0;
        Ob *p2 = 0;
        ia & p & p2;
        delete p;
    }

    return 0;
}

Robert Ramey

comment:2 by Richard Hazlewood, 13 years ago

Okay, I was trying to generalize/test-case. Here's something closer to what I'm really doing (I'm away from the real code, so this is from memory ;-) ):

class Whatever
{
  typedef boost::function<Signature> Func;

  Func m_func;

  ///

  template <typename A>
  void serialize(A &a, const unsigned)
  {
     if (A::is_saving::value)
     {
       FunctionObject *p = m_func.target<FunctionObject>();
       // Let's serialize the function object via pointer
       a & p;
     }
     else
     {
       FunctionObject *p = 0;
       a & p;
       m_func = *p;  // Hand function object to boost::function
       FunctionObject *realp = m_func.target<FunctionObject>();
       a.reset_object_address(realp, p);   // Does not work
       delete p;  // Hence this will ultimately mess things up
                  // (subsequent serialization may land in same address)
     }
  }
};

Also, I can't do:

    {
       a & *m_func.target<FunctionObject>();
    }
    else
    {
       m_func = FunctionObject(); // Initialize boost::function with empty object
       FunctionObject *realp = m_func.target<FunctionObject>();
       a & *realp;  // Deserialize right in
     }

Because the FunctionObject has a private default ctor. This is why I was trying to route it through the serialization library's pointer creation (FunctionObject has access friendship).

Further, this is a simplification. In the real code FunctionObject is a template argument, but it is serializable.

Hope this is clearer. Best...

comment:3 by Robert Ramey, 13 years ago

I'm still not getting it. Doesn't this work?

template<class Archive>
void serialize(Archive &ar, boost::function<Signature> & f, const unsigned int version){
  // ar & any member variables here.
}
class Whatever
{
  typedef boost::function<Signature> Func;

  Func m_func;

  ///

  template <typename A>
  void serialize(A &a, const unsigned)
  {
     a & m_func;
  }
};

reset_object_address has only turned out to be necessary in the most rarest of cases. If you think you really need it, you might be missing something simpler.

Robert Ramey

comment:4 by anonymous, 13 years ago

Well, it would work if you can tell me what goes in here:

template<class Archive>
void serialize(Archive &ar, boost::function<Signature> & f, const unsigned int version){
  // ** here **
}

This just moves the problem to another function. It is the actual serialization of the functor, being held by boost::function, that I'm trying to achieve.

comment:5 by Robert Ramey, 13 years ago

What I think you want is a variable functor. This has nothing to do with seriaization but I'll address it anyway.

a) Method one.

1) Derive all functors from a common base class. 2) Make the base class virtual by adding one virtual function 3) Good idea to make it abstract as well. 4) maybe you want to make operator()() virtual as well. 4) serialize as one would any base class pointer - using EXPORT or register_type

b) Method two.

1) Use a boost::variant which can hold any one of all the types of functors you want

to use.

2) serialize an instance of the variant. This is already in the library.

The crux of the issue is that you really want to to serialize a type rather than a piece of data. Either of the above two methods will do the job.

This is the best I can guess as to what you want to do give the information I have.

Robert Ramey

all the functor derived from a common base class

comment:6 by Richard Hazlewood, 13 years ago

I've certainly considered method-1, as you outline above. Method-2 is an alternative I had not considered. However, both would require large resstructuring of the code-base (which may be inevitable).

The boost::function approach has a convenience (and elegance, IMO) for the application I am working on.

It is only access to the default ctor of the function object that is getting in the way. For example, I could copy the behaviour of the serialization library's auto_ptr_with_deleter just to get to serialization::access::construct<> friend:

template<class Archive>
void serialize(Archive &ar, boost::function<Signature> & f, const unsigned int version){
  if (Archive::is_saving:value)
  {
    ar & *f.target<MyFunctor>();
  }
  else
  {
    // get memory space for functor
    auto_ptr_thing<MyFunctor> p = (MyFunctor*)new char[sizeof(MyFunctor)];
    // in-place construct through the friend
    serialization::access::construct<MyFunctor>(&p);
    // de-serialize
    ar & *p;
    // hand over (this does incur a copy)
    f = *p.get();
    // let p destruct, taking the memory with it
  }
}

This avoids the object tracking. I was just trying to avoid duplicating this inner-library behaviour by routing through its pointer serialization (and it would be cleaner), but, the point of this trac, as soon as I do that I have to worry about tracking - and reset_object_address did not do what I expected.

Ultimately, if the current library behaviour of reset_object_address is considered correct (or not appliable to this type of heap allocated object), then I will consider the alternatives.

Thank you for taking the time to discuss this.

comment:7 by Robert Ramey, 13 years ago

Resolution: invalid
Status: newclosed

What you're trying to do is make a variable functor. This is not a serialization issue. There are several ways to do it - none of which has any problem with serialization.

So, I've done what I can. I'm going to mark this ticket "wont fix". What I really mean is "Can't fix" but the dropdown list doesn't give me that option.

Sorry I couldn't be more help.

Robert Ramey

Note: See TracTickets for help on using tickets.