demo_exception.cpp 7.4 KB

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