Merge fatalerror into utility.
[alexxy/gromacs.git] / src / gromacs / options / optionstoragetemplate.h
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \libinternal \file
32  * \brief
33  * Defines gmx::OptionStorageTemplate template.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \inlibraryapi
37  * \ingroup module_options
38  */
39 #ifndef GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
40 #define GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
41
42 #include <string>
43 #include <vector>
44
45 #include <boost/scoped_ptr.hpp>
46
47 #include "../utility/exceptions.h"
48 #include "../utility/gmxassert.h"
49
50 #include "abstractoption.h"
51 #include "abstractoptionstorage.h"
52
53 namespace gmx
54 {
55
56 class Options;
57
58 /*! \libinternal \brief
59  * Templated base class for constructing option value storage classes.
60  *
61  * \tparam T Assignable type that stores a single option value.
62  *
63  * Provides an implementation of the clearSet(), valueCount(), and processSet()
64  * methods of AbstractOptionStorage, as well as a basic no-action
65  * implementation of processAll().  Two new virtual methods are added:
66  * processSetValues() and refreshValues().  The default implementation of
67  * processSetValues() does nothing, and refreshValues() is used to update
68  * secondary storage after values have been added/changed.
69  * This leaves typeString(), formatValue(), and convertValue() to be
70  * implemented in derived classes.  processSetValues() and processAll() can
71  * also be implemented if necessary.
72  *
73  * Implements transaction support for adding values within a set: all calls to
74  * addValue() add the value to a temporary storage, processSetValues() operates
75  * on this temporary storage, and commitValues() then copies these values to
76  * the real storage.  commitValues() provides a strong exception safety
77  * guarantee for the process (and it only throws if it runs out of memory).
78  *
79  * \inlibraryapi
80  * \ingroup module_options
81  */
82 template <typename T>
83 class OptionStorageTemplate : public AbstractOptionStorage
84 {
85     public:
86         //! Alias for the template class for use in base classes.
87         typedef OptionStorageTemplate<T> MyBase;
88         //! Type of the container that contains the current values.
89         typedef std::vector<T> ValueList;
90
91         virtual ~OptionStorageTemplate();
92
93         // No implementation in this class for the pure virtual methods, but
94         // the declarations are still included for clarity.
95         virtual const char *typeString() const = 0;
96         virtual int valueCount() const { return static_cast<int>(_values->size()); }
97         virtual std::string formatValue(int i) const = 0;
98
99     protected:
100         /*! \brief
101          * Initializes the storage from option settings.
102          *
103          * \param[in] settings  Option settings.
104          * \param[in] staticFlags Option flags that are always set and specify
105          *      generic behavior of the option.
106          * \throws  APIError if invalid settings have been provided.
107          */
108         template <class U>
109         OptionStorageTemplate(const OptionTemplate<T, U> &settings,
110                               OptionFlags staticFlags = OptionFlags());
111
112
113         virtual void clearSet();
114         /*! \copydoc AbstractOptionStorage::convertValue()
115          *
116          * Derived classes should call addValue() after they have converted
117          * \p value to the storage type.  It is allowed to call addValue()
118          * more than once, or not at all.  OptionsAssigner::appendValue()
119          * provides the same exception safety guarantee as this method, so it
120          * should be considered whether the implementation can be made strongly
121          * exception safe.
122          */
123         virtual void convertValue(const std::string &value) = 0;
124         /*! \brief
125          * Processes values for a set after all have been converted.
126          *
127          * \param[in,out] values Valid values in the set.
128          * \throws InvalidInputError if the values do not form a valid set.
129          *
130          * This method is called after all convertValue() calls for a set.
131          * \p values contains all values that were validly converted by
132          * convertValue().  The derived class may alter the values, but should
133          * in such a case ensure that a correct number of values is produced.
134          * If the derived class throws, all values in \p values are discarded.
135          */
136         virtual void processSetValues(ValueList *values)
137         {
138         }
139         /*! \copydoc AbstractOptionStorage::processSet()
140          *
141          * OptionStorage template implements transaction support for a set of
142          * values in this method (see the class description), and provides a
143          * more detailed processSetValues() method that can be overridden in
144          * subclasses to process the actual values.  Derived classes should
145          * override that method instead of this one if set value processing is
146          * necessary.
147          */
148         virtual void processSet();
149         /*! \copydoc AbstractOptionStorage::processAll()
150          *
151          * The implementation in OptionStorageTemplate does nothing.
152          */
153         virtual void processAll()
154         {
155         }
156
157         /*! \brief
158          * Removes all values from the storage.
159          *
160          * Does not throw.
161          */
162         void clear() { _values->clear(); }
163         /*! \brief
164          * Adds a value to a temporary storage.
165          *
166          * \param[in] value  Value to add. A copy is made.
167          * \throws std::bad_alloc if out of memory.
168          * \throws InvalidInputError if the maximum value count has been reached.
169          *
170          * Derived classes should call this function from the convertValue()
171          * implementation to add converted values to the storage.
172          * If the maximum value count has been reached, the value is discarded
173          * and an exception is thrown.
174          *
175          * If adding values outside convertValue() (e.g., to set a custom
176          * default value), derived classes should call clearSet() before adding
177          * values (unless in the constructor) and commitValues() once all
178          * values are added.
179          */
180         void addValue(const T &value);
181         /*! \brief
182          * Commits values added with addValue().
183          *
184          * \throws std::bad_alloc if out of memory.
185          *
186          * If this function succeeds, values added with addValue() since the
187          * previous clearSet() are added to the storage for the option.
188          * Only throws in out-of-memory conditions, and provides the strong
189          * exception safety guarantee.
190          *
191          * See addValue() for cases where this method should be used in derived
192          * classes.
193          *
194          * Calls refreshValues() and clearSet() if it is successful.
195          */
196         void commitValues();
197         /*! \brief
198          * Updates alternative store locations.
199          *
200          * Derived classes should override this method if they implement
201          * alternative store locations, and copy/translate values from the
202          * values() vector to these alternative storages.  They should also
203          * call the base class implementation as part of their implementation.
204          *
205          * Should be called in derived classes if values are modified directly
206          * through the values() method, e.g., in processAll().  Does not need
207          * to be called if commitValues() is used.
208          *
209          * Does not throw, and derived classes should not change that.
210          */
211         virtual void refreshValues();
212
213         /*! \brief
214          * Provides derived classes access to the current list of values.
215          *
216          * The non-const variant should only be used from processAll() in
217          * derived classes if necessary, and refreshValues() should be called
218          * if any changes are made.
219          */
220         ValueList &values() { return *_values; }
221         //! Provides derived classes access to the current list of values.
222         const ValueList &values() const { return *_values; }
223
224     private:
225         /*! \brief
226          * Vector for temporary storage of values before commitSet() is called.
227          */
228         ValueList               _setValues;
229         /*! \brief
230          * Vector for primary storage of option values.
231          *
232          * Is never NULL; points either to externally provided vector, or an
233          * internally allocated one.  The allocation is performed by the
234          * constructor.
235          *
236          * Primarily, modifications to values are done only to this storage,
237          * and other storage locations are updated only when refreshValues() is
238          * called.
239          */
240         ValueList              *_values;
241         T                      *_store;
242         int                    *_countptr;
243         boost::scoped_ptr<ValueList> _ownedValues;
244         boost::scoped_ptr<T>    _defaultValueIfSet;
245
246         // Copy and assign disallowed by base.
247 };
248
249
250 template <typename T>
251 template <class U>
252 OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U> &settings,
253                                                 OptionFlags staticFlags)
254     : AbstractOptionStorage(settings, staticFlags),
255       _values(settings._storeVector),
256       _store(settings._store),
257       _countptr(settings._countptr)
258 {
259     if (_values == NULL)
260     {
261         // The flag should be set for proper error checking.
262         GMX_RELEASE_ASSERT(!hasFlag(efExternalValueVector),
263                            "Internal inconsistency");
264         _ownedValues.reset(new std::vector<T>);
265         _values = _ownedValues.get();
266     }
267     if (hasFlag(efNoDefaultValue)
268         && (settings._defaultValue != NULL
269             || settings._defaultValueIfSet != NULL))
270     {
271         GMX_THROW(APIError("Option does not support default value, but one is set"));
272     }
273     if (_store != NULL && _countptr == NULL && !hasFlag(efVector)
274         && minValueCount() != maxValueCount())
275     {
276         GMX_THROW(APIError("Count storage is not set, although the number of produced values is not known"));
277     }
278     if (!hasFlag(efNoDefaultValue))
279     {
280         setFlag(efHasDefaultValue);
281         if (settings._defaultValue != NULL)
282         {
283             _values->clear();
284             addValue(*settings._defaultValue);
285             // TODO: This is a bit hairy, as it indirectly calls a virtual function.
286             commitValues();
287         }
288         else if (_ownedValues.get() == NULL && _store != NULL)
289         {
290             _values->clear();
291             int count = (settings.isVector() ?
292                             settings._maxValueCount : settings._minValueCount);
293             for (int i = 0; i < count; ++i)
294             {
295                 _values->push_back(_store[i]);
296             }
297         }
298         if (settings._defaultValueIfSet != NULL)
299         {
300             if (hasFlag(efMulti))
301             {
302                 GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
303             }
304             _defaultValueIfSet.reset(new T(*settings._defaultValueIfSet));
305         }
306     }
307     setFlag(efClearOnNextSet);
308 }
309
310
311 template <typename T>
312 OptionStorageTemplate<T>::~OptionStorageTemplate()
313 {
314 }
315
316
317 template <typename T>
318 void OptionStorageTemplate<T>::clearSet()
319 {
320     _setValues.clear();
321 }
322
323
324 template <typename T>
325 void OptionStorageTemplate<T>::processSet()
326 {
327     processSetValues(&_setValues);
328     if (_setValues.empty() && _defaultValueIfSet.get() != NULL)
329     {
330         addValue(*_defaultValueIfSet);
331         setFlag(efHasDefaultValue);
332     }
333     else
334     {
335         clearFlag(efHasDefaultValue);
336     }
337     if (!hasFlag(efDontCheckMinimumCount)
338         && _setValues.size() < static_cast<size_t>(minValueCount()))
339     {
340         clearSet();
341         GMX_THROW(InvalidInputError("Too few (valid) values"));
342     }
343     commitValues();
344 }
345
346
347 template <typename T>
348 void OptionStorageTemplate<T>::addValue(const T &value)
349 {
350     if (maxValueCount() >= 0
351         && _setValues.size() >= static_cast<size_t>(maxValueCount()))
352     {
353         GMX_THROW(InvalidInputError("Too many values"));
354     }
355     _setValues.push_back(value);
356 }
357
358
359 template <typename T>
360 void OptionStorageTemplate<T>::commitValues()
361 {
362     if (hasFlag(efClearOnNextSet))
363     {
364         _values->swap(_setValues);
365         clearFlag(efClearOnNextSet);
366     }
367     else
368     {
369         _values->insert(_values->end(), _setValues.begin(), _setValues.end());
370     }
371     clearSet();
372     refreshValues();
373 }
374
375
376 template <typename T>
377 void OptionStorageTemplate<T>::refreshValues()
378 {
379     if (_countptr != NULL)
380     {
381         *_countptr = static_cast<int>(_values->size());
382     }
383     if (_store != NULL)
384     {
385         for (size_t i = 0; i < _values->size(); ++i)
386         {
387             _store[i] = (*_values)[i];
388         }
389     }
390 }
391
392 } // namespace gmx
393
394 #endif