Merge branch 'origin/release-2020' into master
[gromacs.git] / src / gromacs / options / optionstoragetemplate.h
blob0ec97a877cd36ced66847a4d3f98229c446c88bd
1 /*
2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
36 /*! \libinternal \file
37 * \brief
38 * Defines gmx::OptionStorageTemplate template.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \inlibraryapi
42 * \ingroup module_options
44 #ifndef GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
45 #define GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
47 #include <memory>
48 #include <string>
49 #include <vector>
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"
59 #include "valueconverter.h"
60 #include "valuestore.h"
62 namespace gmx
65 class Options;
67 /*! \libinternal \brief
68 * Templated base class for constructing option value storage classes.
70 * \tparam T Assignable type that stores a single option value.
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.
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).
86 * \inlibraryapi
87 * \ingroup module_options
89 template<typename T>
90 class OptionStorageTemplate : public AbstractOptionStorage
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;
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()
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.
114 std::vector<std::string> defaultValuesAsStrings() const override;
116 protected:
117 //! Smart pointer for managing the final storage interface.
118 typedef std::unique_ptr<IOptionValueStore<T>> StorePointer;
120 /*! \brief
121 * Initializes the storage from option settings.
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.
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.
134 * \param[in] settings Option settings.
135 * \param[in] store Final storage location.
136 * \throws APIError if invalid settings have been provided.
138 * This constructor works for cases where there is no matching
139 * OptionTemplate (e.g., EnumOption).
141 OptionStorageTemplate(const AbstractOption& settings, StorePointer store);
143 //! \copydoc gmx::AbstractOptionStorage::clearSet()
144 void clearSet() override;
145 /*! \copydoc gmx::AbstractOptionStorage::convertValue()
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.
154 void convertValue(const Any& value) override = 0;
155 /*! \brief
156 * Processes values for a set after all have been converted.
158 * \param[in,out] values Valid values in the set.
159 * \throws InvalidInputError if the values do not form a valid set.
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.
167 virtual void processSetValues(ValueList* values) { GMX_UNUSED_VALUE(values); }
168 /*! \copydoc gmx::AbstractOptionStorage::processSet()
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.
177 void processSet() override;
178 /*! \copydoc gmx::AbstractOptionStorage::processAll()
180 * The implementation in OptionStorageTemplate does nothing.
182 void processAll() override {}
183 /*! \brief
184 * Formats a single value as a string.
186 * \param[in] value Value to format.
187 * \returns \p value formatted as a string.
189 * The derived class must provide this method to format values a
190 * strings. Called by defaultValuesAsStrings() to do the actual
191 * formatting.
193 virtual std::string formatSingleValue(const T& value) const = 0;
195 /*! \brief
196 * Adds a value to a temporary storage.
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.
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.
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.
212 void addValue(const T& value);
213 /*! \brief
214 * Commits values added with addValue().
216 * \throws std::bad_alloc if out of memory.
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.
224 * See addValue() for cases where this method should be used in derived
225 * classes.
227 * Calls clearSet() if it is successful.
229 void commitValues();
231 /*! \brief
232 * Sets the default value for the option.
234 * \param[in] value Default value to set.
235 * \throws std::bad_alloc if out of memory.
237 * This method can be used from the derived class constructor to
238 * programmatically set a default value.
240 void setDefaultValue(const T& value);
241 /*! \brief
242 * Sets the default value if set for the option.
244 * \param[in] value Default value to set.
245 * \throws std::bad_alloc if out of memory.
247 * This method can be used from the derived class constructor to
248 * programmatically set a default value.
250 void setDefaultValueIfSet(const T& value);
252 /*! \brief
253 * Provides derived classes access to the current list of values.
255 * The non-const any should only be used from processAll() in
256 * derived classes if necessary.
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(); }
262 private:
263 //! Creates the internal storage object for final values..
264 StorePointer createStore(ValueList* storeVector, T* store, int* storeCount, int initialCount);
266 /*! \brief
267 * Vector for temporary storage of values before commitSet() is called.
269 ValueList setValues_;
270 //! Final storage for option values.
271 const StorePointer store_;
272 // This never releases ownership.
273 std::unique_ptr<T> defaultValueIfSet_;
275 // Copy and assign disallowed by base.
279 /*! \libinternal \brief
280 * Simplified option storage template for options that have one-to-one value
281 * conversion.
283 * \tparam T Assignable type that stores a single option value.
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().
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().
295 * \inlibraryapi
296 * \ingroup module_options
298 template<typename T>
299 class OptionStorageTemplateSimple : public OptionStorageTemplate<T>
301 public:
302 //! Alias for the template class for use in base classes.
303 typedef OptionStorageTemplateSimple<T> MyBase;
305 protected:
306 //! Alias for the converter parameter type for initConverter().
307 typedef OptionValueConverterSimple<T> ConverterType;
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)
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)
325 std::vector<Any> normalizeValues(const std::vector<Any>& values) const override
327 const_cast<MyBase*>(this)->ensureConverterInitialized();
328 std::vector<Any> result;
329 result.reserve(values.size());
330 for (const auto& value : values)
332 result.push_back(normalizeValue(converter_.convert(value)));
334 return result;
337 /*! \brief
338 * Specifies how different types are converted.
340 * See OptionValueConverterSimple for more details.
342 virtual void initConverter(ConverterType* converter) = 0;
343 /*! \brief
344 * Post-processes a value after conversion to the output type.
346 * \param[in] value Value after conversion.
347 * \returns Value to store for the option.
349 * The default implementation only provides an identity mapping.
351 virtual T processValue(const T& value) const { return value; }
352 /*! \brief
353 * Converts a single value to normalized type.
355 * \param[in] value Value after conversion.
356 * \returns Value to store for the option.
358 * This can be overridden to serialize a different type than `T`
359 * when using the option with KeyValueTreeObject.
361 virtual Any normalizeValue(const T& value) const { return Any::create<T>(processValue(value)); }
363 private:
364 void convertValue(const Any& any) override
366 ensureConverterInitialized();
367 this->addValue(processValue(converter_.convert(any)));
369 void ensureConverterInitialized()
371 if (!initialized_)
373 initConverter(&converter_);
374 initialized_ = true;
378 ConverterType converter_;
379 bool initialized_;
383 /********************************************************************
384 * OptionStorageTemplate implementation
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_)))
397 if (hasFlag(efOption_NoDefaultValue)
398 && (settings.defaultValue_ != nullptr || settings.defaultValueIfSet_ != nullptr))
400 GMX_THROW(APIError("Option does not support default value, but one is set"));
402 if (!hasFlag(efOption_NoDefaultValue))
404 setFlag(efOption_HasDefaultValue);
405 if (settings.defaultValue_ != nullptr)
407 setDefaultValue(*settings.defaultValue_);
409 if (settings.defaultValueIfSet_ != nullptr)
411 setDefaultValueIfSet(*settings.defaultValueIfSet_);
417 template<typename T>
418 OptionStorageTemplate<T>::OptionStorageTemplate(const AbstractOption& settings, StorePointer store) :
419 AbstractOptionStorage(settings, OptionFlags()),
420 store_(std::move(store))
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)
431 if (storeVector != nullptr)
433 GMX_RELEASE_ASSERT(store == nullptr && storeCount == nullptr,
434 "Cannot specify more than one storage location");
435 return StorePointer(new OptionValueStoreVector<T>(storeVector));
437 else if (store != nullptr)
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))
443 GMX_THROW(APIError("Cannot set user-allocated storage for arbitrary number of values"));
445 if (storeCount == nullptr && !isVector() && minValueCount() != maxValueCount())
447 GMX_THROW(
448 APIError("Count storage is not set, although the number of produced values is "
449 "not known"));
451 if (hasFlag(efOption_NoDefaultValue))
453 initialCount = 0;
455 return StorePointer(new OptionValueStorePlain<T>(store, storeCount, initialCount));
457 GMX_RELEASE_ASSERT(storeCount == nullptr, "Cannot specify count storage without value storage");
458 return StorePointer(new OptionValueStoreNull<T>());
462 template<typename T>
463 std::vector<Any> OptionStorageTemplate<T>::defaultValues() const
465 std::vector<Any> result;
466 if (hasFlag(efOption_NoDefaultValue))
468 return result;
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())
475 result.push_back(Any::create<T>(value));
477 return normalizeValues(result);
481 template<typename T>
482 std::vector<std::string> OptionStorageTemplate<T>::defaultValuesAsStrings() const
484 std::vector<std::string> result;
485 if (hasFlag(efOption_NoDefaultValue))
487 return result;
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())
494 result.push_back(formatSingleValue(value));
496 if (result.empty() || (result.size() == 1 && result[0].empty()))
498 result.clear();
499 if (defaultValueIfSet_ != nullptr)
501 result.push_back(formatSingleValue(*defaultValueIfSet_));
504 return result;
508 template<typename T>
509 void OptionStorageTemplate<T>::clearSet()
511 setValues_.clear();
515 template<typename T>
516 void OptionStorageTemplate<T>::processSet()
518 processSetValues(&setValues_);
519 if (setValues_.empty() && defaultValueIfSet_ != nullptr)
521 addValue(*defaultValueIfSet_);
522 setFlag(efOption_HasDefaultValue);
524 else
526 clearFlag(efOption_HasDefaultValue);
528 if (!hasFlag(efOption_DontCheckMinimumCount)
529 && setValues_.size() < static_cast<size_t>(minValueCount()))
531 GMX_THROW(InvalidInputError("Too few (valid) values"));
533 commitValues();
537 template<typename T>
538 void OptionStorageTemplate<T>::addValue(const T& value)
540 if (maxValueCount() >= 0 && setValues_.size() >= static_cast<size_t>(maxValueCount()))
542 GMX_THROW(InvalidInputError("Too many values"));
544 setValues_.push_back(value);
547 template<typename T>
548 void OptionStorageTemplate<T>::commitValues()
550 if (hasFlag(efOption_ClearOnNextSet))
552 store_->clear();
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_)
561 store_->append(value);
563 CLANG_DIAGNOSTIC_RESET;
564 clearSet();
567 template<typename T>
568 void OptionStorageTemplate<T>::setDefaultValue(const T& value)
570 if (hasFlag(efOption_NoDefaultValue))
572 GMX_THROW(APIError("Option does not support default value, but one is set"));
574 if (hasFlag(efOption_HasDefaultValue))
576 setFlag(efOption_ExplicitDefaultValue);
577 store_->clear();
578 store_->append(value);
583 template<typename T>
584 void OptionStorageTemplate<T>::setDefaultValueIfSet(const T& value)
586 if (hasFlag(efOption_NoDefaultValue))
588 GMX_THROW(APIError("Option does not support default value, but one is set"));
590 if (hasFlag(efOption_MultipleTimes))
592 GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
594 setFlag(efOption_DefaultValueIfSetExists);
595 defaultValueIfSet_ = std::make_unique<T>(value);
598 } // namespace gmx
600 #endif