Frobby  0.9.5
ElementDeleter.h
Go to the documentation of this file.
1 /* Frobby: Software for monomial ideal computations.
2  Copyright (C) 2007 Bjarke Hammersholt Roune (www.broune.com)
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program. If not, see http://www.gnu.org/licenses/.
16 */
17 #ifndef ELEMENT_DELETER_GUARD
18 #define ELEMENT_DELETER_GUARD
19 
20 // The purpose of this class is to call delete on the elements of a
21 // standard library container which holds pointers. It removes the
22 // elements from the container as they are being deleted, but it does
23 // not delete the container itself. Do not use this class to
24 // deallocate a vector of built-in arrays, since those require
25 // deallocation using delete[] rather than delete.
26 //
27 // This can be a convenience when coding, but the real purpose is to
28 // avoid memory leaks in the face of an exception being thrown. When a
29 // standard library container goes out of scope, its destructor is
30 // called, but the destructor does not deallocate pointer elements, so
31 // they will leak unless special care is taken to aovid that, such as
32 // using this class.
33 //
34 // The most straight-forward solution may seem to use
35 // e.g. vector<auto_ptr<T>> in place of vector<T*>, but this does not
36 // work because storing auto_ptr<T> in a standard library container is
37 // never safe (look this up on the internet to get the details).
38 //
39 // The reference counting smart pointer class shared_ptr from Boost or
40 // TR1 would solve this issue even better by using
41 // vector<shared_ptr<T>> instead of vector<T*>, but that would require
42 // a dependency on Boost or on having a very recent compiler which
43 // incorporates TR1.
44 //
45 // TODO: make an AutoVector that wraps std::vector instead of or as a
46 // supplement to this class.
47 //
48 // Container::value_type is required to be a pointer type.
49 template<class Container>
51  public:
52  // Only the constructor can attach an ElementDeleter to a container,
53  // and this helps ensure (but does not guarantee) the precondition
54  // of the destructor of ElementDeleter.
55  ElementDeleter(Container& container): _container(&container) {
56  }
57 
58  // Calls deleteElements() and shares its precondition.
61  }
62 
63  // Call release() to prevent this ElementDeleter from manipulating
64  // the container in any way.
65  void release() {
66  _container = 0;
67  }
68 
69  // If release has been called, this method does nothing. Otherwise
70  // the container must still be valid, and then delete is called on
71  // each element of the array and clear is called on the container.
72  void deleteElements() {
73  if (_container == 0)
74  return;
75 
76  // The code below may seem obviously correct, but it is a
77  // non-trivial fact that it works in the face of exceptions.
78  //
79  // First of all, we are allowed to assume that destructors do not
80  // throw exceptions, so the loop will run to completion.
81  //
82  // Normally clear() *can* throw an exception according to the
83  // standard (which is weird), but if the copy constructor and
84  // assignment operator do not throw exceptions, then it does
85  // not. In our case, we require the element type to be a pointer
86  // type, so for this reason only do we know that clear will not
87  // throw an exception. Thus we do not have to worry about leaving
88  // around a container full of invalid pointers if clear() should
89  // throw an exception.
90 
91  typename Container::iterator end = _container->end();
92  typename Container::iterator it = _container->begin();
93  for (; it != end; ++it)
94  delete *it;
95  _container->clear();
96  }
97 
98  private:
99  Container* _container;
100 };
101 
102 // exceptionSafePushBack is designed to work around the fact that this
103 // code can leak memory:
104 //
105 // vector<int*> vec;
106 // ElementDeleter<vector<int*> > elementDeleter(vec);
107 // auto_ptr<int> p(new int());
108 // vec.push_back(p.release())
109 //
110 // This is because push_back can fail by throwing a bad_alloc, and at
111 // that point the pointer in p has already been released, so that
112 // pointer is now lost and hence the memory is leaked.
113 //
114 // This can be fixed by replacing
115 //
116 // vec.push_back(p.release())
117 //
118 // by
119 //
120 // vec.push_back(0);
121 // vec.back() = p.release();
122 //
123 // but this is annoying and looks quite strange. It is much clearer to
124 // write
125 //
126 // exceptionSafePushBack(vec, p);
127 //
128 template<class Container, class Element>
129 void exceptionSafePushBack(Container& container, auto_ptr<Element> pointer) {
130  container.push_back(0);
131  container.back() = pointer.release();
132 }
133 
134 // Serves the same purpose as exceptionSafePushBack, except that this
135 // overload will simply ignore an exception without propagating it.
136 // Thus if there is not enough memory to add the element to the container,
137 // the element will simply get deleted and the container will remain
138 // unchanged. This works well if e.g. adding an element to a cache, where
139 // it is no great problem if the element gets deleted rather than added.
140 template<class Container, class Element>
141 void noThrowPushBack(Container& container, auto_ptr<Element> pointer) throw () {
142  try {
143  exceptionSafePushBack(container, pointer);
144  } catch (const bad_alloc&) {
145  // Ignore the exception.
146  }
147 }
148 
149 #endif
void noThrowPushBack(Container &container, auto_ptr< Element > pointer)
void exceptionSafePushBack(Container &container, auto_ptr< Element > pointer)
void deleteElements()
ElementDeleter(Container &container)
Container * _container