Boost serialization - input stream error

I am trying to load serialized content and I have been unable to solve the issue. I have boiled it down to a specific line of code. I tried to minimize the code as best as possible but also show some debugging. The class `loadAudi` is only there to show exactly where the mistake is. The line `ar& audi` in `loadAudi` is what is causing the error. Here is the code (complete output is at the bottom):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <armadillo>

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/void_cast_fwd.hpp>
#include <boost/serialization/binary_object.hpp>

#include <iostream>
#include <fstream>
#include <memory>
#include <sstream>
#include <vector>

class Engine
{
  public:
    Engine () {}
    Engine (const int cyl) : _cyl ( cyl ) {}
    int getCyl () const { return _cyl; }

  private:
    int _cyl;

    friend class boost::serialization::access;
    template <class Archive>
    void serialize(Archive& ar, const unsigned int version) {
      ar & _cyl;
    }
};

class Car {
public:
    virtual char const* type() const = 0;
    virtual void printMember () const = 0;
    virtual void save (const std::string file) const { std::cout << "file" << std::endl; };

    virtual ~Car() = default;

private:
    friend class boost::serialization::access;
    template <class Archive>
    void serialize(Archive& ar, const unsigned int version) {};
};

class Audi : public Car, public std::enable_shared_from_this<Car> {
public:

  Audi() {};
  Audi(const std::string owner, const int hp,const unsigned int cyl)
    : _owner ( owner ),
      _hp    ( hp ),
      _eng          ( std::make_shared<Engine>(cyl) )
  { }

  char const* type() const override { return "Audi"; }

  void printMember () const override
  {
    std::cout
      << this->type() << ":\n"
      << "owner: " << _owner
      << " hp: " << _hp
      << " engine: " << _eng->getCyl()
      << std::endl;
  }
  void save (const std::string file) const {
    std::ofstream of(file, std::ofstream::binary);
    std::stringstream strs;
    boost::archive::binary_oarchive ar(of);
    std::shared_ptr<const Car> audi = shared_from_this();
    boost::serialization::make_binary_object(&audi, sizeof(audi));
  }

private:
  std::string  _owner;
  int          _hp;
  std::shared_ptr<Engine> _eng;

  friend class boost::serialization::access;
  template <class Archive>
  void serialize(Archive& ar, const unsigned int version) {
      ar & boost::serialization::base_object<Car>(*this);
      ar & _owner;
      ar & _hp;
      ar & _eng;
  }
};

Audi& loadAudi (const std::string file_name) {
  std::cout << "Loading " << file_name << std::endl;
  std::ifstream in_f(file_name, std::ifstream::binary);

  std::cout << "1" << std::endl;
  std::shared_ptr<Car> audi; //= std::make_unique<Audi>();

  std::cout << "2" << std::endl;
  boost::archive::binary_iarchive ar(in_f);

  std::cout << "3" << std::endl;
  ar& audi;

  std::cout << "4" << std::endl;
  return dynamic_cast<Audi &>(*audi);
};

BOOST_CLASS_EXPORT(Audi);
BOOST_CLASS_EXPORT(Engine);
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Car); //Tell Boost that Car is abstract

int main() {
  std::string save_file = "test.dat";
  std::ofstream of(save_file, std::ofstream::binary);
  std::ifstream in_f(save_file, std::ifstream::binary);
  {
    std::shared_ptr<Car> audi = std::make_shared<Audi>("Wilma", 3, 16);
    audi->printMember();

    std::stringstream strs;
    boost::archive::binary_oarchive ar(of);
    ar& audi;

    // audi_save_dir = strs.str();
    std::cout << "Count of audi:" << audi.use_count() << std::endl;

    ar << boost::serialization::make_binary_object(&audi, sizeof(audi));
  }


   {
     std::shared_ptr<Car> audi; //= std::make_unique<Audi>();
     std::cout << "Deserialize: Count of audi:" << audi.use_count() << std::endl;

     //std::stringstream strs(f);
     boost::archive::binary_iarchive ar(in_f);
     ar& audi;
     Audi& d = dynamic_cast<Audi &>(*audi);

     std::cout << "Deserialize: Count of audi:" << audi.use_count() << std::endl;
     std::cout << "Print Audi:" << std::endl;
     d.printMember();
   }


  std::shared_ptr<Car> audi = std::make_shared<Audi>("Daniel", 200, 8);
  audi->printMember();

  std::string test_dat = "my-new-audi.dat";
  audi->save(test_dat);
  Audi& d =  loadAudi(test_dat);
  d.printMember();

  return 0;
}


