Merge branch 'origin/release-2020' into master
[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-2018, The GROMACS development team.
5  * Copyright (c) 2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \libinternal \file
37  * \brief
38  * Defines gmx::OptionStorageTemplate template.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \inlibraryapi
42  * \ingroup module_options
43  */
44 #ifndef GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
45 #define GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
46
47 #include <memory>
48 #include <string>
49 #include <vector>
50
51 #include "gromacs/options/abstractoption.h"
52 #include "gromacs/options/abstractoptionstorage.h"
53 #include "gromacs/utility/any.h"
54 #include "gromacs/utility/arrayref.h"
55 #include "gromacs/utility/basedefinitions.h"
56 #include "gromacs/utility/exceptions.h"
57 #include "gromacs/utility/gmxassert.h"
58
59 #include "valueconverter.h"
60 #include "valuestore.h"
61
62 namespace gmx
63 {
64
65 class Options;
66
67 /*! \libinternal \brief
68  * Templated base class for constructing option value storage classes.
69  *
70  * \tparam T Assignable type that stores a single option value.
71  *
72  * Provides an implementation of the clearSet(), valueCount(), processSet(),
73  * and defaultValuesAsStrings() methods of AbstractOptionStorage, as well as a
74  * basic no-action implementation of processAll().  Two new virtual methods are
75  * added: processSetValues() and formatSingleValue().
76  * This leaves typeString(), convertValue() and formatStringValue() to be
77  * implemented in derived classes.
78  * processSetValues() and processAll() can also be implemented if necessary.
79  *
80  * Implements transaction support for adding values within a set: all calls to
81  * addValue() add the value to a temporary storage, processSetValues() operates
82  * on this temporary storage, and commitValues() then copies these values to
83  * the real storage.  commitValues() provides a strong exception safety
84  * guarantee for the process (and it only throws if it runs out of memory).
85  *
86  * \inlibraryapi
87  * \ingroup module_options
88  */
89 template<typename T>
90 class OptionStorageTemplate : public AbstractOptionStorage
91 {
92 public:
93     //! Alias for the template class for use in base classes.
94     typedef OptionStorageTemplate<T> MyBase;
95     //! Type of the container that contains the current values.
96     typedef std::vector<T> ValueList;
97
98     // No implementation in this class for the pure virtual methods, but
99     // the declarations are still included for clarity.
100     // The various copydoc calls are needed with Doxygen 1.8.10, although
101     // things work without with 1.8.5...
102     std::string typeString() const override = 0;
103     //! \copydoc gmx::AbstractOptionStorage::valueCount()
104     int valueCount() const override { return store_->valueCount(); }
105     //! \copydoc gmx::AbstractOptionStorage::defaultValues()
106     std::vector<Any> defaultValues() const override;
107     /*! \copydoc gmx::AbstractOptionStorage::defaultValuesAsStrings()
108      *
109      * OptionStorageTemplate implements handling of defaultValueIfSet()
110      * cases and composing the vector.
111      * Derived classes must implement formatSingleValue() to provide the
112      * actual formatting for a value of type \p T.
113      */
114     std::vector<std::string> defaultValuesAsStrings() const override;
115
116 protected:
117     //! Smart pointer for managing the final storage interface.
118     typedef std::unique_ptr<IOptionValueStore<T>> StorePointer;
119
120     /*! \brief
121      * Initializes the storage from option settings.
122      *
123      * \param[in] settings  Option settings.
124      * \param[in] staticFlags Option flags that are always set and specify
125      *      generic behavior of the option.
126      * \throws  APIError if invalid settings have been provided.
127      */
128     template<class U>
129     explicit OptionStorageTemplate(const OptionTemplate<T, U>& settings,
130                                    OptionFlags                 staticFlags = OptionFlags());
131     /*! \brief
132      * Initializes the storage from base option settings.
133      *
134      * \param[in] settings  Option settings.
135      * \param[in] store     Final storage location.
136      * \throws  APIError if invalid settings have been provided.
137      *
138      * This constructor works for cases where there is no matching
139      * OptionTemplate (e.g., EnumOption).
140      */
141     OptionStorageTemplate(const AbstractOption& settings, StorePointer store);
142
143     //! \copydoc gmx::AbstractOptionStorage::clearSet()
144     void clearSet() override;
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     void convertValue(const Any& value) override = 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) { GMX_UNUSED_VALUE(values); }
168     /*! \copydoc gmx::AbstractOptionStorage::processSet()
169      *
170      * OptionStorageTemplate implements transaction support for a set of
171      * values in this method (see the class description), and provides a
172      * more detailed processSetValues() method that can be overridden in
173      * subclasses to process the actual values.  Derived classes should
174      * override that method instead of this one if set value processing is
175      * necessary.
176      */
177     void processSet() override;
178     /*! \copydoc gmx::AbstractOptionStorage::processAll()
179      *
180      * The implementation in OptionStorageTemplate does nothing.
181      */
182     void processAll() override {}
183     /*! \brief
184      * Formats a single value as a string.
185      *
186      * \param[in] value  Value to format.
187      * \returns   \p value formatted as a string.
188      *
189      * The derived class must provide this method to format values a
190      * strings.  Called by defaultValuesAsStrings() to do the actual
191      * formatting.
192      */
193     virtual std::string formatSingleValue(const T& value) const = 0;
194
195     /*! \brief
196      * Adds a value to a temporary storage.
197      *
198      * \param[in] value  Value to add. A copy is made.
199      * \throws std::bad_alloc if out of memory.
200      * \throws InvalidInputError if the maximum value count has been reached.
201      *
202      * Derived classes should call this function from the convertValue()
203      * implementation to add converted values to the storage.
204      * If the maximum value count has been reached, the value is discarded
205      * and an exception is thrown.
206      *
207      * If adding values outside convertValue() (e.g., to set a custom
208      * default value), derived classes should call clearSet() before adding
209      * values (unless in the constructor) and commitValues() once all
210      * values are added.
211      */
212     void addValue(const T& value);
213     /*! \brief
214      * Commits values added with addValue().
215      *
216      * \throws std::bad_alloc if out of memory.
217      *
218      * If this function succeeds, values added with addValue() since the
219      * previous clearSet() are added to the storage for the option.
220      * Only throws in out-of-memory conditions, and provides the strong
221      * exception safety guarantee as long as the copy constructor of `T`
222      * does not throw.
223      *
224      * See addValue() for cases where this method should be used in derived
225      * classes.
226      *
227      * Calls clearSet() if it is successful.
228      */
229     void commitValues();
230
231     /*! \brief
232      * Sets the default value for the option.
233      *
234      * \param[in] value  Default value to set.
235      * \throws    std::bad_alloc if out of memory.
236      *
237      * This method can be used from the derived class constructor to
238      * programmatically set a default value.
239      */
240     void setDefaultValue(const T& value);
241     /*! \brief
242      * Sets the default value if set for the option.
243      *
244      * \param[in] value  Default value to set.
245      * \throws    std::bad_alloc if out of memory.
246      *
247      * This method can be used from the derived class constructor to
248      * programmatically set a default value.
249      */
250     void setDefaultValueIfSet(const T& value);
251
252     /*! \brief
253      * Provides derived classes access to the current list of values.
254      *
255      * The non-const any should only be used from processAll() in
256      * derived classes if necessary.
257      */
258     ArrayRef<T> values() { return store_->values(); }
259     //! Provides derived classes access to the current list of values.
260     ArrayRef<const T> values() const { return store_->values(); }
261
262 private:
263     //! Creates the internal storage object for final values..
264     StorePointer createStore(ValueList* storeVector, T* store, int* storeCount, int initialCount);
265
266     /*! \brief
267      * Vector for temporary storage of values before commitSet() is called.
268      */
269     ValueList setValues_;
270     //! Final storage for option values.
271     const StorePointer store_;
272     // This never releases ownership.
273     std::unique_ptr<T> defaultValueIfSet_;
274
275     // Copy and assign disallowed by base.
276 };
277
278
279 /*! \libinternal \brief
280  * Simplified option storage template for options that have one-to-one value
281  * conversion.
282  *
283  * \tparam T Assignable type that stores a single option value.
284  *
285  * To implement an option that always map a single input value to a single
286  * output value, derive from this class instead of OptionStorageTemplate.
287  * This class implements convertValue() in terms of two new virtual methods:
288  * initConverter() and processValue().
289  *
290  * To specify how different types of values need to be converted, implement
291  * initConverter().
292  * To do common post-processing of the values after conversion, but before they
293  * are added to the underlying storage, override processValue().
294  *
295  * \inlibraryapi
296  * \ingroup module_options
297  */
298 template<typename T>
299 class OptionStorageTemplateSimple : public OptionStorageTemplate<T>
300 {
301 public:
302     //! Alias for the template class for use in base classes.
303     typedef OptionStorageTemplateSimple<T> MyBase;
304
305 protected:
306     //! Alias for the converter parameter type for initConverter().
307     typedef OptionValueConverterSimple<T> ConverterType;
308
309     //! Initializes the storage.
310     template<class U>
311     explicit OptionStorageTemplateSimple(const OptionTemplate<T, U>& settings,
312                                          OptionFlags                 staticFlags = OptionFlags()) :
313         OptionStorageTemplate<T>(settings, staticFlags),
314         initialized_(false)
315     {
316     }
317     //! Initializes the storage.
318     OptionStorageTemplateSimple(const AbstractOption&                           settings,
319                                 typename OptionStorageTemplate<T>::StorePointer store) :
320         OptionStorageTemplate<T>(settings, std::move(store)),
321         initialized_(false)
322     {
323     }
324
325     std::vector<Any> normalizeValues(const std::vector<Any>& values) const override
326     {
327         const_cast<MyBase*>(this)->ensureConverterInitialized();
328         std::vector<Any> result;
329         result.reserve(values.size());
330         for (const auto& value : values)
331         {
332             result.push_back(normalizeValue(converter_.convert(value)));
333         }
334         return result;
335     }
336
337     /*! \brief
338      * Specifies how different types are converted.
339      *
340      * See OptionValueConverterSimple for more details.
341      */
342     virtual void initConverter(ConverterType* converter) = 0;
343     /*! \brief
344      * Post-processes a value after conversion to the output type.
345      *
346      * \param[in] value  Value after conversion.
347      * \returns   Value to store for the option.
348      *
349      * The default implementation only provides an identity mapping.
350      */
351     virtual T processValue(const T& value) const { return value; }
352     /*! \brief
353      * Converts a single value to normalized type.
354      *
355      * \param[in] value  Value after conversion.
356      * \returns   Value to store for the option.
357      *
358      * This can be overridden to serialize a different type than `T`
359      * when using the option with KeyValueTreeObject.
360      */
361     virtual Any normalizeValue(const T& value) const { return Any::create<T>(processValue(value)); }
362
363 private:
364     void convertValue(const Any& any) override
365     {
366         ensureConverterInitialized();
367         this->addValue(processValue(converter_.convert(any)));
368     }
369     void ensureConverterInitialized()
370     {
371         if (!initialized_)
372         {
373             initConverter(&converter_);
374             initialized_ = true;
375         }
376     }
377
378     ConverterType converter_;
379     bool          initialized_;
380 };
381
382
383 /********************************************************************
384  * OptionStorageTemplate implementation
385  */
386
387 template<typename T>
388 template<class U>
389 OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U>& settings,
390                                                 OptionFlags                 staticFlags) :
391     AbstractOptionStorage(settings, staticFlags),
392     store_(createStore(settings.storeVector_,
393                        settings.store_,
394                        settings.countptr_,
395                        (settings.isVector() ? settings.maxValueCount_ : settings.minValueCount_)))
396 {
397     if (hasFlag(efOption_NoDefaultValue)
398         && (settings.defaultValue_ != nullptr || settings.defaultValueIfSet_ != nullptr))
399     {
400         GMX_THROW(APIError("Option does not support default value, but one is set"));
401     }
402     if (!hasFlag(efOption_NoDefaultValue))
403     {
404         setFlag(efOption_HasDefaultValue);
405         if (settings.defaultValue_ != nullptr)
406         {
407             setDefaultValue(*settings.defaultValue_);
408         }
409         if (settings.defaultValueIfSet_ != nullptr)
410         {
411             setDefaultValueIfSet(*settings.defaultValueIfSet_);
412         }
413     }
414 }
415
416
417 template<typename T>
418 OptionStorageTemplate<T>::OptionStorageTemplate(const AbstractOption& settings, StorePointer store) :
419     AbstractOptionStorage(settings, OptionFlags()),
420     store_(std::move(store))
421 {
422 }
423
424
425 template<typename T>
426 std::unique_ptr<IOptionValueStore<T>> OptionStorageTemplate<T>::createStore(ValueList* storeVector,
427                                                                             T*         store,
428                                                                             int* storeCount, // NOLINT(readability-non-const-parameter) passed non-const to OptionValueStorePlain
429                                                                             int initialCount)
430 {
431     if (storeVector != nullptr)
432     {
433         GMX_RELEASE_ASSERT(store == nullptr && storeCount == nullptr,
434                            "Cannot specify more than one storage location");
435         return StorePointer(new OptionValueStoreVector<T>(storeVector));
436     }
437     else if (store != nullptr)
438     {
439         // If the maximum number of values is not known, storage to
440         // caller-allocated memory is unsafe.
441         if (maxValueCount() < 0 || hasFlag(efOption_MultipleTimes))
442         {
443             GMX_THROW(APIError("Cannot set user-allocated storage for arbitrary number of values"));
444         }
445         if (storeCount == nullptr && !isVector() && minValueCount() != maxValueCount())
446         {
447             GMX_THROW(
448                     APIError("Count storage is not set, although the number of produced values is "
449                              "not known"));
450         }
451         if (hasFlag(efOption_NoDefaultValue))
452         {
453             initialCount = 0;
454         }
455         return StorePointer(new OptionValueStorePlain<T>(store, storeCount, initialCount));
456     }
457     GMX_RELEASE_ASSERT(storeCount == nullptr, "Cannot specify count storage without value storage");
458     return StorePointer(new OptionValueStoreNull<T>());
459 }
460
461
462 template<typename T>
463 std::vector<Any> OptionStorageTemplate<T>::defaultValues() const
464 {
465     std::vector<Any> result;
466     if (hasFlag(efOption_NoDefaultValue))
467     {
468         return result;
469     }
470     GMX_RELEASE_ASSERT(
471             hasFlag(efOption_HasDefaultValue),
472             "Current option implementation can only provide default values before assignment");
473     for (const auto& value : values())
474     {
475         result.push_back(Any::create<T>(value));
476     }
477     return normalizeValues(result);
478 }
479
480
481 template<typename T>
482 std::vector<std::string> OptionStorageTemplate<T>::defaultValuesAsStrings() const
483 {
484     std::vector<std::string> result;
485     if (hasFlag(efOption_NoDefaultValue))
486     {
487         return result;
488     }
489     GMX_RELEASE_ASSERT(
490             hasFlag(efOption_HasDefaultValue),
491             "Current option implementation can only provide default values before assignment");
492     for (const auto& value : values())
493     {
494         result.push_back(formatSingleValue(value));
495     }
496     if (result.empty() || (result.size() == 1 && result[0].empty()))
497     {
498         result.clear();
499         if (defaultValueIfSet_ != nullptr)
500         {
501             result.push_back(formatSingleValue(*defaultValueIfSet_));
502         }
503     }
504     return result;
505 }
506
507
508 template<typename T>
509 void OptionStorageTemplate<T>::clearSet()
510 {
511     setValues_.clear();
512 }
513
514
515 template<typename T>
516 void OptionStorageTemplate<T>::processSet()
517 {
518     processSetValues(&setValues_);
519     if (setValues_.empty() && defaultValueIfSet_ != nullptr)
520     {
521         addValue(*defaultValueIfSet_);
522         setFlag(efOption_HasDefaultValue);
523     }
524     else
525     {
526         clearFlag(efOption_HasDefaultValue);
527     }
528     if (!hasFlag(efOption_DontCheckMinimumCount)
529         && setValues_.size() < static_cast<size_t>(minValueCount()))
530     {
531         GMX_THROW(InvalidInputError("Too few (valid) values"));
532     }
533     commitValues();
534 }
535
536
537 template<typename T>
538 void OptionStorageTemplate<T>::addValue(const T& value)
539 {
540     if (maxValueCount() >= 0 && setValues_.size() >= static_cast<size_t>(maxValueCount()))
541     {
542         GMX_THROW(InvalidInputError("Too many values"));
543     }
544     setValues_.push_back(value);
545 }
546
547 template<typename T>
548 void OptionStorageTemplate<T>::commitValues()
549 {
550     if (hasFlag(efOption_ClearOnNextSet))
551     {
552         store_->clear();
553     }
554     store_->reserve(setValues_.size());
555     // For bool the loop variable isn't a reference (it's its special reference type)
556     // clang-format off
557     CLANG_DIAGNOSTIC_IGNORE(-Wrange-loop-analysis);
558     // clang-format on
559     for (const auto& value : setValues_)
560     {
561         store_->append(value);
562     }
563     CLANG_DIAGNOSTIC_RESET;
564     clearSet();
565 }
566
567 template<typename T>
568 void OptionStorageTemplate<T>::setDefaultValue(const T& value)
569 {
570     if (hasFlag(efOption_NoDefaultValue))
571     {
572         GMX_THROW(APIError("Option does not support default value, but one is set"));
573     }
574     if (hasFlag(efOption_HasDefaultValue))
575     {
576         setFlag(efOption_ExplicitDefaultValue);
577         store_->clear();
578         store_->append(value);
579     }
580 }
581
582
583 template<typename T>
584 void OptionStorageTemplate<T>::setDefaultValueIfSet(const T& value)
585 {
586     if (hasFlag(efOption_NoDefaultValue))
587     {
588         GMX_THROW(APIError("Option does not support default value, but one is set"));
589     }
590     if (hasFlag(efOption_MultipleTimes))
591     {
592         GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
593     }
594     setFlag(efOption_DefaultValueIfSetExists);
595     defaultValueIfSet_ = std::make_unique<T>(value);
596 }
597
598 } // namespace gmx
599
600 #endif