2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010-2018, The GROMACS development team.
5 * Copyright (c) 2019,2021, 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.
38 * Implements classes in basicoptions.h and basicoptionstorage.h.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_options
45 #include "basicoptions.h"
56 #include "gromacs/utility/cstringutil.h"
57 #include "gromacs/utility/exceptions.h"
58 #include "gromacs/utility/strconvert.h"
59 #include "gromacs/utility/stringutil.h"
61 #include "basicoptionstorage.h"
67 * Expands a single value to a vector by copying the value.
69 * \tparam ValueType Type of values to process.
70 * \param[in] length Length of the resulting vector.
71 * \param[in,out] values Values to process.
72 * \throws std::bad_alloc if out of memory.
73 * \throws InvalidInputError if \p values has an invalid number of values.
75 * \p values should have 0, 1, or \p length values.
76 * If \p values has 1 value, it is expanded such that it has \p length
77 * identical values. In other valid cases, nothing is done.
79 * \ingroup module_options
81 template<typename ValueType>
82 void expandVector(size_t length, std::vector<ValueType>* values)
84 if (length > 0 && !values->empty() && values->size() != length)
86 if (values->size() != 1)
88 GMX_THROW(gmx::InvalidInputError(
89 gmx::formatString("Expected 1 or %zu values, got %zu", length, values->size())));
91 const ValueType& value = (*values)[0];
92 values->resize(length, value);
97 * Finds an enumerated value from the list of allowed values.
99 * \param[in] allowedValues List of allowed values.
100 * \param[in] value Value to search for.
101 * \throws gmx::InvalidInputError if \p value does not match anything in
103 * \returns Iterator to the found value.
105 * \ingroup module_options
107 std::vector<std::string>::const_iterator findEnumValue(const std::vector<std::string>& allowedValues,
108 const std::string& value)
110 std::vector<std::string>::const_iterator i;
111 std::vector<std::string>::const_iterator match = allowedValues.end();
112 for (i = allowedValues.begin(); i != allowedValues.end(); ++i)
114 // TODO: Case independence.
115 if (gmx::startsWith(*i, value))
117 if (match == allowedValues.end() || i->size() < match->size())
123 if (match == allowedValues.end())
125 GMX_THROW(gmx::InvalidInputError("Invalid value: " + value));
135 /********************************************************************
136 * BooleanOptionStorage
139 std::string BooleanOptionStorage::formatSingleValue(const bool& value) const
141 return value ? "yes" : "no";
144 void BooleanOptionStorage::initConverter(ConverterType* converter)
146 converter->addConverter<std::string>(&fromStdString<bool>);
149 /********************************************************************
153 BooleanOptionInfo::BooleanOptionInfo(BooleanOptionStorage* option) : OptionInfo(option) {}
155 const BooleanOptionStorage& BooleanOptionInfo::option() const
157 return static_cast<const BooleanOptionStorage&>(OptionInfo::option());
160 bool BooleanOptionInfo::defaultValue() const
162 return option().defaultValue();
165 /********************************************************************
169 AbstractOptionStorage* BooleanOption::createStorage(const OptionManagerContainer& /*managers*/) const
171 return new BooleanOptionStorage(*this);
175 /********************************************************************
176 * IntegerOptionStorage
179 std::string IntegerOptionStorage::formatSingleValue(const int& value) const
181 return toString(value);
184 void IntegerOptionStorage::initConverter(ConverterType* converter)
186 converter->addConverter<std::string>(&fromStdString<int>);
189 void IntegerOptionStorage::processSetValues(ValueList* values)
193 expandVector(maxValueCount(), values);
197 /********************************************************************
201 IntegerOptionInfo::IntegerOptionInfo(IntegerOptionStorage* option) : OptionInfo(option) {}
203 /********************************************************************
207 AbstractOptionStorage* IntegerOption::createStorage(const OptionManagerContainer& /*managers*/) const
209 return new IntegerOptionStorage(*this);
213 /********************************************************************
217 std::string Int64OptionStorage::formatSingleValue(const int64_t& value) const
219 return toString(value);
222 void Int64OptionStorage::initConverter(ConverterType* converter)
224 converter->addConverter<std::string>(&fromStdString<int64_t>);
227 /********************************************************************
231 Int64OptionInfo::Int64OptionInfo(Int64OptionStorage* option) : OptionInfo(option) {}
233 /********************************************************************
237 AbstractOptionStorage* Int64Option::createStorage(const OptionManagerContainer& /*managers*/) const
239 return new Int64OptionStorage(*this);
243 /********************************************************************
244 * DoubleOptionStorage
247 DoubleOptionStorage::DoubleOptionStorage(const DoubleOption& settings) :
248 MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
252 std::string DoubleOptionStorage::typeString() const
254 return isVector() ? "vector" : (isTime() ? "time" : "real");
257 std::string DoubleOptionStorage::formatSingleValue(const double& value) const
259 return toString(value / factor_);
262 void DoubleOptionStorage::initConverter(ConverterType* converter)
264 converter->addConverter<std::string>(&fromStdString<double>);
265 converter->addCastConversion<float>();
268 double DoubleOptionStorage::processValue(const double& value) const
270 // TODO: Consider testing for overflow when scaling with factor_.
271 return value * factor_;
274 void DoubleOptionStorage::processSetValues(ValueList* values)
278 expandVector(maxValueCount(), values);
282 void DoubleOptionStorage::setScaleFactor(double factor)
284 GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
285 if (!hasFlag(efOption_HasDefaultValue))
287 double scale = factor / factor_;
288 for (double& value : values())
296 /********************************************************************
300 DoubleOptionInfo::DoubleOptionInfo(DoubleOptionStorage* option) : OptionInfo(option) {}
302 DoubleOptionStorage& DoubleOptionInfo::option()
304 return static_cast<DoubleOptionStorage&>(OptionInfo::option());
307 const DoubleOptionStorage& DoubleOptionInfo::option() const
309 return static_cast<const DoubleOptionStorage&>(OptionInfo::option());
312 bool DoubleOptionInfo::isTime() const
314 return option().isTime();
317 void DoubleOptionInfo::setScaleFactor(double factor)
319 option().setScaleFactor(factor);
322 /********************************************************************
326 AbstractOptionStorage* DoubleOption::createStorage(const OptionManagerContainer& /*managers*/) const
328 return new DoubleOptionStorage(*this);
332 /********************************************************************
336 FloatOptionStorage::FloatOptionStorage(const FloatOption& settings) :
337 MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
341 std::string FloatOptionStorage::typeString() const
343 return isVector() ? "vector" : (isTime() ? "time" : "real");
346 std::string FloatOptionStorage::formatSingleValue(const float& value) const
348 return toString(value / factor_);
351 void FloatOptionStorage::initConverter(ConverterType* converter)
353 converter->addConverter<std::string>(&fromStdString<float>);
354 converter->addCastConversion<double>();
357 float FloatOptionStorage::processValue(const float& value) const
359 // TODO: Consider testing for overflow when scaling with factor_.
360 return value * factor_;
363 void FloatOptionStorage::processSetValues(ValueList* values)
367 expandVector(maxValueCount(), values);
371 void FloatOptionStorage::setScaleFactor(double factor)
373 GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
374 if (!hasFlag(efOption_HasDefaultValue))
376 float scale = factor / factor_;
377 for (float& value : values())
385 /********************************************************************
389 FloatOptionInfo::FloatOptionInfo(FloatOptionStorage* option) : OptionInfo(option) {}
391 FloatOptionStorage& FloatOptionInfo::option()
393 return static_cast<FloatOptionStorage&>(OptionInfo::option());
396 const FloatOptionStorage& FloatOptionInfo::option() const
398 return static_cast<const FloatOptionStorage&>(OptionInfo::option());
401 bool FloatOptionInfo::isTime() const
403 return option().isTime();
406 void FloatOptionInfo::setScaleFactor(double factor)
408 option().setScaleFactor(factor);
411 /********************************************************************
415 AbstractOptionStorage* FloatOption::createStorage(const OptionManagerContainer& /*managers*/) const
417 return new FloatOptionStorage(*this);
421 /********************************************************************
422 * StringOptionStorage
425 StringOptionStorage::StringOptionStorage(const StringOption& settings) :
426 MyBase(settings), info_(this)
428 if (settings.defaultEnumIndex_ >= 0 && settings.enumValues_ == nullptr)
430 GMX_THROW(APIError("Cannot set default enum index without enum values"));
432 if (settings.enumValues_ != nullptr)
434 int count = settings.enumValuesCount_;
438 while (settings.enumValues_[count] != nullptr)
443 for (int i = 0; i < count; ++i)
445 if (settings.enumValues_[i] == nullptr)
447 GMX_THROW(APIError("Enumeration value cannot be NULL"));
449 allowed_.emplace_back(settings.enumValues_[i]);
451 if (settings.defaultEnumIndex_ >= 0)
453 if (settings.defaultEnumIndex_ >= count)
455 GMX_THROW(APIError("Default enumeration index is out of range"));
457 const std::string* defaultValue = settings.defaultValue();
458 if (defaultValue != nullptr && *defaultValue != allowed_[settings.defaultEnumIndex_])
460 GMX_THROW(APIError("Conflicting default values"));
462 setDefaultValue(allowed_[settings.defaultEnumIndex_]);
467 std::string StringOptionStorage::formatExtraDescription() const
470 if (!allowed_.empty())
473 result.append(joinStrings(allowed_, ", "));
478 std::string StringOptionStorage::formatSingleValue(const std::string& value) const
483 void StringOptionStorage::initConverter(ConverterType* /*converter*/) {}
485 std::string StringOptionStorage::processValue(const std::string& value) const
487 if (!allowed_.empty())
489 return *findEnumValue(this->allowed_, value);
494 /********************************************************************
498 StringOptionInfo::StringOptionInfo(StringOptionStorage* option) : OptionInfo(option) {}
500 const StringOptionStorage& StringOptionInfo::option() const
502 return static_cast<const StringOptionStorage&>(OptionInfo::option());
505 bool StringOptionInfo::isEnumerated() const
507 return !allowedValues().empty();
510 const std::vector<std::string>& StringOptionInfo::allowedValues() const
512 return option().allowedValues();
515 /********************************************************************
519 AbstractOptionStorage* StringOption::createStorage(const OptionManagerContainer& /*managers*/) const
521 return new StringOptionStorage(*this);
525 /********************************************************************
529 EnumOptionStorage::EnumOptionStorage(const AbstractOption& settings,
530 const char* const* enumValues,
533 int defaultValueIfSet,
534 StorePointer store) :
535 MyBase(settings, std::move(store)), info_(this)
537 if (enumValues == nullptr)
539 GMX_THROW(APIError("Allowed values must be provided to EnumOption"));
545 while (enumValues[count] != nullptr)
550 for (int i = 0; i < count; ++i)
552 if (enumValues[i] == nullptr)
554 GMX_THROW(APIError("Enumeration value cannot be NULL"));
556 allowed_.emplace_back(enumValues[i]);
559 GMX_ASSERT(defaultValue < count, "Default enumeration value is out of range");
560 GMX_ASSERT(defaultValueIfSet < count, "Default enumeration value is out of range");
561 setFlag(efOption_HasDefaultValue);
562 if (defaultValue >= 0)
564 setDefaultValue(defaultValue);
566 if (defaultValueIfSet >= 0)
568 setDefaultValueIfSet(defaultValueIfSet);
572 std::string EnumOptionStorage::formatExtraDescription() const
576 result.append(joinStrings(allowed_, ", "));
580 std::string EnumOptionStorage::formatSingleValue(const int& value) const
582 if (value < 0 || value >= ssize(allowed_))
584 return std::string();
586 return allowed_[value];
589 Any EnumOptionStorage::normalizeValue(const int& value) const
591 return Any::create<std::string>(formatSingleValue(value));
594 void EnumOptionStorage::initConverter(ConverterType* converter)
596 converter->addConverter<std::string>([this](const std::string& value) {
597 return findEnumValue(this->allowed_, value) - this->allowed_.begin();
601 /********************************************************************
605 EnumOptionInfo::EnumOptionInfo(EnumOptionStorage* option) : OptionInfo(option) {}
607 const EnumOptionStorage& EnumOptionInfo::option() const
609 return static_cast<const EnumOptionStorage&>(OptionInfo::option());
612 const std::vector<std::string>& EnumOptionInfo::allowedValues() const
614 return option().allowedValues();
617 /********************************************************************
625 AbstractOptionStorage* createEnumOptionStorage(const AbstractOption& option,
626 const char* const* enumValues,
629 int defaultValueIfSet,
630 std::unique_ptr<IOptionValueStore<int>> store)
632 return new EnumOptionStorage(option, enumValues, count, defaultValue, defaultValueIfSet, move(store));
636 } // namespace internal