And the output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
Audi:
owner: Wilma hp: 3 engine: 16
Count of audi:1
Deserialize: Count of audi:0
Deserialize: Count of audi:2
Print Audi:
Audi:
owner: Wilma hp: 3 engine: 16
Audi:
owner: Daniel hp: 200 engine: 8
Loading my-new-audi.dat
1
2
3
terminate called after throwing an instance of 'boost::archive::archive_exception'
  what():  input stream error
[2]    29888 abort      ./a.out


I would appreciate any help!
Last edited on
Oon line 100 you have local shared_ptr to audi. As soon as loadAudi(...) ends the shared_ptr is destroyed (because it is actually not shared) and hence the result is invalid.

Why not return the shared_ptr?
Hi coder777, thanks for the response. The `loadAudi()` class is just for debugging purposes i.e. to show that the issue comes exactly when `ar& audi` is called at line 141. When removing `loadAudi()` from the code, the results are the same but in this case the `ar& audi` at line 141 is the problem.
Hi coder777, I'm not seeing how that can work with shared_ptr. I tried it but I don't think it is valid. What exactly did you have in mind?
I got it to work. I needed to:

1. `Audi& loadAudi (const std::string file_name)` to `Audi loadAudi (const std::string file_name)`
2. Add `ar& audi` to `save()`
What I mean is this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
std::shared_ptr<Audi> loadAudi (const std::string file_name) {
  std::cout << "Loading " << file_name << std::endl;
  std::ifstream in_f(file_name, std::ifstream::binary);

  std::cout << "1" << std::endl;
  std::shared_ptr<Audi> audi;

  std::cout << "2" << std::endl;
  boost::archive::binary_iarchive ar(in_f);

  std::cout << "3" << std::endl;
  ar& audi;

  std::cout << "4" << std::endl;
  return audi;
};


The boost::archive: throws an exception when it cannot read the required object. The cause might be that it is not correctly written. Line 77 doen't look right.
You should use a try{...} catch block.

Avoid dynamic_cast unless you really need it. On line 142 you surley don't need i. Line 146 should be:

audi->printMember();

By the way: use_count() on line 129/137/144 is a member of shared_ptr. It tells how many times it is referenced. See:

http://www.cplusplus.com/reference/memory/shared_ptr/use_count/

What are you trying to do at this lines?
Thanks for the help. I have a few comments:

1. Yes I deleted all `make_binary_object` lines as those are not needed.

2. If I change the code like in your post with (std::shared_ptr<Audi> audi; loadAudi()`) , what do I have to put then for line 155 and 156?

3. `use_count` was just for debugging purposes. I will delete it when everything works.

4. Yes you are correct. I don't need line 142.

Thanks in advance!
If I change the code like in your post with (std::shared_ptr<Audi> audi; loadAudi()`) , what do I have to put then for line 155 and 156?
shared_ptr is an replacement for raw pointers. Hence you can use them similarly:
1
2
3
4
5
  std::shared_ptr<Audi> d =  loadAudi(test_dat);
  if(d) // Checks whether loading the car was successful 
    d->printMember();
  else
    std::cout << "loadAudi failed\n"; 


What are line 77/131 good for? Can't you just remove them?

3. `use_count` was just for debugging purposes. I will delete it when everything works.
They just don't provide much useful information, do they?
2. I tried that before and I get the same error as before. With the loadAudi() function changed to how you have it, I get:

1
2
3
4
5
6
7
Loading my-new-audi.dat
1
2
3
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
[1]    32062 abort      ./a.out


So the error here is at `ar& audi`. I also added `ar& audi` to `save`. If I but change the `loadAudi` function to:

Audi& loadAudi (const std::string file_name) to Audi loadAudi (const std::string file_name)

and add `ar& audi` to `save()` the code works. But I would like to know how your way would work.


1. Yes I just deleted them and everything compiled as usual. That was from testing things out.

Honestly i'm not sure what the problem is?

If I but change the `loadAudi` function to:
Of course. In the first case you are returning a reference to a local variable. See:

https://stackoverflow.com/questions/4643713/c-returning-reference-to-local-variable

The shared_ptr will not change that since it is as well a local (and not otherwise referenced) variable that is destroyed at the end of the function.

When you use loadAudi like this

Audi loadAudi (const std::string file_name)

you are returning a copy of the local variable which is okay. The shared_ptr has no effect here.

Since you don't use the special traits of shared_ptr (sharing the data) the question is: why do you use it in the first place?
Topic archived. No new replies allowed.