c748bc303e6ecd61632c536dee345d680ed58ff9
[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,2015,2016,2017, 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 <memory>
47 #include <string>
48 #include <vector>
49
50 #include "gromacs/options/abstractoption.h"
51 #include "gromacs/options/abstractoptionstorage.h"
52 #include "gromacs/options/valuestore.h"
53 #include "gromacs/utility/arrayref.h"
54 #include "gromacs/utility/basedefinitions.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/gmxassert.h"
57 #include "gromacs/utility/variant.h"
58
59 #include "valueconverter.h"
60
61 namespace gmx
62 {
63
64 class Options;
65
66 /*! \libinternal \brief
67  * Templated base class for constructing option value storage classes.
68  *
69  * \tparam T Assignable type that stores a single option value.
70  *
71  * Provides an implementation of the clearSet(), valueCount(), processSet(),
72  * and defaultValuesAsStrings() methods of AbstractOptionStorage, as well as a
73  * basic no-action implementation of processAll().  Two new virtual methods are
74  * added: processSetValues() and formatSingleValue().
75  * This leaves typeString(), convertValue() and formatStringValue() to be
76  * implemented in derived classes.
77  * processSetValues() and processAll() can also be implemented if necessary.
78  *
79  * Implements transaction support for adding values within a set: all calls to
80  * addValue() add the value to a temporary storage, processSetValues() operates
81  * on this temporary storage, and commitValues() then copies these values to
82  * the real storage.  commitValues() provides a strong exception safety
83  * guarantee for the process (and it only throws if it runs out of memory).
84  *
85  * \inlibraryapi
86  * \ingroup module_options
87  */
88 template <typename T>
89 class OptionStorageTemplate : public AbstractOptionStorage
90 {
91     public:
92         //! Alias for the template class for use in base classes.
93         typedef OptionStorageTemplate<T> MyBase;
94         //! Type of the container that contains the current values.
95         typedef std::vector<T> ValueList;
96
97         // No implementation in this class for the pure virtual methods, but
98         // the declarations are still included for clarity.
99         // The various copydoc calls are needed with Doxygen 1.8.10, although
100         // things work without with 1.8.5...
101         virtual std::string typeString() const = 0;
102         //! \copydoc gmx::AbstractOptionStorage::valueCount()
103         virtual int valueCount() const { return store_->valueCount(); }
104         //! \copydoc gmx::AbstractOptionStorage::defaultValues()
105         virtual std::vector<Variant> defaultValues() const;
106         /*! \copydoc gmx::AbstractOptionStorage::defaultValuesAsStrings()
107          *
108          * OptionStorageTemplate implements handling of defaultValueIfSet()
109          * cases and composing the vector.
110          * Derived classes must implement formatSingleValue() to provide the
111          * actual formatting for a value of type \p T.
112          */
113         virtual std::vector<std::string> defaultValuesAsStrings() const;
114
115     protected:
116         //! Smart pointer for managing the final storage interface.
117         typedef std::unique_ptr<IOptionValueStore<T> > StorePointer;
118
119         /*! \brief
120          * Initializes the storage from option settings.
121          *
122          * \param[in] settings  Option settings.
123          * \param[in] staticFlags Option flags that are always set and specify
124          *      generic behavior of the option.
125          * \throws  APIError if invalid settings have been provided.
126          */
127         template <class U>
128         explicit OptionStorageTemplate(const OptionTemplate<T, U> &settings,
129                                        OptionFlags staticFlags = OptionFlags());
130         /*! \brief
131          * Initializes the storage from base option settings.
132          *
133          * \param[in] settings  Option settings.
134          * \param[in] store     Final storage location.
135          * \throws  APIError if invalid settings have been provided.
136          *
137          * This constructor works for cases where there is no matching
138          * OptionTemplate (e.g., EnumOption).
139          */
140         OptionStorageTemplate(const AbstractOption &settings,
141                               StorePointer          store);
142
143         //! \copydoc gmx::AbstractOptionStorage::clearSet()
144         virtual void clearSet();
145         /*! \copydoc gmx::AbstractOptionStorage::convertValue()
146          *
147          * Derived classes should call addValue() after they have converted
148          * \p value to the storage type.  It is allowed to call addValue()
149          * more than once, or not at all.  OptionsAssigner::appendValue()
150          * provides the same exception safety guarantee as this method, so it
151          * should be considered whether the implementation can be made strongly
152          * exception safe.
153          */
154         virtual void convertValue(const Variant &value) = 0;
155         /*! \brief
156          * Processes values for a set after all have been converted.
157          *
158          * \param[in,out] values Valid values in the set.
159          * \throws InvalidInputError if the values do not form a valid set.
160          *
161          * This method is called after all convertValue() calls for a set.
162          * \p values contains all values that were validly converted by
163          * convertValue().  The derived class may alter the values, but should
164          * in such a case ensure that a correct number of values is produced.
165          * If the derived class throws, all values in \p values are discarded.
166          */
167         virtual void processSetValues(ValueList *values)
168         {
169             GMX_UNUSED_VALUE(values);
170         }
171         /*! \copydoc gmx::AbstractOptionStorage::processSet()
172          *
173          * OptionStorageTemplate implements transaction support for a set of
174          * values in this method (see the class description), and provides a
175          * more detailed processSetValues() method that can be overridden in
176          * subclasses to process the actual values.  Derived classes should
177          * override that method instead of this one if set value processing is
178          * necessary.
179          */
180         virtual void processSet();
181         /*! \copydoc gmx::AbstractOptionStorage::processAll()
182          *
183          * The implementation in OptionStorageTemplate does nothing.
184          */
185         virtual void processAll()
186         {
187         }
188         /*! \brief
189          * Formats a single value as a string.
190          *
191          * \param[in] value  Value to format.
192          * \returns   \p value formatted as a string.
193          *
194          * The derived class must provide this method to format values a
195          * strings.  Called by defaultValuesAsStrings() to do the actual
196          * formatting.
197          */
198         virtual std::string formatSingleValue(const T &value) const = 0;
199
200         /*! \brief
201          * Adds a value to a temporary storage.
202          *
203          * \param[in] value  Value to add. A copy is made.
204          * \throws std::bad_alloc if out of memory.
205          * \throws InvalidInputError if the maximum value count has been reached.
206          *
207          * Derived classes should call this function from the convertValue()
208          * implementation to add converted values to the storage.
209          * If the maximum value count has been reached, the value is discarded
210          * and an exception is thrown.
211          *
212          * If adding values outside convertValue() (e.g., to set a custom
213          * default value), derived classes should call clearSet() before adding
214          * values (unless in the constructor) and commitValues() once all
215          * values are added.
216          */
217         void addValue(const T &value);
218         /*! \brief
219          * Commits values added with addValue().
220          *
221          * \throws std::bad_alloc if out of memory.
222          *
223          * If this function succeeds, values added with addValue() since the
224          * previous clearSet() are added to the storage for the option.
225          * Only throws in out-of-memory conditions, and provides the strong
226          * exception safety guarantee as long as the copy constructor of `T`
227          * does not throw.
228          *
229          * See addValue() for cases where this method should be used in derived
230          * classes.
231          *
232          * Calls clearSet() if it is successful.
233          */
234         void commitValues();
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.
262          */
263         ArrayRef<T>       values() { return store_->values(); }
264         //! Provides derived classes access to the current list of values.
265         ArrayRef<const T> values() const { return store_->values(); }
266
267     private:
268         //! Creates the internal storage object for final values..
269         StorePointer createStore(ValueList *storeVector,
270                                  T *store, int *storeCount,
271                                  int initialCount);
272
273         /*! \brief
274          * Vector for temporary storage of values before commitSet() is called.
275          */
276         ValueList               setValues_;
277         //! Final storage for option values.
278         const StorePointer      store_;
279         // This never releases ownership.
280         std::unique_ptr<T>      defaultValueIfSet_;
281
282         // Copy and assign disallowed by base.
283 };
284
285
286 /*! \libinternal \brief
287  * Simplified option storage template for options that have one-to-one value
288  * conversion.
289  *
290  * \tparam T Assignable type that stores a single option value.
291  *
292  * To implement an option that always map a single input value to a single
293  * output value, derive from this class instead of OptionStorageTemplate.
294  * This class implements convertValue() in terms of two new virtual methods:
295  * initConverter() and processValue().
296  *
297  * To specify how different types of values need to be converted, implement
298  * initConverter().
299  * To do common post-processing of the values after conversion, but before they
300  * are added to the underlying storage, override processValue().
301  *
302  * \inlibraryapi
303  * \ingroup module_options
304  */
305 template <typename T>
306 class OptionStorageTemplateSimple : public OptionStorageTemplate<T>
307 {
308     public:
309         //! Alias for the template class for use in base classes.
310         typedef OptionStorageTemplateSimple<T> MyBase;
311
312     protected:
313         //! Alias for the converter parameter type for initConverter().
314         typedef OptionValueConverterSimple<T> ConverterType;
315
316         //! Initializes the storage.
317         template <class U>
318         explicit OptionStorageTemplateSimple(const OptionTemplate<T, U> &settings,
319                                              OptionFlags staticFlags = OptionFlags())
320             : OptionStorageTemplate<T>(settings, staticFlags), initialized_(false)
321         {
322         }
323         //! Initializes the storage.
324         OptionStorageTemplateSimple(const AbstractOption                            &settings,
325                                     typename OptionStorageTemplate<T>::StorePointer  store)
326             : OptionStorageTemplate<T>(settings, std::move(store)), initialized_(false)
327         {
328         }
329
330         virtual std::vector<Variant>
331         normalizeValues(const std::vector<Variant> &values) const
332         {
333             const_cast<MyBase *>(this)->ensureConverterInitialized();
334             std::vector<Variant> result;
335             for (const auto &value : values)
336             {
337                 result.push_back(normalizeValue(converter_.convert(value)));
338             }
339             return result;
340         }
341
342         /*! \brief
343          * Specifies how different types are converted.
344          *
345          * See OptionValueConverterSimple for more details.
346          */
347         virtual void initConverter(ConverterType *converter) = 0;
348         /*! \brief
349          * Post-processes a value after conversion to the output type.
350          *
351          * \param[in] value  Value after conversion.
352          * \returns   Value to store for the option.
353          *
354          * The default implementation only provides an identity mapping.
355          */
356         virtual T processValue(const T &value) const
357         {
358             return value;
359         }
360         /*! \brief
361          * Converts a single value to normalized type.
362          *
363          * \param[in] value  Value after conversion.
364          * \returns   Value to store for the option.
365          *
366          * This can be overridden to serialize a different type than `T`
367          * when using the option with KeyValueTreeObject.
368          */
369         virtual Variant normalizeValue(const T &value) const
370         {
371             return Variant::create<T>(processValue(value));
372         }
373
374     private:
375         virtual void convertValue(const Variant &variant)
376         {
377             ensureConverterInitialized();
378             this->addValue(processValue(converter_.convert(variant)));
379         }
380         void ensureConverterInitialized()
381         {
382             if (!initialized_)
383             {
384                 initConverter(&converter_);
385                 initialized_ = true;
386             }
387         }
388
389         ConverterType  converter_;
390         bool           initialized_;
391 };
392
393
394 /********************************************************************
395  * OptionStorageTemplate implementation
396  */
397
398 template <typename T>
399 template <class U>
400 OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U> &settings,
401                                                 OptionFlags staticFlags)
402     : AbstractOptionStorage(settings, staticFlags),
403       store_(createStore(settings.storeVector_,
404                          settings.store_, settings.countptr_,
405                          (settings.isVector() ?
406                           settings.maxValueCount_ : settings.minValueCount_)))
407 {
408     if (hasFlag(efOption_NoDefaultValue)
409         && (settings.defaultValue_ != nullptr
410             || settings.defaultValueIfSet_ != nullptr))
411     {
412         GMX_THROW(APIError("Option does not support default value, but one is set"));
413     }
414     if (!hasFlag(efOption_NoDefaultValue))
415     {
416         setFlag(efOption_HasDefaultValue);
417         if (settings.defaultValue_ != nullptr)
418         {
419             setDefaultValue(*settings.defaultValue_);
420         }
421         if (settings.defaultValueIfSet_ != nullptr)
422         {
423             setDefaultValueIfSet(*settings.defaultValueIfSet_);
424         }
425     }
426 }
427
428
429 template <typename T>
430 OptionStorageTemplate<T>::OptionStorageTemplate(const AbstractOption &settings,
431                                                 StorePointer          store)
432     : AbstractOptionStorage(settings, OptionFlags()), store_(std::move(store))
433 {
434 }
435
436
437 template <typename T>
438 std::unique_ptr<IOptionValueStore<T> > OptionStorageTemplate<T>::createStore(
439         ValueList *storeVector, T *store, int *storeCount, int initialCount)
440 {
441     if (storeVector != nullptr)
442     {
443         GMX_RELEASE_ASSERT(store == nullptr && storeCount == nullptr,
444                            "Cannot specify more than one storage location");
445         return StorePointer(new OptionValueStoreVector<T>(storeVector));
446     }
447     else if (store != nullptr)
448     {
449         // If the maximum number of values is not known, storage to
450         // caller-allocated memory is unsafe.
451         if (maxValueCount() < 0 || hasFlag(efOption_MultipleTimes))
452         {
453             GMX_THROW(APIError("Cannot set user-allocated storage for arbitrary number of values"));
454         }
455         if (storeCount == nullptr && !isVector() && minValueCount() != maxValueCount())
456         {
457             GMX_THROW(APIError("Count storage is not set, although the number of produced values is not known"));
458         }
459         if (hasFlag(efOption_NoDefaultValue))
460         {
461             initialCount = 0;
462         }
463         return StorePointer(new OptionValueStorePlain<T>(store, storeCount, initialCount));
464     }
465     GMX_RELEASE_ASSERT(storeCount == nullptr,
466                        "Cannot specify count storage without value storage");
467     return StorePointer(new OptionValueStoreNull<T>());
468 }
469
470
471 template <typename T>
472 std::vector<Variant> OptionStorageTemplate<T>::defaultValues() const
473 {
474     std::vector<Variant> result;
475     if (hasFlag(efOption_NoDefaultValue))
476     {
477         return result;
478     }
479     GMX_RELEASE_ASSERT(hasFlag(efOption_HasDefaultValue),
480                        "Current option implementation can only provide default values before assignment");
481     for (const auto &value : values())
482     {
483         result.push_back(Variant::create<T>(value));
484     }
485     return normalizeValues(result);
486 }
487
488
489 template <typename T>
490 std::vector<std::string> OptionStorageTemplate<T>::defaultValuesAsStrings() const
491 {
492     std::vector<std::string> result;
493     if (hasFlag(efOption_NoDefaultValue))
494     {
495         return result;
496     }
497     GMX_RELEASE_ASSERT(hasFlag(efOption_HasDefaultValue),
498                        "Current option implementation can only provide default values before assignment");
499     for (const auto &value : values())
500     {
501         result.push_back(formatSingleValue(value));
502     }
503     if (result.empty() || (result.size() == 1 && result[0].empty()))
504     {
505         result.clear();
506         if (defaultValueIfSet_.get() != nullptr)
507         {
508             result.push_back(formatSingleValue(*defaultValueIfSet_));
509         }
510     }
511     return result;
512 }
513
514
515 template <typename T>
516 void OptionStorageTemplate<T>::clearSet()
517 {
518     setValues_.clear();
519 }
520
521
522 template <typename T>
523 void OptionStorageTemplate<T>::processSet()
524 {
525     processSetValues(&setValues_);
526     if (setValues_.empty() && defaultValueIfSet_.get() != nullptr)
527     {
528         addValue(*defaultValueIfSet_);
529         setFlag(efOption_HasDefaultValue);
530     }
531     else
532     {
533         clearFlag(efOption_HasDefaultValue);
534     }
535     if (!hasFlag(efOption_DontCheckMinimumCount)
536         && setValues_.size() < static_cast<size_t>(minValueCount()))
537     {
538         GMX_THROW(InvalidInputError("Too few (valid) values"));
539     }
540     commitValues();
541 }
542
543
544 template <typename T>
545 void OptionStorageTemplate<T>::addValue(const T &value)
546 {
547     if (maxValueCount() >= 0
548         && setValues_.size() >= static_cast<size_t>(maxValueCount()))
549     {
550         GMX_THROW(InvalidInputError("Too many values"));
551     }
552     setValues_.push_back(value);
553 }
554
555
556 template <typename T>
557 void OptionStorageTemplate<T>::commitValues()
558 {
559     if (hasFlag(efOption_ClearOnNextSet))
560     {
561         store_->clear();
562     }
563     store_->reserve(setValues_.size());
564     for (T value : setValues_)
565     {
566         store_->append(value);
567     }
568     clearSet();
569 }
570
571
572 template <typename T>
573 void OptionStorageTemplate<T>::setDefaultValue(const T &value)
574 {
575     if (hasFlag(efOption_NoDefaultValue))
576     {
577         GMX_THROW(APIError("Option does not support default value, but one is set"));
578     }
579     if (hasFlag(efOption_HasDefaultValue))
580     {
581         setFlag(efOption_ExplicitDefaultValue);
582         store_->clear();
583         store_->append(value);
584     }
585 }
586
587
588 template <typename T>
589 void OptionStorageTemplate<T>::setDefaultValueIfSet(const T &value)
590 {
591     if (hasFlag(efOption_NoDefaultValue))
592     {
593         GMX_THROW(APIError("Option does not support default value, but one is set"));
594     }
595     if (hasFlag(efOption_MultipleTimes))
596     {
597         GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
598     }
599     setFlag(efOption_DefaultValueIfSetExists);
600     defaultValueIfSet_.reset(new T(value));
601 }
602
603 } // namespace gmx
604
605 #endif