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.
49template<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.
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//
128template<class Container, class Element>
129void 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.
140template<class Container, class Element>
141void 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