Split off nbnxn GPU timing and staging reduction
[gromacs.git] / src / gromacs / options / optionstoragetemplate.h
blobc748bc303e6ecd61632c536dee345d680ed58ff9
1 /*
2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
35 /*! \libinternal \file
36 * \brief
37 * Defines gmx::OptionStorageTemplate template.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \inlibraryapi
41 * \ingroup module_options
43 #ifndef GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
44 #define GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
46 #include <memory>
47 #include <string>
48 #include <vector>
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"
59 #include "valueconverter.h"
61 namespace gmx
64 class Options;
66 /*! \libinternal \brief
67 * Templated base class for constructing option value storage classes.
69 * \tparam T Assignable type that stores a single option value.
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.
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).
85 * \inlibraryapi
86 * \ingroup module_options
88 template <typename T>
89 class OptionStorageTemplate : public AbstractOptionStorage
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;
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()
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.
113 virtual std::vector<std::string> defaultValuesAsStrings() const;
115 protected:
116 //! Smart pointer for managing the final storage interface.
117 typedef std::unique_ptr<IOptionValueStore<T> > StorePointer;
119 /*! \brief
120 * Initializes the storage from option settings.
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.
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.
133 * \param[in] settings Option settings.
134 * \param[in] store Final storage location.
135 * \throws APIError if invalid settings have been provided.
137 * This constructor works for cases where there is no matching
138 * OptionTemplate (e.g., EnumOption).
140 OptionStorageTemplate(const AbstractOption &settings,
141 StorePointer store);
143 //! \copydoc gmx::AbstractOptionStorage::clearSet()
144 virtual void clearSet();
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 virtual void convertValue(const Variant &value) = 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)
169 GMX_UNUSED_VALUE(values);
171 /*! \copydoc gmx::AbstractOptionStorage::processSet()
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.
180 virtual void processSet();
181 /*! \copydoc gmx::AbstractOptionStorage::processAll()
183 * The implementation in OptionStorageTemplate does nothing.
185 virtual void processAll()
188 /*! \brief
189 * Formats a single value as a string.
191 * \param[in] value Value to format.
192 * \returns \p value formatted as a string.
194 * The derived class must provide this method to format values a
195 * strings. Called by defaultValuesAsStrings() to do the actual
196 * formatting.
198 virtual std::string formatSingleValue(const T &value) const = 0;
200 /*! \brief
201 * Adds a value to a temporary storage.
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.
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.
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.
217 void addValue(const T &value);
218 /*! \brief
219 * Commits values added with addValue().
221 * \throws std::bad_alloc if out of memory.
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.
229 * See addValue() for cases where this method should be used in derived
230 * classes.
232 * Calls clearSet() if it is successful.
234 void commitValues();
236 /*! \brief
237 * Sets the default value for the option.
239 * \param[in] value Default value to set.
240 * \throws std::bad_alloc if out of memory.
242 * This method can be used from the derived class constructor to
243 * programmatically set a default value.
245 void setDefaultValue(const T &value);
246 /*! \brief
247 * Sets the default value if set for the option.
249 * \param[in] value Default value to set.
250 * \throws std::bad_alloc if out of memory.
252 * This method can be used from the derived class constructor to
253 * programmatically set a default value.
255 void setDefaultValueIfSet(const T &value);
257 /*! \brief
258 * Provides derived classes access to the current list of values.
260 * The non-const variant should only be used from processAll() in
261 * derived classes if necessary.
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(); }
267 private:
268 //! Creates the internal storage object for final values..
269 StorePointer createStore(ValueList *storeVector,
270 T *store, int *storeCount,
271 int initialCount);
273 /*! \brief
274 * Vector for temporary storage of values before commitSet() is called.
276 ValueList setValues_;
277 //! Final storage for option values.
278 const StorePointer store_;
279 // This never releases ownership.
280 std::unique_ptr<T> defaultValueIfSet_;
282 // Copy and assign disallowed by base.
286 /*! \libinternal \brief
287 * Simplified option storage template for options that have one-to-one value
288 * conversion.
290 * \tparam T Assignable type that stores a single option value.
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().
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().
302 * \inlibraryapi
303 * \ingroup module_options
305 template <typename T>
306 class OptionStorageTemplateSimple : public OptionStorageTemplate<T>
308 public:
309 //! Alias for the template class for use in base classes.
310 typedef OptionStorageTemplateSimple<T> MyBase;
312 protected:
313 //! Alias for the converter parameter type for initConverter().
314 typedef OptionValueConverterSimple<T> ConverterType;
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)
323 //! Initializes the storage.
324 OptionStorageTemplateSimple(const AbstractOption &settings,
325 typename OptionStorageTemplate<T>::StorePointer store)
326 : OptionStorageTemplate<T>(settings, std::move(store)), initialized_(false)
330 virtual std::vector<Variant>
331 normalizeValues(const std::vector<Variant> &values) const
333 const_cast<MyBase *>(this)->ensureConverterInitialized();
334 std::vector<Variant> result;
335 for (const auto &value : values)
337 result.push_back(normalizeValue(converter_.convert(value)));
339 return result;
342 /*! \brief
343 * Specifies how different types are converted.
345 * See OptionValueConverterSimple for more details.
347 virtual void initConverter(ConverterType *converter) = 0;
348 /*! \brief
349 * Post-processes a value after conversion to the output type.
351 * \param[in] value Value after conversion.
352 * \returns Value to store for the option.
354 * The default implementation only provides an identity mapping.
356 virtual T processValue(const T &value) const
358 return value;
360 /*! \brief
361 * Converts a single value to normalized type.
363 * \param[in] value Value after conversion.
364 * \returns Value to store for the option.
366 * This can be overridden to serialize a different type than `T`
367 * when using the option with KeyValueTreeObject.
369 virtual Variant normalizeValue(const T &value) const
371 return Variant::create<T>(processValue(value));
374 private:
375 virtual void convertValue(const Variant &variant)
377 ensureConverterInitialized();
378 this->addValue(processValue(converter_.convert(variant)));
380 void ensureConverterInitialized()
382 if (!initialized_)
384 initConverter(&converter_);
385 initialized_ = true;
389 ConverterType converter_;
390 bool initialized_;
394 /********************************************************************
395 * OptionStorageTemplate implementation
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_)))
408 if (hasFlag(efOption_NoDefaultValue)
409 && (settings.defaultValue_ != nullptr
410 || settings.defaultValueIfSet_ != nullptr))
412 GMX_THROW(APIError("Option does not support default value, but one is set"));
414 if (!hasFlag(efOption_NoDefaultValue))
416 setFlag(efOption_HasDefaultValue);
417 if (settings.defaultValue_ != nullptr)
419 setDefaultValue(*settings.defaultValue_);
421 if (settings.defaultValueIfSet_ != nullptr)
423 setDefaultValueIfSet(*settings.defaultValueIfSet_);
429 template <typename T>
430 OptionStorageTemplate<T>::OptionStorageTemplate(const AbstractOption &settings,
431 StorePointer store)
432 : AbstractOptionStorage(settings, OptionFlags()), store_(std::move(store))
437 template <typename T>
438 std::unique_ptr<IOptionValueStore<T> > OptionStorageTemplate<T>::createStore(
439 ValueList *storeVector, T *store, int *storeCount, int initialCount)
441 if (storeVector != nullptr)
443 GMX_RELEASE_ASSERT(store == nullptr && storeCount == nullptr,
444 "Cannot specify more than one storage location");
445 return StorePointer(new OptionValueStoreVector<T>(storeVector));
447 else if (store != nullptr)
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))
453 GMX_THROW(APIError("Cannot set user-allocated storage for arbitrary number of values"));
455 if (storeCount == nullptr && !isVector() && minValueCount() != maxValueCount())
457 GMX_THROW(APIError("Count storage is not set, although the number of produced values is not known"));
459 if (hasFlag(efOption_NoDefaultValue))
461 initialCount = 0;
463 return StorePointer(new OptionValueStorePlain<T>(store, storeCount, initialCount));
465 GMX_RELEASE_ASSERT(storeCount == nullptr,
466 "Cannot specify count storage without value storage");
467 return StorePointer(new OptionValueStoreNull<T>());
471 template <typename T>
472 std::vector<Variant> OptionStorageTemplate<T>::defaultValues() const
474 std::vector<Variant> result;
475 if (hasFlag(efOption_NoDefaultValue))
477 return result;
479 GMX_RELEASE_ASSERT(hasFlag(efOption_HasDefaultValue),
480 "Current option implementation can only provide default values before assignment");
481 for (const auto &value : values())
483 result.push_back(Variant::create<T>(value));
485 return normalizeValues(result);
489 template <typename T>
490 std::vector<std::string> OptionStorageTemplate<T>::defaultValuesAsStrings() const
492 std::vector<std::string> result;
493 if (hasFlag(efOption_NoDefaultValue))
495 return result;
497 GMX_RELEASE_ASSERT(hasFlag(efOption_HasDefaultValue),
498 "Current option implementation can only provide default values before assignment");
499 for (const auto &value : values())
501 result.push_back(formatSingleValue(value));
503 if (result.empty() || (result.size() == 1 && result[0].empty()))
505 result.clear();
506 if (defaultValueIfSet_.get() != nullptr)
508 result.push_back(formatSingleValue(*defaultValueIfSet_));
511 return result;
515 template <typename T>
516 void OptionStorageTemplate<T>::clearSet()
518 setValues_.clear();
522 template <typename T>
523 void OptionStorageTemplate<T>::processSet()
525 processSetValues(&setValues_);
526 if (setValues_.empty() && defaultValueIfSet_.get() != nullptr)
528 addValue(*defaultValueIfSet_);
529 setFlag(efOption_HasDefaultValue);
531 else
533 clearFlag(efOption_HasDefaultValue);
535 if (!hasFlag(efOption_DontCheckMinimumCount)
536 && setValues_.size() < static_cast<size_t>(minValueCount()))
538 GMX_THROW(InvalidInputError("Too few (valid) values"));
540 commitValues();
544 template <typename T>
545 void OptionStorageTemplate<T>::addValue(const T &value)
547 if (maxValueCount() >= 0
548 && setValues_.size() >= static_cast<size_t>(maxValueCount()))
550 GMX_THROW(InvalidInputError("Too many values"));
552 setValues_.push_back(value);
556 template <typename T>
557 void OptionStorageTemplate<T>::commitValues()
559 if (hasFlag(efOption_ClearOnNextSet))
561 store_->clear();
563 store_->reserve(setValues_.size());
564 for (T value : setValues_)
566 store_->append(value);
568 clearSet();
572 template <typename T>
573 void OptionStorageTemplate<T>::setDefaultValue(const T &value)
575 if (hasFlag(efOption_NoDefaultValue))
577 GMX_THROW(APIError("Option does not support default value, but one is set"));
579 if (hasFlag(efOption_HasDefaultValue))
581 setFlag(efOption_ExplicitDefaultValue);
582 store_->clear();
583 store_->append(value);
588 template <typename T>
589 void OptionStorageTemplate<T>::setDefaultValueIfSet(const T &value)
591 if (hasFlag(efOption_NoDefaultValue))
593 GMX_THROW(APIError("Option does not support default value, but one is set"));
595 if (hasFlag(efOption_MultipleTimes))
597 GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
599 setFlag(efOption_DefaultValueIfSetExists);
600 defaultValueIfSet_.reset(new T(value));
603 } // namespace gmx
605 #endif