1/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
2// demo_exception.cpp
3
4// (C) Copyright 2002-4 Robert Ramey - http://www.rrsd.com .
5// Use, modification and distribution is subject to the Boost Software
6// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8
9// Example of safe exception handling for pointer de-serialization
10//
11// This example was prepared by Robert Ramey to demonstrate and test
12// safe exception handling during the de-serialization of pointers in
13// a non-trivial example.
14//
15// Hopefully, this addresses exception issues raised by
16// Vahan Margaryan who spent considerable time and effort
17// in the analysis and testing of issues of exception safety
18// of the serialization library.
19
20#include <algorithm>
21#include <iostream>
22#include <cstddef> // NULL
23#include <fstream>
24#include <string>
25
26#include <cstdio> // remove
27#include <boost/config.hpp>
28#if defined(BOOST_NO_STDC_NAMESPACE)
29namespace std{
30 using ::remove;
31}
32#endif
33
34#include <boost/archive/tmpdir.hpp>
35
36#ifndef BOOST_NO_EXCEPTIONS
37#include <exception>
38#endif
39
40#include <boost/archive/text_iarchive.hpp>
41#include <boost/archive/text_oarchive.hpp>
42
43#include <boost/serialization/list.hpp>
44#include <boost/serialization/split_member.hpp>
45
46template<class TPTR>
47struct deleter
48{
49 void operator()(TPTR t){
50 delete t;
51 }
52};
53
54class Course;
55class Student;
56
57class Student
58{
59public:
60 static int count;
61 Student(){
62 count++;
63 }
64 ~Student(){
65 some_courses.clear();
66 count--;
67 }
68 std::list<Course *> some_courses;
69private:
70 friend class boost::serialization::access;
71 template<class Archive>
72 void serialize(Archive & ar, const unsigned int /* file_version */){
73 ar & some_courses;
74 }
75};
76
77int Student::count = 0;
78
79class Course
80{
81public:
82 static int count;
83 Course(){
84 count++;
85 }
86 ~Course(){
87 // doesnt delete pointers in list
88 // since it doesn't "own" them
89 some_students.clear();
90 count--;
91 }
92 std::list<Student *> some_students;
93private:
94 friend class boost::serialization::access;
95 template<class Archive>
96 void serialize(Archive & ar, const unsigned int /* file_version */){
97 ar & some_students;
98 }
99};
100
101int Course::count = 0;
102
103class School
104{
105public:
106 ~School(){
107 // must delete all the students because
108 // it "owns" them
109 std::for_each(first: all_students.begin(), last: all_students.end(), f: deleter<Student *>());
110 all_students.clear();
111 // as well as courses
112 std::for_each(first: all_courses.begin(), last: all_courses.end(), f: deleter<Course *>());
113 all_courses.clear();
114 }
115 std::list<Student *> all_students;
116 std::list<Course *> all_courses;
117private:
118 friend class boost::serialization::access;
119 BOOST_SERIALIZATION_SPLIT_MEMBER()
120 template<class Archive>
121 void save(Archive & ar, const unsigned int file_version) const;
122 template<class Archive>
123 void load(Archive & ar, const unsigned int file_version);
124};
125
126#if 0
127// case 1:
128template<class Archive>
129void School::serialize(Archive & ar, const unsigned int /* file_version */){
130 // if an exception occurs while loading courses
131 // the structure courses may have some courses each
132 // with students
133 ar & all_courses;
134 // while all_students will have no members.
135 ar & all_students; // create students that have no courses
136 // so ~School() will delete all members of courses
137 // but this will NOT delete any students - see above
138 // a memory leak will be the result.
139}
140
141// switching the order of serialization doesn't help in this case
142// case 2:
143template<class Archive>
144void School::serialize(Archive & ar, const unsigned int /* file_version */){
145 ar & all_students;
146 ar >> all_courses; // create any courses that have no students
147}
148#endif
149
150template<class Archive>
151void School::save(Archive & ar, const unsigned int /* file_version */) const {
152 ar << all_students;
153 ar << all_courses;
154}
155
156template<class Archive>
157void School::load(Archive & ar, const unsigned int /* file_version */){
158 // if an exception occurs while loading courses
159 // the structure courses may have some courses each
160 // with students
161 try{
162 // deserialization of a Course * will in general provoke the
163 // deserialization of Student * which are added to the list of
164 // students for a class. That is, this process will result
165 // in the copying of a pointer.
166 ar >> all_courses;
167 ar >> all_students; // create students that have no courses
168 }
169 catch(std::exception){
170 // eliminate any dangling references
171 all_courses.clear();
172 all_students.clear();
173 throw;
174 }
175}
176
177void init(School *school){
178 Student *bob = new Student();
179 Student *ted = new Student();
180 Student *carol = new Student();
181 Student *alice = new Student();
182
183 school->all_students.push_back(x: bob);
184 school->all_students.push_back(x: ted);
185 school->all_students.push_back(x: carol);
186 school->all_students.push_back(x: alice);
187
188 Course *math = new Course();
189 Course *history = new Course();
190 Course *literature = new Course();
191 Course *gym = new Course();
192
193 school->all_courses.push_back(x: math);
194 school->all_courses.push_back(x: history);
195 school->all_courses.push_back(x: literature);
196 school->all_courses.push_back(x: gym);
197
198 bob->some_courses.push_back(x: math);
199 math->some_students.push_back(x: bob);
200 bob->some_courses.push_back(x: literature);
201 literature->some_students.push_back(x: bob);
202
203 ted->some_courses.push_back(x: math);
204 math->some_students.push_back(x: ted);
205 ted->some_courses.push_back(x: history);
206 history->some_students.push_back(x: ted);
207
208 alice->some_courses.push_back(x: literature);
209 literature->some_students.push_back(x: alice);
210 alice->some_courses.push_back(x: history);
211 history->some_students.push_back(x: alice);
212
213 // no students signed up for gym
214 // carol has no courses
215}
216
217void save(const School * const school, const char *filename){
218 std::ofstream ofile(filename);
219 boost::archive::text_oarchive ar(ofile);
220 ar << school;
221}
222
223void load(School * & school, const char *filename){
224 std::ifstream ifile(filename);
225 boost::archive::text_iarchive ar(ifile);
226 try{
227 ar >> school;
228 }
229 catch(std::exception){
230 // eliminate dangling reference
231 school = NULL;
232 }
233}
234
235int main(int argc, char *argv[]){
236 std::string filename(boost::archive::tmpdir());
237 filename += "/demofile.txt";
238
239 School *school = new School();
240 std::cout << "1. student count = " << Student::count << std::endl;
241 std::cout << "2. class count = " << Course::count << std::endl;
242 init(school);
243 std::cout << "3. student count = " << Student::count << std::endl;
244 std::cout << "4. class count = " << Course::count << std::endl;
245 save(school, filename: filename.c_str());
246 delete school;
247 school = NULL;
248 std::cout << "5. student count = " << Student::count << std::endl;
249 std::cout << "6. class count = " << Course::count << std::endl;
250 load(school, filename: filename.c_str());
251 std::cout << "7. student count = " << Student::count << std::endl;
252 std::cout << "8. class count = " << Course::count << std::endl;
253 delete school;
254 std::cout << "9. student count = " << Student::count << std::endl;
255 std::cout << "10. class count = " << Course::count << std::endl;
256 std::remove(filename: filename.c_str());
257 return Student::count + Course::count;
258}
259

source code of boost/libs/serialization/example/demo_exception.cpp