Unable to deserialize boost::adjacency_list by boost v.1.55 which was serialized with boost v.1.48

361 Views Asked by At

After boost upgrade from v.1.48 to v.1.55 it become impossible to deserialize some of my custom classes (using boost::serialize). The culprit is a boost::adjacency_list class member.

In order to recreate the issue I tried to serialize (with boost v.1.48) and deserialize (with boost v.1.55) an empty boost::adjacency_list and it fails on deserialization with exception from my custom input stream class like "unexpected end of input stream".

I also tried to serialize an empty boost::adjacency_list with both v.1.48 and v.1.55 and they have different sizes: 26 and 31 bytes, respectively.

Does any one know if it's possible to read older data-files with new boost::serialization?

UPD: Tried to serialize adjacency_list into XML using 1.48 and deserialize it back using boost 1.55. Deserialization failed.

Simplified version of my custom adjacency_list:

struct vertex_data_t
{
    typedef boost::vertex_property_tag kind;
};
typedef int ObjectType;
typedef boost::property<vertex_data_t, ObjectType > Property;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, Property> AdjList;

Serialization code:

TEST(BoostSerializationTest148, Test01)
{
    AdjList adjList148;
    std::ofstream fOut("c:/temp/adjList148.xml");
    boost::archive::xml_oarchive(fOut) << BOOST_SERIALIZATION_NVP(adjList148);
}

Deserialization code:

TEST(BoostSerializationTest155, Test01)
{
    AdjList adjList155;
    std::ofstream fOut("c:/temp/adjList155.xml");
    boost::archive::xml_oarchive(fOut) << BOOST_SERIALIZATION_NVP(adjList155);

    AdjList adjList148;
    std::ifstream fIn("c:/temp/adjList148.xml");
    boost::archive::xml_iarchive(fIn) >> BOOST_SERIALIZATION_NVP(adjList148);  // archive_exception::input_stream_error exception
}

adjList148.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="9">
<adjList148 class_id="0" tracking_level="0" version="0">
    <V>0</V>
    <E>0</E>
</adjList148>
</boost_serialization>

adjList155.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="10">
<adjList155 class_id="0" tracking_level="0" version="0">
    <V>0</V>
    <E>0</E>
    <graph_property class_id="1" tracking_level="0" version="0"></graph_property>
</adjList155>
</boost_serialization>
1

There are 1 best solutions below

1
On BEST ANSWER

Yes, it should be possible. The problem can be two-fold: your code or Boost graph code. (Problem in Boost.Serialization is less likely).

First of all, reduce the problem -- exclude your custom stream.

Second, make the problem readable -- use XML archive.

So the first check would be as follows: can you serialize adjacency_list into XML using 1.48 and deserialize it back using boost 1.55?

Alternative form of this test: when you serialize adjacency_list into XML, results from 1.48 and 1.55 should be equivalent (except, maybe some "version" attributes). Is it the case?

If this test passes, then Boost Graph serialization is OK. Then the problem is likely to be in your custom data stream. Still, it would be easier to debug with XML archives.

Update:

Indeed when I compare the function load(...) from two versions of boost/graph/adj_list_serialize.hpp, I see that 1.55 have extra statement

ar >> serialization::make_nvp("graph_property", 
                    get_property(graph, graph_all_t()) );

I suggest you do somewhat of a workaround. First, you state that your class has a "version"; second, when loading, you have to choose 1.48 vs. 1.55 code depending on the version.

The code can look like the following:

#include <boost/graph/adj_list_serialize.hpp>
#include <boost/serialization/serialization.hpp>

struct vertex_data_t
{
    typedef boost::vertex_property_tag kind;
};
typedef int ObjectType;
typedef boost::property<vertex_data_t, ObjectType > Property;
typedef boost::adjacency_list<boost::vecS, boost::vecS, 
                 boost::bidirectionalS, Property> AdjList;

BOOST_CLASS_VERSION(AdjList, 1); //default is 0, corresponds to files 
                                 //serialized with Boost 1.48

namespace boost {

namespace serialization {

template<class Archive>
inline void load(
    Archive & ar,
    AdjList &graph,
    const unsigned int file_version
){
  typedef AdjList Graph;
  typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
  typedef typename graph_traits<Graph>::edge_descriptor Edge;

  unsigned int V;
  ar >> BOOST_SERIALIZATION_NVP(V);
  unsigned int E;
  ar >> BOOST_SERIALIZATION_NVP(E);

  std::vector<Vertex> verts(V);
  int i = 0;
  while(V-- > 0){
    Vertex v = add_vertex(graph);
    verts[i++] = v;
    ar >> serialization::make_nvp("vertex_property", 
                                  get(vertex_all_t(), graph, v) );
  }
  while(E-- > 0){
    int u; int v;
    ar >> BOOST_SERIALIZATION_NVP(u);
    ar >> BOOST_SERIALIZATION_NVP(v);
    Edge e; bool inserted;
    boost::tie(e,inserted) = add_edge(verts[u], verts[v], graph);
    ar >> serialization::make_nvp("edge_property", 
                                  get(edge_all_t(), graph, e) );
  }
  if (file_version>=1)
      ar >> serialization::make_nvp("graph_property", 
                                     get_property(graph, graph_all_t()) );
}

}
} //namespace boost

When loading old files this code will not load "graph_property" (the same way as 1.48); when loading the newly created archives it will work the way 1.55 behaves.