Merge remote-tracking branch 'gerrit/release-4-6'
[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 "../fatalerror/exceptions.h"
48 #include "../fatalerror/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 InvalidInputError if the maximum value count has been reached.
168          *
169          * Derived classes should call this function from the convertValue()
170          * implementation to add converted values to the storage.
171          * If the maximum value cont has been reached, the value is discarded
172          * and an exception is thrown.
173          *
174          * If adding values outside convertValue() (e.g., to set a custom
175          * default value), derived classes should call clearSet() before adding
176          * values (unless in the constructor) and commitValues() once all
177          * values are added.
178          */
179         void addValue(const T &value);
180         /*! \brief
181          * Commits values added with addValue().
182          *
183          * If this function succeeds, values added with addValue() since the
184          * previous clearSet() are added to the storage for the option.
185          * Only throws in out-of-memory conditions, and provides the strong
186          * exception safety guarantee.
187          *
188          * See addValue() for cases where this method should be used in derived
189          * classes.
190          *
191          * Calls refreshValues() and clearSet() if it is successful.
192          */
193         void commitValues();
194         /*! \brief
195          * Updates alternative store locations.
196          *
197          * Derived classes should override this method if they implement
198          * alternative store locations, and copy/translate values from the
199          * values() vector to these alternative storages.  They should also
200          * call the base class implementation as part of their implementation.
201          *
202          * Should be called in derived classes if values are modified directly
203          * through the values() method, e.g., in processAll().  Does not need
204          * to be called if commitValues() is used.
205          *
206          * Does not throw, and derived classes should not change that.
207          */
208         virtual void refreshValues();
209
210         /*! \brief
211          * Provides derived classes access to the current list of values.
212          *
213          * The non-const variant should only be used from processAll() in
214          * derived classes if necessary, and refreshValues() should be called
215          * if any changes are made.
216          */
217         ValueList &values() { return *_values; }
218         //! Provides derived classes access to the current list of values.
219         const ValueList &values() const { return *_values; }
220
221     private:
222         /*! \brief
223          * Vector for temporary storage of values before commitSet() is called.
224          */
225         ValueList               _setValues;
226         /*! \brief
227          * Vector for primary storage of option values.
228          *
229          * Is never NULL; points either to externally provided vector, or an
230          * internally allocated one.  The allocation is performed by the
231          * constructor.
232          *
233          * Primarily, modifications to values are done only to this storage,
234          * and other storage locations are updated only when refreshValues() is
235          * called.
236          */
237         ValueList              *_values;
238         T                      *_store;
239         int                    *_countptr;
240         boost::scoped_ptr<ValueList> _ownedValues;
241         boost::scoped_ptr<T>    _defaultValueIfSet;
242
243         // Copy and assign disallowed by base.
244 };
245
246
247 template <typename T>
248 template <class U>
249 OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U> &settings,
250                                                 OptionFlags staticFlags)
251     : AbstractOptionStorage(settings, staticFlags),
252       _values(settings._storeVector),
253       _store(settings._store),
254       _countptr(settings._countptr)
255 {
256     if (_values == NULL)
257     {
258         // The flag should be set for proper error checking.
259         GMX_RELEASE_ASSERT(!hasFlag(efExternalValueVector),
260                            "Internal inconsistency");
261         _ownedValues.reset(new std::vector<T>);
262         _values = _ownedValues.get();
263     }
264     if (hasFlag(efNoDefaultValue)
265         && (settings._defaultValue != NULL
266             || settings._defaultValueIfSet != NULL))
267     {
268         GMX_THROW(APIError("Option does not support default value, but one is set"));
269     }
270     if (_store != NULL && _countptr == NULL && !hasFlag(efVector)
271         && minValueCount() != maxValueCount())
272     {
273         GMX_THROW(APIError("Count storage is not set, although the number of produced values is not known"));
274     }
275     if (!hasFlag(efNoDefaultValue))
276     {
277         setFlag(efHasDefaultValue);
278         if (settings._defaultValue != NULL)
279         {
280             _values->clear();
281             addValue(*settings._defaultValue);
282             // TODO: This is a bit hairy, as it indirectly calls a virtual function.
283             commitValues();
284         }
285         else if (_ownedValues.get() == NULL && _store != NULL)
286         {
287             _values->clear();
288             int count = (settings.isVector() ?
289                             settings._maxValueCount : settings._minValueCount);
290             for (int i = 0; i < count; ++i)
291             {
292                 _values->push_back(_store[i]);
293             }
294         }
295         if (settings._defaultValueIfSet != NULL)
296         {
297             if (hasFlag(efMulti))
298             {
299                 GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
300             }
301             _defaultValueIfSet.reset(new T(*settings._defaultValueIfSet));
302         }
303     }
304     setFlag(efClearOnNextSet);
305 }
306
307
308 template <typename T>
309 OptionStorageTemplate<T>::~OptionStorageTemplate()
310 {
311 }
312
313
314 template <typename T>
315 void OptionStorageTemplate<T>::clearSet()
316 {
317     _setValues.clear();
318 }
319
320
321 template <typename T>
322 void OptionStorageTemplate<T>::processSet()
323 {
324     processSetValues(&_setValues);
325     if (_setValues.empty() && _defaultValueIfSet.get() != NULL)
326     {
327         addValue(*_defaultValueIfSet);
328         setFlag(efHasDefaultValue);
329     }
330     else
331     {
332         clearFlag(efHasDefaultValue);
333     }
334     if (!hasFlag(efDontCheckMinimumCount)
335         && _setValues.size() < static_cast<size_t>(minValueCount()))
336     {
337         clearSet();
338         GMX_THROW(InvalidInputError("Too few (valid) values"));
339     }
340     commitValues();
341 }
342
343
344 template <typename T>
345 void OptionStorageTemplate<T>::addValue(const T &value)
346 {
347     if (maxValueCount() >= 0
348         && _setValues.size() >= static_cast<size_t>(maxValueCount()))
349     {
350         GMX_THROW(InvalidInputError("Too many values"));
351     }
352     _setValues.push_back(value);
353 }
354
355
356 template <typename T>
357 void OptionStorageTemplate<T>::commitValues()
358 {
359     if (hasFlag(efClearOnNextSet))
360     {
361         _values->swap(_setValues);
362         clearFlag(efClearOnNextSet);
363     }
364     else
365     {
366         _values->insert(_values->end(), _setValues.begin(), _setValues.end());
367     }
368     clearSet();
369     refreshValues();
370 }
371
372
373 template <typename T>
374 void OptionStorageTemplate<T>::refreshValues()
375 {
376     if (_countptr != NULL)
377     {
378         *_countptr = static_cast<int>(_values->size());
379     }
380     if (_store != NULL)
381     {
382         for (size_t i = 0; i < _values->size(); ++i)
383         {
384             _store[i] = (*_values)[i];
385         }
386     }
387 }
388
389 } // namespace gmx
390
391 #endif