| /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 |
| // demo_exception.cpp |
| |
| // (C) Copyright 2002-4 Robert Ramey - http://www.rrsd.com . |
| // Use, modification and distribution is subject to the Boost Software |
| // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| |
| // Example of safe exception handling for pointer de-serialization |
| // |
| // This example was prepared by Robert Ramey to demonstrate and test |
| // safe exception handling during the de-serialization of pointers in |
| // a non-trivial example. |
| // |
| // Hopefully, this addresses exception issues raised by |
| // Vahan Margaryan who spent considerable time and effort |
| // in the analysis and testing of issues of exception safety |
| // of the serialization library. |
| |
| #include <algorithm> |
| #include <iostream> |
| #include <cstddef> // NULL |
| #include <fstream> |
| #include <string> |
| |
| #include <cstdio> // remove |
| #include <boost/config.hpp> |
| #if defined(BOOST_NO_STDC_NAMESPACE) |
| namespace std{ |
| using ::remove; |
| } |
| #endif |
| |
| #include <boost/archive/tmpdir.hpp> |
| |
| #ifndef BOOST_NO_EXCEPTIONS |
| #include <exception> |
| #endif |
| |
| #include <boost/archive/text_iarchive.hpp> |
| #include <boost/archive/text_oarchive.hpp> |
| |
| #include <boost/serialization/list.hpp> |
| #include <boost/serialization/split_member.hpp> |
| |
| template<class TPTR> |
| struct deleter |
| { |
| void operator()(TPTR t){ |
| delete t; |
| } |
| }; |
| |
| class Course; |
| class Student; |
| |
| class Student |
| { |
| public: |
| static int count; |
| Student(){ |
| count++; |
| } |
| ~Student(){ |
| some_courses.clear(); |
| count--; |
| } |
| std::list<Course *> some_courses; |
| private: |
| friend class boost::serialization::access; |
| template<class Archive> |
| void serialize(Archive & ar, const unsigned int /* file_version */){ |
| ar & some_courses; |
| } |
| }; |
| |
| int Student::count = 0; |
| |
| class Course |
| { |
| public: |
| static int count; |
| Course(){ |
| count++; |
| } |
| ~Course(){ |
| // doesnt delete pointers in list |
| // since it doesn't "own" them |
| some_students.clear(); |
| count--; |
| } |
| std::list<Student *> some_students; |
| private: |
| friend class boost::serialization::access; |
| template<class Archive> |
| void serialize(Archive & ar, const unsigned int /* file_version */){ |
| ar & some_students; |
| } |
| }; |
| |
| int Course::count = 0; |
| |
| class School |
| { |
| public: |
| ~School(){ |
| // must delete all the students because |
| // it "owns" them |
| std::for_each(all_students.begin(), all_students.end(), deleter<Student *>()); |
| all_students.clear(); |
| // as well as courses |
| std::for_each(all_courses.begin(), all_courses.end(), deleter<Course *>()); |
| all_courses.clear(); |
| } |
| std::list<Student *> all_students; |
| std::list<Course *> all_courses; |
| private: |
| friend class boost::serialization::access; |
| BOOST_SERIALIZATION_SPLIT_MEMBER() |
| template<class Archive> |
| void save(Archive & ar, const unsigned int file_version) const; |
| template<class Archive> |
| void load(Archive & ar, const unsigned int file_version); |
| }; |
| |
| #if 0 |
| // case 1: |
| template<class Archive> |
| void School::serialize(Archive & ar, const unsigned int /* file_version */){ |
| // if an exeception occurs while loading courses |
| // the structure courses may have some courses each |
| // with students |
| ar & all_courses; |
| // while all_students will have no members. |
| ar & all_students; // create students that have no courses |
| // so ~School() will delete all members of courses |
| // but this will NOT delete any students - see above |
| // a memory leak will be the result. |
| } |
| |
| // switching the order of serialization doesn't help in this case |
| // case 2: |
| template<class Archive> |
| void School::serialize(Archive & ar, const unsigned int /* file_version */){ |
| ar & all_students; |
| ar >> all_courses; // create any courses that have no students |
| } |
| #endif |
| |
| template<class Archive> |
| void School::save(Archive & ar, const unsigned int /* file_version */) const { |
| ar << all_students; |
| ar << all_courses; |
| } |
| |
| template<class Archive> |
| void School::load(Archive & ar, const unsigned int /* file_version */){ |
| // if an exeception occurs while loading courses |
| // the structure courses may have some courses each |
| // with students |
| try{ |
| // deserialization of a Course * will in general provoke the |
| // deserialization of Student * which are added to the list of |
| // students for a class. That is, this process will result |
| // in the copying of a pointer. |
| ar >> all_courses; |
| ar >> all_students; // create students that have no courses |
| } |
| catch(std::exception){ |
| // elminate any dangling references |
| all_courses.clear(); |
| all_students.clear(); |
| throw; |
| } |
| } |
| |
| void init(School *school){ |
| Student *bob = new Student(); |
| Student *ted = new Student(); |
| Student *carol = new Student(); |
| Student *alice = new Student(); |
| |
| school->all_students.push_back(bob); |
| school->all_students.push_back(ted); |
| school->all_students.push_back(carol); |
| school->all_students.push_back(alice); |
| |
| Course *math = new Course(); |
| Course *history = new Course(); |
| Course *literature = new Course(); |
| Course *gym = new Course(); |
| |
| school->all_courses.push_back(math); |
| school->all_courses.push_back(history); |
| school->all_courses.push_back(literature); |
| school->all_courses.push_back(gym); |
| |
| bob->some_courses.push_back(math); |
| math->some_students.push_back(bob); |
| bob->some_courses.push_back(literature); |
| literature->some_students.push_back(bob); |
| |
| ted->some_courses.push_back(math); |
| math->some_students.push_back(ted); |
| ted->some_courses.push_back(history); |
| history->some_students.push_back(ted); |
| |
| alice->some_courses.push_back(literature); |
| literature->some_students.push_back(alice); |
| alice->some_courses.push_back(history); |
| history->some_students.push_back(alice); |
| |
| // no students signed up for gym |
| // carol has no courses |
| } |
| |
| void save(const School * const school, const char *filename){ |
| std::ofstream ofile(filename); |
| boost::archive::text_oarchive ar(ofile); |
| ar << school; |
| } |
| |
| void load(School * & school, const char *filename){ |
| std::ifstream ifile(filename); |
| boost::archive::text_iarchive ar(ifile); |
| try{ |
| ar >> school; |
| } |
| catch(std::exception){ |
| // eliminate dangling reference |
| school = NULL; |
| } |
| } |
| |
| int main(int argc, char *argv[]){ |
| std::string filename(boost::archive::tmpdir()); |
| filename += "/demofile.txt"; |
| |
| School *school = new School(); |
| std::cout << "1. student count = " << Student::count << std::endl; |
| std::cout << "2. class count = " << Course::count << std::endl; |
| init(school); |
| std::cout << "3. student count = " << Student::count << std::endl; |
| std::cout << "4. class count = " << Course::count << std::endl; |
| save(school, filename.c_str()); |
| delete school; |
| school = NULL; |
| std::cout << "5. student count = " << Student::count << std::endl; |
| std::cout << "6. class count = " << Course::count << std::endl; |
| load(school, filename.c_str()); |
| std::cout << "7. student count = " << Student::count << std::endl; |
| std::cout << "8. class count = " << Course::count << std::endl; |
| delete school; |
| std::cout << "9. student count = " << Student::count << std::endl; |
| std::cout << "10. class count = " << Course::count << std::endl; |
| std::remove(filename.c_str()); |
| return Student::count + Course::count; |
| } |