0

In my project I have a class containing and std::list and in another class I maintain an iterator pointing to a place in the middle of that list.

I can successfully serialize the list, but the iterator member variable is causing problems. Here is a program to reproduce:

#include <boost/serialization/list.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <iostream>
#include <fstream>

class A
{
public:
  A(){}
  A(const std::list<int> & _i) : i(_i) {}
  virtual ~A(){}

  std::list<int> i;

  void display() {
    std::cout << "i:";
    for (const int j : i)
      std::cout << " " << j;
    std::cout << std::endl;
  }

private:
  friend class boost::serialization::access;
  //friend std::ostream & operator<<(std::ostream &os, const A &a);
  template<class Archive>
  void serialize(Archive &ar, const unsigned int version)
  {
    ar & i;
  }
};

class Stepper
{
public:
  Stepper() {}
  Stepper(const A& a)
    : p(a.i.size()>0 ? a.i.begin() : a.i.end()) {}

  std::list<int>::const_iterator p;

  void display() {
    std::cout << "p: " << *p << std::endl;
  }

  void step() { p++; }

private:
  friend class boost::serialization::access;
  //friend std::ostream & operator<<(std::ostream &os, const A &a);
  template<class Archive>
  void serialize(Archive &ar, const unsigned int version)
  {
    ar & p;
  }
};

int main()
{
  {
    A a({5,6,7});
    Stepper sa(a);
    a.display();
    sa.display();
    sa.step();
    sa.display();

    std::ofstream ofs( "a.txt" );
    boost::archive::text_oarchive ar(ofs);
    ar & a;
    ar & sa;
  }

  A b;
  Stepper sb;
  {
    std::ifstream ifs( "a.txt" );
    boost::archive::text_iarchive ar(ifs);
    ar & b;
    ar & sb;
  }

  b.display();
  sb.display();

  return 0;
}

In this program, the class A can be serialized without problems. (Remove the ar&sa stuff..) But unfortunately when trying to serialize the class containing the iterator (the exact code above), I get the following compilation errors:

[..snip..]

testser.cpp:72:10:   required from here /usr/include/boost/serialization/access.hpp:116:11:
error: ‘struct std::_List_const_iterator<int>’ has no member named ‘serialize’
         t.serialize(ar, file_version);
         ~~^~~~~~~~~


[..snip..]

testser.cpp:81:10:   required from here /usr/include/boost/serialization/access.hpp:116:11:
error: ‘struct std::_List_const_iterator<int>’ has no member named ‘serialize’

So, it seems that boost/serialization/list.hpp does not support iterators. And yet, as far as I can tell, it's totally legitimate to keep an iterator to a list item somewhere, as they cannot be invalidated unless erased. Is there a way to serialize this iterator using boost? Do I need to write a custom function? Do I have to return a custom iterator from my std::list? (That sounds particularly ugly..)

Thanks for any insight.

Steve
  • 409
  • 1
  • 3
  • 10
  • 2
    The problem is the iterator is going to refer to the list that used to exist. Not the new on that was read from the file that could be in a different location. You could store an "offset" of what that node is in the list and then get a new iterator to the new list and increment that offset number of times. – NathanOliver Feb 22 '18 at 15:56
  • By analogy, how would you expect a *pointer* to serialise? – Caleth Feb 22 '18 at 17:07
  • Serializing usually used to save data between program is shut down and launched again. That means that all your objects should die. – Nikita Smirnov Feb 22 '18 at 17:14
  • Yes I agree but I want to restore a pointer/iterator to the same place in the list after the list is restored. – Steve Feb 22 '18 at 18:59
  • "By analogy, how would you expect a pointer to serialise?" Our code is already full of objects that have pointers to each other, they serialise just fine – Steve Feb 23 '18 at 04:18
  • "You could store an "offset" of what that node is in the list and then get a new iterator to the new list and increment that offset number of times." Perhaps, I'd like to see boost serialization code that can help me do this if that's the best way. – Steve Feb 23 '18 at 04:19
  • "pointers to each other, they serialise just fine" - yes and *that* was exactly the point @Caleth raised. Are you aware of how it is that "they serialize just fine"? That's a splendid question. – sehe Feb 23 '18 at 21:49
  • @sehe I'm sorry, I don't follow. Are you saying that iterators can, in fact, be serialized the same way as pointers? In that case, can someone answer my question better than I did? These comments are supposed to be for clarifying the question, but if you have an answer, such as, this is possible, or impossible, please do so in an Answer, where you have room to explain properly. – Steve Feb 27 '18 at 20:10

1 Answers1

0

Okay it seems the only way to do this is to split the serialization into save and load, and calculate the iterator's position in the list. This works as long as the iterator is valid. Unfortunately it means needing to add a pointer to the list to the structure, which I didn't want, but actually in my application I can access this so it is not a problem for me.

class Stepper                                                                            
{                                                                                        
public:                                                                                  
  Stepper() {}                                                                           
  Stepper(const A& _a)                                                                   
    : a(&_a), p(a->i.size()>0 ? a->i.begin() : a->i.end()) {}                            

  const A* a;                                                                            
  std::list<int>::const_iterator p;                                                      

  void display() {                                                                       
    std::cout << "p: " << *p << std::endl;                                               
  }                                                                                      

  void step() { p++; }                                                                   

private:                                                                                 
  friend class boost::serialization::access;                                             

  template<class Archive>                                                                
  void save(Archive &ar, const unsigned int version) const                               
  {                                                                                      
    int d = std::distance(a->i.begin(), p);                                              
    ar & a;                                                                              
    ar & d;                                                                              
  }                                                                                      

  template<class Archive>                                                                
  void load(Archive &ar, const unsigned int version)                                     
  {                                                                                      
    int d;                                                                               
    ar & a;                                                                              
    ar & d;                                                                              
    p = a->i.begin();                                                                    
    for (; d>0; --d)                                                                     
      p++;                                                                               
  }                                                                                      

  BOOST_SERIALIZATION_SPLIT_MEMBER()                                                     
};
Steve
  • 409
  • 1
  • 3
  • 10