• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
29 namespace 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 
46 template<class TPTR>
47 struct deleter
48 {
operator ()deleter49     void operator()(TPTR t){
50         delete t;
51     }
52 };
53 
54 class Course;
55 class Student;
56 
57 class Student
58 {
59 public:
60     static int count;
Student()61     Student(){
62         count++;
63     }
~Student()64     ~Student(){
65         some_courses.clear();
66         count--;
67     }
68     std::list<Course *> some_courses;
69 private:
70     friend class boost::serialization::access;
71     template<class Archive>
serialize(Archive & ar,const unsigned int)72     void serialize(Archive & ar, const unsigned int /* file_version */){
73         ar & some_courses;
74     }
75 };
76 
77 int Student::count = 0;
78 
79 class Course
80 {
81 public:
82     static int count;
Course()83     Course(){
84         count++;
85     }
~Course()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;
93 private:
94     friend class boost::serialization::access;
95     template<class Archive>
serialize(Archive & ar,const unsigned int)96     void serialize(Archive & ar, const unsigned int /* file_version */){
97         ar & some_students;
98     }
99 };
100 
101 int Course::count = 0;
102 
103 class School
104 {
105 public:
~School()106     ~School(){
107         // must delete all the students because
108         // it "owns" them
109         std::for_each(all_students.begin(), all_students.end(), deleter<Student *>());
110         all_students.clear();
111         // as well as courses
112         std::for_each(all_courses.begin(), all_courses.end(), deleter<Course *>());
113         all_courses.clear();
114     }
115     std::list<Student *> all_students;
116     std::list<Course *> all_courses;
117 private:
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:
128 template<class Archive>
129 void School::serialize(Archive & ar, const unsigned int /* file_version */){
130     // if an exeception 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:
143 template<class Archive>
144 void 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 
150 template<class Archive>
save(Archive & ar,const unsigned int) const151 void School::save(Archive & ar, const unsigned int /* file_version */) const {
152     ar << all_students;
153     ar << all_courses;
154 }
155 
156 template<class Archive>
load(Archive & ar,const unsigned int)157 void School::load(Archive & ar, const unsigned int /* file_version */){
158     // if an exeception 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         // elminate any dangling references
171         all_courses.clear();
172         all_students.clear();
173         throw;
174     }
175 }
176 
init(School * school)177 void 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(bob);
184     school->all_students.push_back(ted);
185     school->all_students.push_back(carol);
186     school->all_students.push_back(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(math);
194     school->all_courses.push_back(history);
195     school->all_courses.push_back(literature);
196     school->all_courses.push_back(gym);
197 
198     bob->some_courses.push_back(math);
199     math->some_students.push_back(bob);
200     bob->some_courses.push_back(literature);
201     literature->some_students.push_back(bob);
202 
203     ted->some_courses.push_back(math);
204     math->some_students.push_back(ted);
205     ted->some_courses.push_back(history);
206     history->some_students.push_back(ted);
207 
208     alice->some_courses.push_back(literature);
209     literature->some_students.push_back(alice);
210     alice->some_courses.push_back(history);
211     history->some_students.push_back(alice);
212 
213     // no students signed up for gym
214     // carol has no courses
215 }
216 
save(const School * const school,const char * filename)217 void 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 
load(School * & school,const char * filename)223 void 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 
main(int argc,char * argv[])235 int 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.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.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.c_str());
257     return Student::count + Course::count;
258 }
259