43a745f1c43098ff05763cafbed79cf2da7b26c6
[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,2018, 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             result.reserve(values.size());
336             for (const auto &value : values)
337             {
338                 result.push_back(normalizeValue(converter_.convert(value)));
339             }
340             return result;
341         }
342
343         /*! \brief
344          * Specifies how different types are converted.
345          *
346          * See OptionValueConverterSimple for more details.
347          */
348         virtual void initConverter(ConverterType *converter) = 0;
349         /*! \brief
350          * Post-processes a value after conversion to the output type.
351          *
352          * \param[in] value  Value after conversion.
353          * \returns   Value to store for the option.
354          *
355          * The default implementation only provides an identity mapping.
356          */
357         virtual T processValue(const T &value) const
358         {
359             return value;
360         }
361         /*! \brief
362          * Converts a single value to normalized type.
363          *
364          * \param[in] value  Value after conversion.
365          * \returns   Value to store for the option.
366          *
367          * This can be overridden to serialize a different type than `T`
368          * when using the option with KeyValueTreeObject.
369          */
370         virtual Variant normalizeValue(const T &value) const
371         {
372             return Variant::create<T>(processValue(value));
373         }
374
375     private:
376         virtual void convertValue(const Variant &variant)
377         {
378             ensureConverterInitialized();
379             this->addValue(processValue(converter_.convert(variant)));
380         }
381         void ensureConverterInitialized()
382         {
383             if (!initialized_)
384             {
385                 initConverter(&converter_);
386                 initialized_ = true;
387             }
388         }
389
390         ConverterType  converter_;
391         bool           initialized_;
392 };
393
394
395 /********************************************************************
396  * OptionStorageTemplate implementation
397  */
398
399 template <typename T>
400 template <class U>
401 OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U> &settings,
402                                                 OptionFlags staticFlags)
403     : AbstractOptionStorage(settings, staticFlags),
404       store_(createStore(settings.storeVector_,
405                          settings.store_, settings.countptr_,
406                          (settings.isVector() ?
407                           settings.maxValueCount_ : settings.minValueCount_)))
408 {
409     if (hasFlag(efOption_NoDefaultValue)
410         && (settings.defaultValue_ != nullptr
411             || settings.defaultValueIfSet_ != nullptr))
412     {
413         GMX_THROW(APIError("Option does not support default value, but one is set"));
414     }
415     if (!hasFlag(efOption_NoDefaultValue))
416     {
417         setFlag(efOption_HasDefaultValue);
418         if (settings.defaultValue_ != nullptr)
419         {
420             setDefaultValue(*settings.defaultValue_);
421         }
422         if (settings.defaultValueIfSet_ != nullptr)
423         {
424             setDefaultValueIfSet(*settings.defaultValueIfSet_);
425         }
426     }
427 }
428
429
430 template <typename T>
431 OptionStorageTemplate<T>::OptionStorageTemplate(const AbstractOption &settings,
432                                                 StorePointer          store)
433     : AbstractOptionStorage(settings, OptionFlags()), store_(std::move(store))
434 {
435 }
436
437
438 template <typename T>
439 std::unique_ptr<IOptionValueStore<T> > OptionStorageTemplate<T>::createStore(
440         ValueList *storeVector, T *store,
441         int *storeCount, // NOLINT(readability-non-const-parameter) passed non-const to OptionValueStorePlain
442         int initialCount)
443 {
444     if (storeVector != nullptr)
445     {
446         GMX_RELEASE_ASSERT(store == nullptr && storeCount == nullptr,
447                            "Cannot specify more than one storage location");
448         return StorePointer(new OptionValueStoreVector<T>(storeVector));
449     }
450     else if (store != nullptr)
451     {
452         // If the maximum number of values is not known, storage to
453         // caller-allocated memory is unsafe.
454         if (maxValueCount() < 0 || hasFlag(efOption_MultipleTimes))
455         {
456             GMX_THROW(APIError("Cannot set user-allocated storage for arbitrary number of values"));
457         }
458         if (storeCount == nullptr && !isVector() && minValueCount() != maxValueCount())
459         {
460             GMX_THROW(APIError("Count storage is not set, although the number of produced values is not known"));
461         }
462         if (hasFlag(efOption_NoDefaultValue))
463         {
464             initialCount = 0;
465         }
466         return StorePointer(new OptionValueStorePlain<T>(store, storeCount, initialCount));
467     }
468     GMX_RELEASE_ASSERT(storeCount == nullptr,
469                        "Cannot specify count storage without value storage");
470     return StorePointer(new OptionValueStoreNull<T>());
471 }
472
473
474 template <typename T>
475 std::vector<Variant> OptionStorageTemplate<T>::defaultValues() const
476 {
477     std::vector<Variant> result;
478     if (hasFlag(efOption_NoDefaultValue))
479     {
480         return result;
481     }
482     GMX_RELEASE_ASSERT(hasFlag(efOption_HasDefaultValue),
483                        "Current option implementation can only provide default values before assignment");
484     for (const auto &value : values())
485     {
486         result.push_back(Variant::create<T>(value));
487     }
488     return normalizeValues(result);
489 }
490
491
492 template <typename T>
493 std::vector<std::string> OptionStorageTemplate<T>::defaultValuesAsStrings() const
494 {
495     std::vector<std::string> result;
496     if (hasFlag(efOption_NoDefaultValue))
497     {
498         return result;
499     }
500     GMX_RELEASE_ASSERT(hasFlag(efOption_HasDefaultValue),
501                        "Current option implementation can only provide default values before assignment");
502     for (const auto &value : values())
503     {
504         result.push_back(formatSingleValue(value));
505     }
506     if (result.empty() || (result.size() == 1 && result[0].empty()))
507     {
508         result.clear();
509         if (defaultValueIfSet_ != nullptr)
510         {
511             result.push_back(formatSingleValue(*defaultValueIfSet_));
512         }
513     }
514     return result;
515 }
516
517
518 template <typename T>
519 void OptionStorageTemplate<T>::clearSet()
520 {
521     setValues_.clear();
522 }
523
524
525 template <typename T>
526 void OptionStorageTemplate<T>::processSet()
527 {
528     processSetValues(&setValues_);
529     if (setValues_.empty() && defaultValueIfSet_ != nullptr)
530     {
531         addValue(*defaultValueIfSet_);
532         setFlag(efOption_HasDefaultValue);
533     }
534     else
535     {
536         clearFlag(efOption_HasDefaultValue);
537     }
538     if (!hasFlag(efOption_DontCheckMinimumCount)
539         && setValues_.size() < static_cast<size_t>(minValueCount()))
540     {
541         GMX_THROW(InvalidInputError("Too few (valid) values"));
542     }
543     commitValues();
544 }
545
546
547 template <typename T>
548 void OptionStorageTemplate<T>::addValue(const T &value)
549 {
550     if (maxValueCount() >= 0
551         && setValues_.size() >= static_cast<size_t>(maxValueCount()))
552     {
553         GMX_THROW(InvalidInputError("Too many values"));
554     }
555     setValues_.push_back(value);
556 }
557
558 template <typename T>
559 void OptionStorageTemplate<T>::commitValues()
560 {
561     if (hasFlag(efOption_ClearOnNextSet))
562     {
563         store_->clear();
564     }
565     store_->reserve(setValues_.size());
566     // For bool the loop variable isn't a reference (it's its special reference type)
567     CLANG_DIAGNOSTIC_IGNORE(-Wrange-loop-analysis)
568     for (const auto &value : setValues_)
569     {
570         store_->append(value);
571     }
572     DIAGNOSTIC_RESET;
573     clearSet();
574 }
575
576 template <typename T>
577 void OptionStorageTemplate<T>::setDefaultValue(const T &value)
578 {
579     if (hasFlag(efOption_NoDefaultValue))
580     {
581         GMX_THROW(APIError("Option does not support default value, but one is set"));
582     }
583     if (hasFlag(efOption_HasDefaultValue))
584     {
585         setFlag(efOption_ExplicitDefaultValue);
586         store_->clear();
587         store_->append(value);
588     }
589 }
590
591
592 template <typename T>
593 void OptionStorageTemplate<T>::setDefaultValueIfSet(const T &value)
594 {
595     if (hasFlag(efOption_NoDefaultValue))
596     {
597         GMX_THROW(APIError("Option does not support default value, but one is set"));
598     }
599     if (hasFlag(efOption_MultipleTimes))
600     {
601         GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
602     }
603     setFlag(efOption_DefaultValueIfSetExists);
604     defaultValueIfSet_ = compat::make_unique<T>(value);
605 }
606
607 } // namespace gmx
608
609 #endif