Remove remaining scoped_ptrs
[gromacs.git] / src / gromacs / options / optionstoragetemplate.h
blob324731646633e9327b33a000df8368f3b5929312
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014,2015, 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/utility/basedefinitions.h"
53 #include "gromacs/utility/exceptions.h"
54 #include "gromacs/utility/gmxassert.h"
56 namespace gmx
59 class Options;
61 /*! \libinternal \brief
62 * Templated base class for constructing option value storage classes.
64 * \tparam T Assignable type that stores a single option value.
66 * Provides an implementation of the clearSet(), valueCount(), and processSet()
67 * methods of AbstractOptionStorage, as well as a basic no-action
68 * implementation of processAll(). Two new virtual methods are added:
69 * processSetValues() and refreshValues(). The default implementation of
70 * processSetValues() does nothing, and refreshValues() is used to update
71 * secondary storage after values have been added/changed.
72 * This leaves typeString(), formatValue(), and convertValue() to be
73 * implemented in derived classes. processSetValues() and processAll() can
74 * also be implemented if necessary.
76 * Implements transaction support for adding values within a set: all calls to
77 * addValue() add the value to a temporary storage, processSetValues() operates
78 * on this temporary storage, and commitValues() then copies these values to
79 * the real storage. commitValues() provides a strong exception safety
80 * guarantee for the process (and it only throws if it runs out of memory).
82 * \inlibraryapi
83 * \ingroup module_options
85 template <typename T>
86 class OptionStorageTemplate : public AbstractOptionStorage
88 public:
89 //! Alias for the template class for use in base classes.
90 typedef OptionStorageTemplate<T> MyBase;
91 //! Type of the container that contains the current values.
92 typedef std::vector<T> ValueList;
94 virtual ~OptionStorageTemplate();
96 // No implementation in this class for the pure virtual methods, but
97 // the declarations are still included for clarity.
98 virtual std::string typeString() const = 0;
99 virtual int valueCount() const { return static_cast<int>(values_->size()); }
100 /*! \copydoc gmx::AbstractOptionStorage::formatValue()
102 * OptionStorageTemplate implements handling of DefaultValueIfSetIndex
103 * in this method, as well as checking that \p i is a valid index.
104 * Derived classes must implement formatSingleValue() to provide the
105 * actual formatting for a value of type \p T.
107 virtual std::string formatValue(int i) const;
109 protected:
110 /*! \brief
111 * Initializes the storage from option settings.
113 * \param[in] settings Option settings.
114 * \param[in] staticFlags Option flags that are always set and specify
115 * generic behavior of the option.
116 * \throws APIError if invalid settings have been provided.
118 template <class U>
119 explicit OptionStorageTemplate(const OptionTemplate<T, U> &settings,
120 OptionFlags staticFlags = OptionFlags());
123 virtual void clearSet();
124 /*! \copydoc gmx::AbstractOptionStorage::convertValue()
126 * Derived classes should call addValue() after they have converted
127 * \p value to the storage type. It is allowed to call addValue()
128 * more than once, or not at all. OptionsAssigner::appendValue()
129 * provides the same exception safety guarantee as this method, so it
130 * should be considered whether the implementation can be made strongly
131 * exception safe.
133 virtual void convertValue(const std::string &value) = 0;
134 /*! \brief
135 * Processes values for a set after all have been converted.
137 * \param[in,out] values Valid values in the set.
138 * \throws InvalidInputError if the values do not form a valid set.
140 * This method is called after all convertValue() calls for a set.
141 * \p values contains all values that were validly converted by
142 * convertValue(). The derived class may alter the values, but should
143 * in such a case ensure that a correct number of values is produced.
144 * If the derived class throws, all values in \p values are discarded.
146 virtual void processSetValues(ValueList *values)
148 GMX_UNUSED_VALUE(values);
150 /*! \copydoc gmx::AbstractOptionStorage::processSet()
152 * OptionStorageTemplate implements transaction support for a set of
153 * values in this method (see the class description), and provides a
154 * more detailed processSetValues() method that can be overridden in
155 * subclasses to process the actual values. Derived classes should
156 * override that method instead of this one if set value processing is
157 * necessary.
159 virtual void processSet();
160 /*! \copydoc gmx::AbstractOptionStorage::processAll()
162 * The implementation in OptionStorageTemplate does nothing.
164 virtual void processAll()
167 /*! \brief
168 * Formats a single value as a string.
170 * \param[in] value Value to format.
171 * \returns \p value formatted as a string.
173 * The derived class must provide this method to format values a
174 * strings. Called by formatValue() to do the actual formatting.
176 virtual std::string formatSingleValue(const T &value) const = 0;
178 /*! \brief
179 * Removes all values from the storage.
181 * Does not throw.
183 void clear() { values_->clear(); }
184 /*! \brief
185 * Adds a value to a temporary storage.
187 * \param[in] value Value to add. A copy is made.
188 * \throws std::bad_alloc if out of memory.
189 * \throws InvalidInputError if the maximum value count has been reached.
191 * Derived classes should call this function from the convertValue()
192 * implementation to add converted values to the storage.
193 * If the maximum value count has been reached, the value is discarded
194 * and an exception is thrown.
196 * If adding values outside convertValue() (e.g., to set a custom
197 * default value), derived classes should call clearSet() before adding
198 * values (unless in the constructor) and commitValues() once all
199 * values are added.
201 void addValue(const T &value);
202 /*! \brief
203 * Commits values added with addValue().
205 * \throws std::bad_alloc if out of memory.
207 * If this function succeeds, values added with addValue() since the
208 * previous clearSet() are added to the storage for the option.
209 * Only throws in out-of-memory conditions, and provides the strong
210 * exception safety guarantee.
212 * See addValue() for cases where this method should be used in derived
213 * classes.
215 * Calls refreshValues() and clearSet() if it is successful.
217 void commitValues();
218 /*! \brief
219 * Updates alternative store locations.
221 * Derived classes should override this method if they implement
222 * alternative store locations, and copy/translate values from the
223 * values() vector to these alternative storages. They should also
224 * call the base class implementation as part of their implementation.
226 * Should be called in derived classes if values are modified directly
227 * through the values() method, e.g., in processAll(). Does not need
228 * to be called if commitValues() is used.
230 * Does not throw, and derived classes should not change that.
232 virtual void refreshValues();
234 /*! \brief
235 * Sets the default value for the option.
237 * \param[in] value Default value to set.
238 * \throws std::bad_alloc if out of memory.
240 * This method can be used from the derived class constructor to
241 * programmatically set a default value.
243 void setDefaultValue(const T &value);
244 /*! \brief
245 * Sets the default value if set for the option.
247 * \param[in] value Default value to set.
248 * \throws std::bad_alloc if out of memory.
250 * This method can be used from the derived class constructor to
251 * programmatically set a default value.
253 void setDefaultValueIfSet(const T &value);
255 /*! \brief
256 * Provides derived classes access to the current list of values.
258 * The non-const variant should only be used from processAll() in
259 * derived classes if necessary, and refreshValues() should be called
260 * if any changes are made.
262 ValueList &values() { return *values_; }
263 //! Provides derived classes access to the current list of values.
264 const ValueList &values() const { return *values_; }
266 private:
267 /*! \brief
268 * Vector for temporary storage of values before commitSet() is called.
270 ValueList setValues_;
271 /*! \brief
272 * Vector for primary storage of option values.
274 * Is never NULL; points either to externally provided vector, or an
275 * internally allocated one. The allocation is performed by the
276 * constructor.
278 * Primarily, modifications to values are done only to this storage,
279 * and other storage locations are updated only when refreshValues() is
280 * called.
282 ValueList *values_;
283 T *store_;
284 int *countptr_;
285 // These never release ownership.
286 std::unique_ptr<ValueList> ownedValues_;
287 std::unique_ptr<T> defaultValueIfSet_;
289 // Copy and assign disallowed by base.
293 template <typename T>
294 template <class U>
295 OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U> &settings,
296 OptionFlags staticFlags)
297 : AbstractOptionStorage(settings, staticFlags),
298 values_(settings.storeVector_),
299 store_(settings.store_),
300 countptr_(settings.countptr_)
302 // If the maximum number of values is not known, storage to
303 // caller-allocated memory is unsafe.
304 if (store_ != NULL && (maxValueCount() < 0 || hasFlag(efOption_MultipleTimes)))
306 GMX_THROW(APIError("Cannot set user-allocated storage for arbitrary number of values"));
308 if (values_ == NULL)
310 ownedValues_.reset(new std::vector<T>);
311 values_ = ownedValues_.get();
313 if (hasFlag(efOption_NoDefaultValue)
314 && (settings.defaultValue_ != NULL
315 || settings.defaultValueIfSet_ != NULL))
317 GMX_THROW(APIError("Option does not support default value, but one is set"));
319 if (store_ != NULL && countptr_ == NULL && !isVector()
320 && minValueCount() != maxValueCount())
322 GMX_THROW(APIError("Count storage is not set, although the number of produced values is not known"));
324 if (!hasFlag(efOption_NoDefaultValue))
326 setFlag(efOption_HasDefaultValue);
327 if (settings.defaultValue_ != NULL)
329 setDefaultValue(*settings.defaultValue_);
331 else if (ownedValues_.get() != NULL && store_ != NULL)
333 values_->clear();
334 int count = (settings.isVector() ?
335 settings.maxValueCount_ : settings.minValueCount_);
336 for (int i = 0; i < count; ++i)
338 values_->push_back(store_[i]);
341 if (settings.defaultValueIfSet_ != NULL)
343 setDefaultValueIfSet(*settings.defaultValueIfSet_);
349 template <typename T>
350 OptionStorageTemplate<T>::~OptionStorageTemplate()
355 template <typename T>
356 std::string OptionStorageTemplate<T>::formatValue(int i) const
358 GMX_RELEASE_ASSERT(i == DefaultValueIfSetIndex || (i >= 0 && i < valueCount()),
359 "Invalid value index");
360 if (i == DefaultValueIfSetIndex)
362 if (defaultValueIfSet_.get() != NULL)
364 return formatSingleValue(*defaultValueIfSet_);
366 return std::string();
368 return formatSingleValue(values()[i]);
372 template <typename T>
373 void OptionStorageTemplate<T>::clearSet()
375 setValues_.clear();
379 template <typename T>
380 void OptionStorageTemplate<T>::processSet()
382 processSetValues(&setValues_);
383 if (setValues_.empty() && defaultValueIfSet_.get() != NULL)
385 addValue(*defaultValueIfSet_);
386 setFlag(efOption_HasDefaultValue);
388 else
390 clearFlag(efOption_HasDefaultValue);
392 if (!hasFlag(efOption_DontCheckMinimumCount)
393 && setValues_.size() < static_cast<size_t>(minValueCount()))
395 GMX_THROW(InvalidInputError("Too few (valid) values"));
397 commitValues();
401 template <typename T>
402 void OptionStorageTemplate<T>::addValue(const T &value)
404 if (maxValueCount() >= 0
405 && setValues_.size() >= static_cast<size_t>(maxValueCount()))
407 GMX_THROW(InvalidInputError("Too many values"));
409 setValues_.push_back(value);
413 template <typename T>
414 void OptionStorageTemplate<T>::commitValues()
416 if (hasFlag(efOption_ClearOnNextSet))
418 values_->swap(setValues_);
420 else
422 values_->insert(values_->end(), setValues_.begin(), setValues_.end());
424 clearSet();
425 refreshValues();
429 template <typename T>
430 void OptionStorageTemplate<T>::refreshValues()
432 if (countptr_ != NULL)
434 *countptr_ = static_cast<int>(values_->size());
436 if (store_ != NULL)
438 for (size_t i = 0; i < values_->size(); ++i)
440 store_[i] = (*values_)[i];
446 template <typename T>
447 void OptionStorageTemplate<T>::setDefaultValue(const T &value)
449 if (hasFlag(efOption_NoDefaultValue))
451 GMX_THROW(APIError("Option does not support default value, but one is set"));
453 if (hasFlag(efOption_HasDefaultValue))
455 setFlag(efOption_ExplicitDefaultValue);
456 clear();
457 clearSet();
458 addValue(value);
459 // TODO: As this is called from the constructor, it should not call
460 // virtual functions.
461 commitValues();
466 template <typename T>
467 void OptionStorageTemplate<T>::setDefaultValueIfSet(const T &value)
469 if (hasFlag(efOption_NoDefaultValue))
471 GMX_THROW(APIError("Option does not support default value, but one is set"));
473 if (hasFlag(efOption_MultipleTimes))
475 GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
477 setFlag(efOption_DefaultValueIfSetExists);
478 defaultValueIfSet_.reset(new T(value));
481 } // namespace gmx
483 #endif