dae027c79ad8a08309ba1cd3b435b4b92eb4c08e
[alexxy/gromacs.git] / src / gromacs / options / optionstoragetemplate.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \libinternal \file
36  * \brief
37  * Defines gmx::OptionStorageTemplate template.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \inlibraryapi
41  * \ingroup module_options
42  */
43 #ifndef GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
44 #define GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
45
46 #include <string>
47 #include <vector>
48
49 #include <boost/scoped_ptr.hpp>
50
51 #include "../utility/common.h"
52 #include "../utility/exceptions.h"
53 #include "../utility/gmxassert.h"
54
55 #include "abstractoption.h"
56 #include "abstractoptionstorage.h"
57
58 namespace gmx
59 {
60
61 class Options;
62
63 /*! \libinternal \brief
64  * Templated base class for constructing option value storage classes.
65  *
66  * \tparam T Assignable type that stores a single option value.
67  *
68  * Provides an implementation of the clearSet(), valueCount(), and processSet()
69  * methods of AbstractOptionStorage, as well as a basic no-action
70  * implementation of processAll().  Two new virtual methods are added:
71  * processSetValues() and refreshValues().  The default implementation of
72  * processSetValues() does nothing, and refreshValues() is used to update
73  * secondary storage after values have been added/changed.
74  * This leaves typeString(), formatValue(), and convertValue() to be
75  * implemented in derived classes.  processSetValues() and processAll() can
76  * also be implemented if necessary.
77  *
78  * Implements transaction support for adding values within a set: all calls to
79  * addValue() add the value to a temporary storage, processSetValues() operates
80  * on this temporary storage, and commitValues() then copies these values to
81  * the real storage.  commitValues() provides a strong exception safety
82  * guarantee for the process (and it only throws if it runs out of memory).
83  *
84  * \inlibraryapi
85  * \ingroup module_options
86  */
87 template <typename T>
88 class OptionStorageTemplate : public AbstractOptionStorage
89 {
90     public:
91         //! Alias for the template class for use in base classes.
92         typedef OptionStorageTemplate<T> MyBase;
93         //! Type of the container that contains the current values.
94         typedef std::vector<T> ValueList;
95
96         virtual ~OptionStorageTemplate();
97
98         // No implementation in this class for the pure virtual methods, but
99         // the declarations are still included for clarity.
100         virtual std::string typeString() const = 0;
101         virtual int valueCount() const { return static_cast<int>(values_->size()); }
102         /*! \copydoc gmx::AbstractOptionStorage::formatValue()
103          *
104          * OptionStorageTemplate implements handling of DefaultValueIfSetIndex
105          * in this method, as well as checking that \p i is a valid index.
106          * Derived classes must implement formatSingleValue() to provide the
107          * actual formatting for a value of type \p T.
108          */
109         virtual std::string formatValue(int i) const;
110
111     protected:
112         /*! \brief
113          * Initializes the storage from option settings.
114          *
115          * \param[in] settings  Option settings.
116          * \param[in] staticFlags Option flags that are always set and specify
117          *      generic behavior of the option.
118          * \throws  APIError if invalid settings have been provided.
119          */
120         template <class U>
121         explicit OptionStorageTemplate(const OptionTemplate<T, U> &settings,
122                                        OptionFlags staticFlags = OptionFlags());
123
124
125         virtual void clearSet();
126         /*! \copydoc gmx::AbstractOptionStorage::convertValue()
127          *
128          * Derived classes should call addValue() after they have converted
129          * \p value to the storage type.  It is allowed to call addValue()
130          * more than once, or not at all.  OptionsAssigner::appendValue()
131          * provides the same exception safety guarantee as this method, so it
132          * should be considered whether the implementation can be made strongly
133          * exception safe.
134          */
135         virtual void convertValue(const std::string &value) = 0;
136         /*! \brief
137          * Processes values for a set after all have been converted.
138          *
139          * \param[in,out] values Valid values in the set.
140          * \throws InvalidInputError if the values do not form a valid set.
141          *
142          * This method is called after all convertValue() calls for a set.
143          * \p values contains all values that were validly converted by
144          * convertValue().  The derived class may alter the values, but should
145          * in such a case ensure that a correct number of values is produced.
146          * If the derived class throws, all values in \p values are discarded.
147          */
148         virtual void processSetValues(ValueList *values)
149         {
150             GMX_UNUSED_VALUE(values);
151         }
152         /*! \copydoc gmx::AbstractOptionStorage::processSet()
153          *
154          * OptionStorageTemplate implements transaction support for a set of
155          * values in this method (see the class description), and provides a
156          * more detailed processSetValues() method that can be overridden in
157          * subclasses to process the actual values.  Derived classes should
158          * override that method instead of this one if set value processing is
159          * necessary.
160          */
161         virtual void processSet();
162         /*! \copydoc gmx::AbstractOptionStorage::processAll()
163          *
164          * The implementation in OptionStorageTemplate does nothing.
165          */
166         virtual void processAll()
167         {
168         }
169         /*! \brief
170          * Formats a single value as a string.
171          *
172          * \param[in] value  Value to format.
173          * \returns   \p value formatted as a string.
174          *
175          * The derived class must provide this method to format values a
176          * strings.  Called by formatValue() to do the actual formatting.
177          */
178         virtual std::string formatSingleValue(const T &value) const = 0;
179
180         /*! \brief
181          * Removes all values from the storage.
182          *
183          * Does not throw.
184          */
185         void clear() { values_->clear(); }
186         /*! \brief
187          * Adds a value to a temporary storage.
188          *
189          * \param[in] value  Value to add. A copy is made.
190          * \throws std::bad_alloc if out of memory.
191          * \throws InvalidInputError if the maximum value count has been reached.
192          *
193          * Derived classes should call this function from the convertValue()
194          * implementation to add converted values to the storage.
195          * If the maximum value count has been reached, the value is discarded
196          * and an exception is thrown.
197          *
198          * If adding values outside convertValue() (e.g., to set a custom
199          * default value), derived classes should call clearSet() before adding
200          * values (unless in the constructor) and commitValues() once all
201          * values are added.
202          */
203         void addValue(const T &value);
204         /*! \brief
205          * Commits values added with addValue().
206          *
207          * \throws std::bad_alloc if out of memory.
208          *
209          * If this function succeeds, values added with addValue() since the
210          * previous clearSet() are added to the storage for the option.
211          * Only throws in out-of-memory conditions, and provides the strong
212          * exception safety guarantee.
213          *
214          * See addValue() for cases where this method should be used in derived
215          * classes.
216          *
217          * Calls refreshValues() and clearSet() if it is successful.
218          */
219         void commitValues();
220         /*! \brief
221          * Updates alternative store locations.
222          *
223          * Derived classes should override this method if they implement
224          * alternative store locations, and copy/translate values from the
225          * values() vector to these alternative storages.  They should also
226          * call the base class implementation as part of their implementation.
227          *
228          * Should be called in derived classes if values are modified directly
229          * through the values() method, e.g., in processAll().  Does not need
230          * to be called if commitValues() is used.
231          *
232          * Does not throw, and derived classes should not change that.
233          */
234         virtual void refreshValues();
235
236         /*! \brief
237          * Sets the default value for the option.
238          *
239          * \param[in] value  Default value to set.
240          * \throws    std::bad_alloc if out of memory.
241          *
242          * This method can be used from the derived class constructor to
243          * programmatically set a default value.
244          */
245         void setDefaultValue(const T &value);
246         /*! \brief
247          * Sets the default value if set for the option.
248          *
249          * \param[in] value  Default value to set.
250          * \throws    std::bad_alloc if out of memory.
251          *
252          * This method can be used from the derived class constructor to
253          * programmatically set a default value.
254          */
255         void setDefaultValueIfSet(const T &value);
256
257         /*! \brief
258          * Provides derived classes access to the current list of values.
259          *
260          * The non-const variant should only be used from processAll() in
261          * derived classes if necessary, and refreshValues() should be called
262          * if any changes are made.
263          */
264         ValueList       &values() { return *values_; }
265         //! Provides derived classes access to the current list of values.
266         const ValueList &values() const { return *values_; }
267
268     private:
269         /*! \brief
270          * Vector for temporary storage of values before commitSet() is called.
271          */
272         ValueList               setValues_;
273         /*! \brief
274          * Vector for primary storage of option values.
275          *
276          * Is never NULL; points either to externally provided vector, or an
277          * internally allocated one.  The allocation is performed by the
278          * constructor.
279          *
280          * Primarily, modifications to values are done only to this storage,
281          * and other storage locations are updated only when refreshValues() is
282          * called.
283          */
284         ValueList                   *values_;
285         T                           *store_;
286         int                         *countptr_;
287         boost::scoped_ptr<ValueList> ownedValues_;
288         boost::scoped_ptr<T>         defaultValueIfSet_;
289
290         // Copy and assign disallowed by base.
291 };
292
293
294 template <typename T>
295 template <class U>
296 OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U> &settings,
297                                                 OptionFlags staticFlags)
298     : AbstractOptionStorage(settings, staticFlags),
299       values_(settings.storeVector_),
300       store_(settings.store_),
301       countptr_(settings.countptr_)
302 {
303     // If the maximum number of values is not known, storage to
304     // caller-allocated memory is unsafe.
305     if (store_ != NULL && (maxValueCount() < 0 || hasFlag(efOption_MultipleTimes)))
306     {
307         GMX_THROW(APIError("Cannot set user-allocated storage for arbitrary number of values"));
308     }
309     if (values_ == NULL)
310     {
311         ownedValues_.reset(new std::vector<T>);
312         values_ = ownedValues_.get();
313     }
314     if (hasFlag(efOption_NoDefaultValue)
315         && (settings.defaultValue_ != NULL
316             || settings.defaultValueIfSet_ != NULL))
317     {
318         GMX_THROW(APIError("Option does not support default value, but one is set"));
319     }
320     if (store_ != NULL && countptr_ == NULL && !isVector()
321         && minValueCount() != maxValueCount())
322     {
323         GMX_THROW(APIError("Count storage is not set, although the number of produced values is not known"));
324     }
325     if (!hasFlag(efOption_NoDefaultValue))
326     {
327         setFlag(efOption_HasDefaultValue);
328         if (settings.defaultValue_ != NULL)
329         {
330             setDefaultValue(*settings.defaultValue_);
331         }
332         else if (ownedValues_.get() != NULL && store_ != NULL)
333         {
334             values_->clear();
335             int count = (settings.isVector() ?
336                          settings.maxValueCount_ : settings.minValueCount_);
337             for (int i = 0; i < count; ++i)
338             {
339                 values_->push_back(store_[i]);
340             }
341         }
342         if (settings.defaultValueIfSet_ != NULL)
343         {
344             setDefaultValueIfSet(*settings.defaultValueIfSet_);
345         }
346     }
347 }
348
349
350 template <typename T>
351 OptionStorageTemplate<T>::~OptionStorageTemplate()
352 {
353 }
354
355
356 template <typename T>
357 std::string OptionStorageTemplate<T>::formatValue(int i) const
358 {
359     GMX_RELEASE_ASSERT(i == DefaultValueIfSetIndex || (i >= 0 && i < valueCount()),
360                        "Invalid value index");
361     if (i == DefaultValueIfSetIndex)
362     {
363         if (defaultValueIfSet_.get() != NULL)
364         {
365             return formatSingleValue(*defaultValueIfSet_);
366         }
367         return std::string();
368     }
369     return formatSingleValue(values()[i]);
370 }
371
372
373 template <typename T>
374 void OptionStorageTemplate<T>::clearSet()
375 {
376     setValues_.clear();
377 }
378
379
380 template <typename T>
381 void OptionStorageTemplate<T>::processSet()
382 {
383     processSetValues(&setValues_);
384     if (setValues_.empty() && defaultValueIfSet_.get() != NULL)
385     {
386         addValue(*defaultValueIfSet_);
387         setFlag(efOption_HasDefaultValue);
388     }
389     else
390     {
391         clearFlag(efOption_HasDefaultValue);
392     }
393     if (!hasFlag(efOption_DontCheckMinimumCount)
394         && setValues_.size() < static_cast<size_t>(minValueCount()))
395     {
396         GMX_THROW(InvalidInputError("Too few (valid) values"));
397     }
398     commitValues();
399 }
400
401
402 template <typename T>
403 void OptionStorageTemplate<T>::addValue(const T &value)
404 {
405     if (maxValueCount() >= 0
406         && setValues_.size() >= static_cast<size_t>(maxValueCount()))
407     {
408         GMX_THROW(InvalidInputError("Too many values"));
409     }
410     setValues_.push_back(value);
411 }
412
413
414 template <typename T>
415 void OptionStorageTemplate<T>::commitValues()
416 {
417     if (hasFlag(efOption_ClearOnNextSet))
418     {
419         values_->swap(setValues_);
420     }
421     else
422     {
423         values_->insert(values_->end(), setValues_.begin(), setValues_.end());
424     }
425     clearSet();
426     refreshValues();
427 }
428
429
430 template <typename T>
431 void OptionStorageTemplate<T>::refreshValues()
432 {
433     if (countptr_ != NULL)
434     {
435         *countptr_ = static_cast<int>(values_->size());
436     }
437     if (store_ != NULL)
438     {
439         for (size_t i = 0; i < values_->size(); ++i)
440         {
441             store_[i] = (*values_)[i];
442         }
443     }
444 }
445
446
447 template <typename T>
448 void OptionStorageTemplate<T>::setDefaultValue(const T &value)
449 {
450     if (hasFlag(efOption_NoDefaultValue))
451     {
452         GMX_THROW(APIError("Option does not support default value, but one is set"));
453     }
454     if (hasFlag(efOption_HasDefaultValue))
455     {
456         setFlag(efOption_ExplicitDefaultValue);
457         clear();
458         clearSet();
459         addValue(value);
460         // TODO: As this is called from the constructor, it should not call
461         // virtual functions.
462         commitValues();
463     }
464 }
465
466
467 template <typename T>
468 void OptionStorageTemplate<T>::setDefaultValueIfSet(const T &value)
469 {
470     if (hasFlag(efOption_NoDefaultValue))
471     {
472         GMX_THROW(APIError("Option does not support default value, but one is set"));
473     }
474     if (hasFlag(efOption_MultipleTimes))
475     {
476         GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
477     }
478     setFlag(efOption_DefaultValueIfSetExists);
479     defaultValueIfSet_.reset(new T(value));
480 }
481
482 } // namespace gmx
483
484 #endif