Merge release-4-6 into master
[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         /*! \copydoc gmx::AbstractOptionStorage::formatValue()
98          *
99          * OptionStorageTemplate implements handling of DefaultValueIfSetIndex
100          * in this method, as well as checking that \p i is a valid index.
101          * Derived classes must implement formatSingleValue() to provide the
102          * actual formatting for a value of type \p T.
103          */
104         virtual std::string formatValue(int i) const;
105
106     protected:
107         /*! \brief
108          * Initializes the storage from option settings.
109          *
110          * \param[in] settings  Option settings.
111          * \param[in] staticFlags Option flags that are always set and specify
112          *      generic behavior of the option.
113          * \throws  APIError if invalid settings have been provided.
114          */
115         template <class U>
116         explicit OptionStorageTemplate(const OptionTemplate<T, U> &settings,
117                                        OptionFlags staticFlags = OptionFlags());
118
119
120         virtual void clearSet();
121         /*! \copydoc gmx::AbstractOptionStorage::convertValue()
122          *
123          * Derived classes should call addValue() after they have converted
124          * \p value to the storage type.  It is allowed to call addValue()
125          * more than once, or not at all.  OptionsAssigner::appendValue()
126          * provides the same exception safety guarantee as this method, so it
127          * should be considered whether the implementation can be made strongly
128          * exception safe.
129          */
130         virtual void convertValue(const std::string &value) = 0;
131         /*! \brief
132          * Processes values for a set after all have been converted.
133          *
134          * \param[in,out] values Valid values in the set.
135          * \throws InvalidInputError if the values do not form a valid set.
136          *
137          * This method is called after all convertValue() calls for a set.
138          * \p values contains all values that were validly converted by
139          * convertValue().  The derived class may alter the values, but should
140          * in such a case ensure that a correct number of values is produced.
141          * If the derived class throws, all values in \p values are discarded.
142          */
143         virtual void processSetValues(ValueList *values)
144         {
145         }
146         /*! \copydoc gmx::AbstractOptionStorage::processSet()
147          *
148          * OptionStorageTemplate implements transaction support for a set of
149          * values in this method (see the class description), and provides a
150          * more detailed processSetValues() method that can be overridden in
151          * subclasses to process the actual values.  Derived classes should
152          * override that method instead of this one if set value processing is
153          * necessary.
154          */
155         virtual void processSet();
156         /*! \copydoc gmx::AbstractOptionStorage::processAll()
157          *
158          * The implementation in OptionStorageTemplate does nothing.
159          */
160         virtual void processAll()
161         {
162         }
163         /*! \brief
164          * Formats a single value as a string.
165          *
166          * \param[in] value  Value to format.
167          * \returns   \p value formatted as a string.
168          *
169          * The derived class must provide this method to format values a
170          * strings.  Called by formatValue() to do the actual formatting.
171          */
172         virtual std::string formatSingleValue(const T &value) const = 0;
173
174         /*! \brief
175          * Removes all values from the storage.
176          *
177          * Does not throw.
178          */
179         void clear() { values_->clear(); }
180         /*! \brief
181          * Adds a value to a temporary storage.
182          *
183          * \param[in] value  Value to add. A copy is made.
184          * \throws std::bad_alloc if out of memory.
185          * \throws InvalidInputError if the maximum value count has been reached.
186          *
187          * Derived classes should call this function from the convertValue()
188          * implementation to add converted values to the storage.
189          * If the maximum value count has been reached, the value is discarded
190          * and an exception is thrown.
191          *
192          * If adding values outside convertValue() (e.g., to set a custom
193          * default value), derived classes should call clearSet() before adding
194          * values (unless in the constructor) and commitValues() once all
195          * values are added.
196          */
197         void addValue(const T &value);
198         /*! \brief
199          * Commits values added with addValue().
200          *
201          * \throws std::bad_alloc if out of memory.
202          *
203          * If this function succeeds, values added with addValue() since the
204          * previous clearSet() are added to the storage for the option.
205          * Only throws in out-of-memory conditions, and provides the strong
206          * exception safety guarantee.
207          *
208          * See addValue() for cases where this method should be used in derived
209          * classes.
210          *
211          * Calls refreshValues() and clearSet() if it is successful.
212          */
213         void commitValues();
214         /*! \brief
215          * Updates alternative store locations.
216          *
217          * Derived classes should override this method if they implement
218          * alternative store locations, and copy/translate values from the
219          * values() vector to these alternative storages.  They should also
220          * call the base class implementation as part of their implementation.
221          *
222          * Should be called in derived classes if values are modified directly
223          * through the values() method, e.g., in processAll().  Does not need
224          * to be called if commitValues() is used.
225          *
226          * Does not throw, and derived classes should not change that.
227          */
228         virtual void refreshValues();
229
230         /*! \brief
231          * Sets the default value for the option.
232          *
233          * \param[in] value  Default value to set.
234          * \throws    std::bad_alloc if out of memory.
235          *
236          * This method can be used from the derived class constructor to
237          * programmatically set a default value.
238          */
239         void setDefaultValue(const T &value);
240         /*! \brief
241          * Sets the default value if set for the option.
242          *
243          * \param[in] value  Default value to set.
244          * \throws    std::bad_alloc if out of memory.
245          *
246          * This method can be used from the derived class constructor to
247          * programmatically set a default value.
248          */
249         void setDefaultValueIfSet(const T &value);
250
251         /*! \brief
252          * Provides derived classes access to the current list of values.
253          *
254          * The non-const variant should only be used from processAll() in
255          * derived classes if necessary, and refreshValues() should be called
256          * if any changes are made.
257          */
258         ValueList       &values() { return *values_; }
259         //! Provides derived classes access to the current list of values.
260         const ValueList &values() const { return *values_; }
261
262     private:
263         /*! \brief
264          * Vector for temporary storage of values before commitSet() is called.
265          */
266         ValueList               setValues_;
267         /*! \brief
268          * Vector for primary storage of option values.
269          *
270          * Is never NULL; points either to externally provided vector, or an
271          * internally allocated one.  The allocation is performed by the
272          * constructor.
273          *
274          * Primarily, modifications to values are done only to this storage,
275          * and other storage locations are updated only when refreshValues() is
276          * called.
277          */
278         ValueList                   *values_;
279         T                           *store_;
280         int                         *countptr_;
281         boost::scoped_ptr<ValueList> ownedValues_;
282         boost::scoped_ptr<T>         defaultValueIfSet_;
283
284         // Copy and assign disallowed by base.
285 };
286
287
288 template <typename T>
289 template <class U>
290 OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U> &settings,
291                                                 OptionFlags staticFlags)
292     : AbstractOptionStorage(settings, staticFlags),
293       values_(settings.storeVector_),
294       store_(settings.store_),
295       countptr_(settings.countptr_)
296 {
297     // If the maximum number of values is not known, storage to
298     // caller-allocated memory is unsafe.
299     if (store_ != NULL && (maxValueCount() < 0 || hasFlag(efOption_MultipleTimes)))
300     {
301         GMX_THROW(APIError("Cannot set user-allocated storage for arbitrary number of values"));
302     }
303     if (values_ == NULL)
304     {
305         ownedValues_.reset(new std::vector<T>);
306         values_ = ownedValues_.get();
307     }
308     if (hasFlag(efOption_NoDefaultValue)
309         && (settings.defaultValue_ != NULL
310             || settings.defaultValueIfSet_ != NULL))
311     {
312         GMX_THROW(APIError("Option does not support default value, but one is set"));
313     }
314     if (store_ != NULL && countptr_ == NULL && !isVector()
315         && minValueCount() != maxValueCount())
316     {
317         GMX_THROW(APIError("Count storage is not set, although the number of produced values is not known"));
318     }
319     if (!hasFlag(efOption_NoDefaultValue))
320     {
321         setFlag(efOption_HasDefaultValue);
322         if (settings.defaultValue_ != NULL)
323         {
324             setDefaultValue(*settings.defaultValue_);
325         }
326         else if (ownedValues_.get() != NULL && store_ != NULL)
327         {
328             values_->clear();
329             int count = (settings.isVector() ?
330                          settings.maxValueCount_ : settings.minValueCount_);
331             for (int i = 0; i < count; ++i)
332             {
333                 values_->push_back(store_[i]);
334             }
335         }
336         if (settings.defaultValueIfSet_ != NULL)
337         {
338             setDefaultValueIfSet(*settings.defaultValueIfSet_);
339         }
340     }
341 }
342
343
344 template <typename T>
345 OptionStorageTemplate<T>::~OptionStorageTemplate()
346 {
347 }
348
349
350 template <typename T>
351 std::string OptionStorageTemplate<T>::formatValue(int i) const
352 {
353     GMX_RELEASE_ASSERT(i == DefaultValueIfSetIndex || (i >= 0 && i < valueCount()),
354                        "Invalid value index");
355     if (i == DefaultValueIfSetIndex)
356     {
357         if (defaultValueIfSet_.get() != NULL)
358         {
359             return formatSingleValue(*defaultValueIfSet_);
360         }
361         return std::string();
362     }
363     return formatSingleValue(values()[i]);
364 }
365
366
367 template <typename T>
368 void OptionStorageTemplate<T>::clearSet()
369 {
370     setValues_.clear();
371 }
372
373
374 template <typename T>
375 void OptionStorageTemplate<T>::processSet()
376 {
377     processSetValues(&setValues_);
378     if (setValues_.empty() && defaultValueIfSet_.get() != NULL)
379     {
380         addValue(*defaultValueIfSet_);
381         setFlag(efOption_HasDefaultValue);
382     }
383     else
384     {
385         clearFlag(efOption_HasDefaultValue);
386     }
387     if (!hasFlag(efOption_DontCheckMinimumCount)
388         && setValues_.size() < static_cast<size_t>(minValueCount()))
389     {
390         GMX_THROW(InvalidInputError("Too few (valid) values"));
391     }
392     commitValues();
393 }
394
395
396 template <typename T>
397 void OptionStorageTemplate<T>::addValue(const T &value)
398 {
399     if (maxValueCount() >= 0
400         && setValues_.size() >= static_cast<size_t>(maxValueCount()))
401     {
402         GMX_THROW(InvalidInputError("Too many values"));
403     }
404     setValues_.push_back(value);
405 }
406
407
408 template <typename T>
409 void OptionStorageTemplate<T>::commitValues()
410 {
411     if (hasFlag(efOption_ClearOnNextSet))
412     {
413         values_->swap(setValues_);
414     }
415     else
416     {
417         values_->insert(values_->end(), setValues_.begin(), setValues_.end());
418     }
419     clearSet();
420     refreshValues();
421 }
422
423
424 template <typename T>
425 void OptionStorageTemplate<T>::refreshValues()
426 {
427     if (countptr_ != NULL)
428     {
429         *countptr_ = static_cast<int>(values_->size());
430     }
431     if (store_ != NULL)
432     {
433         for (size_t i = 0; i < values_->size(); ++i)
434         {
435             store_[i] = (*values_)[i];
436         }
437     }
438 }
439
440
441 template <typename T>
442 void OptionStorageTemplate<T>::setDefaultValue(const T &value)
443 {
444     if (hasFlag(efOption_NoDefaultValue))
445     {
446         GMX_THROW(APIError("Option does not support default value, but one is set"));
447     }
448     if (hasFlag(efOption_HasDefaultValue))
449     {
450         setFlag(efOption_ExplicitDefaultValue);
451         clear();
452         clearSet();
453         addValue(value);
454         // TODO: As this is called from the constructor, it should not call
455         // virtual functions.
456         commitValues();
457     }
458 }
459
460
461 template <typename T>
462 void OptionStorageTemplate<T>::setDefaultValueIfSet(const T &value)
463 {
464     if (hasFlag(efOption_NoDefaultValue))
465     {
466         GMX_THROW(APIError("Option does not support default value, but one is set"));
467     }
468     if (hasFlag(efOption_MultipleTimes))
469     {
470         GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
471     }
472     defaultValueIfSet_.reset(new T(value));
473 }
474
475 } // namespace gmx
476
477 #endif