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