void AbstractOptionStorage::startSet()
{
GMX_RELEASE_ASSERT(!_inSet, "finishSet() not called");
- clearSet();
// The last condition takes care of the situation where multiple
// sources are used, and a later source should be able to reassign
// the value even though the option is already set.
{
GMX_THROW(InvalidInputError("Option specified multiple times"));
}
+ clearSet();
_inSet = true;
}
{
GMX_RELEASE_ASSERT(_inSet, "startSet() not called");
_inSet = false;
+ // TODO: Should this be done only when processSet() does not throw?
setFlag(efSet);
processSet();
}
*
* This header is needed directly only when implementing new option types,
* but methods of OptionTemplate are visible even to the normal user through
- * its subclasses..
+ * its subclasses.
*
* \author Teemu Murtola <teemu.murtola@cbr.su.se>
* \inlibraryapi
*
* \param[in] options Option collection object.
* \returns The created storage object.
+ * \throws APIError if invalid option settings have been provided.
*
* This method is called by when creating an option object
* The \p options object is used to implement global properties.
*
* Derived classes should implement the method to create an actual
* storage object and populate it with correct values.
+ * They should also throw APIError if they detect problems.
*
* Should only be called by Options::addOption().
- *
- * \see AbstractOptionStorage::init()
*/
virtual AbstractOptionStorage *createDefaultStorage(Options *options) const = 0;
/*! \brief
* Creates the description string for the option.
*
+ * \returns Description string for the option.
+ *
* This function is virtual to allow derived classes to customize the
* description programmatically, e.g., by adding the list of allowed
* values.
* \endcode
*
* All public functions in this class return \c *this casted to a reference to
- * \p U.
+ * \p U. They do not throw.
*
* For examples of how to use classes derived from this class, see the class
* documentation for Options.
* \p store. If there is no maximum allowed number or if the maximum
* is inconveniently large, storeVector() should be used.
*
+ * For information on when values are available in the storage, see
+ * storeVector().
+ *
* The pointer provided should remain valid as long as the associated
* Options object exists.
*/
*
* \param[in] countptr Storage for the number of values.
*
+ * For information on when values are available in the storage, see
+ * storeVector().
+ *
* The pointers provided should remain valid as long as the associated
* Options object exists.
*/
*
* \param[in] store Vector to store option values in.
*
- * The caller should not make any assumptions about the contents of the
- * vector while options are being parsed.
+ * Values are added to the vector after each successful set of values
+ * is parsed. Note that for some options, the value may be changed
+ * later, and is only guaranteed to be correct after Options::finish()
+ * has been called (see, e.g., DoubleOption::time()).
*
* The pointer provided should remain valid as long as the associated
* Options object exists.
* This marks the vurrent value of the option as a default value,
* causing next call to startSet() to clear it. This allows values
* from the new source to overwrite old values.
+ *
+ * This method does not throw.
*/
void startSource();
/*! \brief
* Starts adding a new set of values for the option.
*
+ * \throws InvalidInputError if option is specified multiple times,
+ * but is not specified to accept it.
+ *
* If the parameter is specified multiple times, startSet() should be
* called before the values for each instance.
+ *
+ * Strong exception safety guarantee.
*/
void startSet();
/*! \brief
* Adds a new value for the option, converting it from a string.
*
* \param[in] value String value to convert.
+ * \throws InvalidInputError if value cannot be converted, or
+ * if there are too many values.
+ *
+ * This method should only be called between startSet() and
+ * finishSet().
*/
void appendValue(const std::string &value);
/*! \brief
* Performs validation and/or actions once a set of values has been
* added.
*
+ * \throws InvalidInputError if too few values have been provided, or
+ * if the valid values since previous startSet() are invalid as a
+ * set.
+ *
* If the parameter is specified multiple times, finishSet() should be
* called after the values for each instance.
*/
/*! \brief
* Performs validation and/or actions once all values have been added.
*
- * This function should be called after all values have been provided
+ * \throws InvalidInputError if the option is required but not set, or
+ * if all valid values together are invalid as a set.
+ *
+ * This method should be called after all values have been provided
* with appendValue().
*/
void finish();
*
* \param[in] settings Option settings.
* \param[in] options Option collection that will contain the
- * option.
+ * option.
+ * \param[in] staticFlags Option flags that are always set and specify
+ * generic behavior of the option.
+ * \throws APIError if invalid settings have been provided.
*/
AbstractOptionStorage(const AbstractOption &settings, Options *options,
- OptionFlags staticFlags = OptionFlags());
+ OptionFlags staticFlags);
//! Returns true if the given flag is set.
bool hasFlag(OptionFlag flag) const { return _flags.test(flag); }
* Sets a new minimum number of values required in one set.
*
* \param[in] count New minimum number of values (must be > 0).
+ * \throws InvalidInputError if already provided values violate the limit.
*
* If values have already been provided, it is checked that there are
- * enough. If called together with setMaxValueCount(), only
- * one of them will log an error in \p errors even if both fail, but
- * the return value is not affected.
+ * enough.
*
* Cannot be called for options with ::efMulti set, because it is
* impossible to check the requirement after the values have been set.
+ * If attempted, will assert.
*/
void setMinValueCount(int count);
/*! \brief
*
* \param[in] count New maximum number of values
* (must be > 0, or -1 for no limit).
+ * \throws InvalidInputError if already provided values violate the limit.
*
* If values have already been provided, it is checked that there are
- * not too many. If called together with setMinValueCount(), only
- * one of them will log an error in \p errors even if both fail, but
- * the return value is not affected.
+ * not too many.
*
* Cannot be called for options with ::efMulti set, because it is
* impossible to check the requirement after the values have been set.
+ * If attempted, will assert.
*/
void setMaxValueCount(int count);
*
* This function is always called before starting to add values to
* a set, allowing the storage to clear its internal buffers.
+ *
+ * Should not throw.
*/
virtual void clearSet() = 0;
/*! \brief
* Adds a new value, converting it from a string.
*
* \param[in] value String value to convert.
+ * \throws InvalidInputError if \p value is not valid for this option
+ * or if there have been too many values in the set.
*
- * This function may be called multiple times if the underlying
+ * This method may be called multiple times if the underlying
* option is defined to accept multiple values.
+ *
+ * \see OptionStorageTemplate::convertValue()
*/
virtual void convertValue(const std::string &value) = 0;
/*! \brief
* Performs validation and/or actions once a set of values has been
* added.
*
- * This function may be called multiple times of the underlying option
+ * \throws InvalidInputError if the values in the set are not valid
+ * as a whole.
+ *
+ * This method may be called multiple times if the underlying option
* can be specified multiple times.
+ *
+ * \see OptionStorageTemplate::processSetValues()
*/
virtual void processSet() = 0;
/*! \brief
* Performs validation and/or actions once all values have been added.
*
- * This function is always called once.
+ * \throws InvalidInputError if all provided values are not valid as
+ * a set.
+ *
+ * This method is always called once.
+ *
+ * If the method throws, implementation should take care to leave the
+ * option in a consistent, meaningful state. However, currently none
+ * of the implementations actually throw in any situation where the
+ * option may be left in an inconsistent state.
*/
virtual void processAll() = 0;
options.addOption(BooleanOption("pbc").store(&bPBC));
* \endcode
*
+ * Public methods in this class do not throw.
+ *
* \inpublicapi
*/
class BooleanOption : public OptionTemplate<bool, BooleanOption>
}
protected:
+ //! Creates a BooleanOptionStorage object.
virtual AbstractOptionStorage *createDefaultStorage(Options *options) const;
};
options.addOption(IntegerOption("box").store(box).vector());
* \endcode
*
+ * Public methods in this class do not throw.
+ *
* \inpublicapi
*/
class IntegerOption : public OptionTemplate<int, IntegerOption>
MyClass &vector() { setVector(); return me(); }
protected:
+ //! Creates an IntegerOptionStorage object.
virtual AbstractOptionStorage *createDefaultStorage(Options *options) const;
/*! \brief
/*! \brief
* Specifies an option that provides floating-point (double) values.
*
+ * Public methods in this class do not throw.
+ *
* \inpublicapi
*/
class DoubleOption : public OptionTemplate<double, DoubleOption>
MyClass &vector() { setVector(); return me(); }
/*! \brief
* Sets the option to obey time conversion rules.
+ *
+ * For options with this flag, the user can specify a global time unit
+ * using a global option that is added by Options::addDefaultOptions().
+ * For the program, the value is always converted to ps.
+ *
+ * When this flag is specified, the option value is available only
+ * after Options::finish() has been called instead of immediately after
+ * assignment.
*/
MyClass &timeValue() { _bTime = true; return me(); }
private:
+ //! Creates a DoubleOptionStorage object.
virtual AbstractOptionStorage *createDefaultStorage(Options *options) const;
bool _bTime;
.storeEnumIndex(&type));
* \endcode
*
+ * Public methods in this class do not throw.
+ *
* \inpublicapi
*/
class StringOption : public OptionTemplate<std::string, StringOption>
explicit StringOption(const char *name)
: MyBase(name), _enumValues(NULL), _defaultEnumIndex(-1),
_enumIndexStore(NULL)
- {}
+ {
+ }
/*! \brief
* Sets the option to only accept one of a fixed set of strings.
*
- * \param[in] values Array of strings to accept, terminated with a
- * NULL value.
+ * \param[in] values Array of strings to accept, a NULL pointer
+ * following the last string.
*
* Also accepts prefixes of the strings; if a prefix matches more than
* one of the possible strings, the shortest one is used (in a tie, the
{ _enumIndexStore = store; return me(); }
protected:
+ //! Creates a StringOptionStorage object.
virtual AbstractOptionStorage *createDefaultStorage(Options *options) const;
virtual std::string createDescription() const;
/*! \brief
* Specifies an option that provides file names.
*
+ * Public methods in this class do not throw.
+ *
+ * This class is currently a stub implementation.
+ *
* \inpublicapi
*/
class FileNameOption : public OptionTemplate<std::string, FileNameOption>
MyClass &libraryFile() { setFlag(efFileLibrary); return me(); }
protected:
+ //! Creates a FileNameOptionStorage object.
virtual AbstractOptionStorage *createDefaultStorage(Options *options) const;
private:
/*! \brief
* Parses the command-line.
+ *
+ * \throws InvalidInputError if any errors were detected in the input.
+ *
+ * All command-line arguments are parsed, and an aggregate exception
+ * with all the detected errors is thrown in the end.
*/
void parse(int *argc, char *argv[]);
* code.
*
* Currently, the returned structure is always filled with default
- * values.
+ * values for most fields.
*
* \deprecated
*/
{
return _usedProperties & (1<<id);
}
+ /*! \brief
+ * Adds options for setting requested global properties.
+ *
+ * \param[in,out] options Options to which the global property options
+ * are added.
+ *
+ * If a global property has been requested and it can be set/customized
+ * by the user, this method adds the necessary option to \p options.
+ *
+ * This method performs the real work of Options::addDefaultOptions().
+ */
void addDefaultOptions(Options *options);
/*! \brief
* Initializes variables dependent on global properties.
*
* \param[in] name Name to search for.
* \returns Pointer to the found subsection, or NULL if not found.
+ *
+ * Does not throw.
*/
Options *findSubSection(const char *name) const;
/*! \brief
*
* \param[in] name Name to search for.
* \returns Pointer to the found option, or NULL if not found.
+ *
+ * Does not throw.
*/
AbstractOptionStorage *findOption(const char *name) const;
/*! \brief
- * Calls Option::startSource() for all options, including subsections.
+ * Calls AbstractOptionStorage::startSource() for all options,
+ * including subsections.
+ *
+ * Does not throw.
*/
void startSource();
namespace gmx
{
+// TODO: Move this into the utility module
static std::string composeString(const char *const *sarray)
{
std::string result;
//! Returns the full description of the option collection.
const std::string &description() const;
- //! Sets the full description of the option collection.
+ /*! \brief
+ * Sets the full description of the option collection.
+ *
+ * \param[in] desc Array of strings to form the description, terminated
+ * by a NULL pointer.
+ *
+ * The strings in the \p desc array are concatenated to form the
+ * description, adding a single space between the strings if there is
+ * no whitespace in the end of a string.
+ */
void setDescription(const char *const *desc);
//int addBugs(int nbugs, const char *const *bugs);
/*! \brief
* Adds an option collection as a subsection of this collection.
*
+ * \param[in] section Subsection to add.
+ *
* The name() field of \p section is used as the name of the
- * subsection.
+ * subsection. If an attempt is made to add two different subsections
+ * with the same name, this function asserts.
*
* For certain functionality to work properly, no options should
* be added to the subsection after it has been added to another
* collection.
* It is not possible to add the same Options object as a subsection to
* several different Options.
+ * If an attempt is made, the function asserts.
*/
void addSubSection(Options *section);
/*! \brief
* Adds a recognized option to the collection.
*
- * The behavior is undefined if invalid settings are provided.
- * The current implementation asserts if it detects an error.
+ * \throws APIError if invalid option settings are provided.
*
* See \link Options class documentation \endlink for example usage.
*/
*/
void addDefaultOptions();
- //! Returns true if option is set.
+ //! Returns true if option \p name is set.
bool isSet(const char *name) const;
/*! \brief
* Notifies the collection that all option values are assigned.
*
- * \param[in] errors Error reporter for reporting errors.
- * \retval 0 on success.
+ * \throws InvalidInputError if invalid user input is detected.
*
* This function should be called after no more option values are
* to be assigned. Values in storage variables are guaranteed to be
- * available only after this call.
+ * available only after this call, although in most cases, they are
+ * available already during assignment.
+ *
+ * If invalid option values, e.g., missing required option, is detected
+ * at this point, this function throws. The thrown exception contains
+ * information on all errors detected during the call.
*/
void finish();
* it can change if this object is added as a subsection into
* another collection.
*
+ * The global property object is shared by all Options objects that
+ * are part of the same section/subsection hierarchy.
+ *
* \see OptionsGlobalProperties
*/
OptionsGlobalProperties &globalProperties();
{
AbstractOptionStorage *option = _impl->_currentOption;
GMX_RELEASE_ASSERT(option != NULL, "startOption() not called");
+ // Does not count correctly, but the actual count is not really used.
+ // TODO: Rename the variable to better reflect the usage.
++_impl->_currentValueCount;
option->appendValue(value);
}
{
if (_impl->_currentValueCount == 0)
{
+ // Should not throw, otherwise something is wrong.
+ // TODO: Get rid of the hard-coded values.
option->appendValue(_impl->_reverseBoolean ? "0" : "1");
}
else if (_impl->_reverseBoolean)
* This class extends the interface of an Options object by providing methods
* to set values for options. It also keeps track of necessary state variables
* to assign values to options in subsections within the Options object.
- * Typical use (without error checking):
+ * Typical use (without error handling):
* \code
gmx::options::Options options("name", "Title");
// Set up options
gmx::options::OptionsAssigner assigner(&options);
+assigner.start();
assigner.startOption("opt1");
assigner.appendValue("3");
+assigner.finishOption();
assigner.startSubSection("section");
assigner.startOption("opt2"); // Now in the subsection
assigner.appendValue("yes");
+assigner.finishOption();
assigner.finishSubSection()
assigner.startOption("opt3"); // Again in the main options
assigner.appendValue("2");
-assigner.finish(); // At minimum, the return value of finish() should be checked.
+assigner.finishOption();
+assigner.finish();
* \endcode
*
- * As shown in the example, calling finishOption() or finishSubSection() is
- * optional; they are automatically called when appropriate by startOption(),
- * startSubSection(), and finish().
- * However, you need to call them explicitly if you want to act on the return
- * value: these calls do not influence the return value of
- * startOption() / startSubSection().
- * They do influence the return value of finish(), however.
- * The finish() method should always be called.
- *
* \inlibraryapi
* \ingroup module_options
*/
*
* Can be set or cleared at any time, and will have effect on all
* subsequent calls of startOption().
+ *
+ * Does not throw.
*/
void setAcceptBooleanNoPrefix(bool enabled);
/*! \brief
*
* Can be set or cleared at any time, and will have effect on all
* subsequent calls of startOption().
+ *
+ * Does not throw.
*/
void setNoStrictSectioning(bool enabled);
/*! \brief
* Start assigning values.
+ *
+ * Does not throw.
*/
void start();
/*! \brief
* Start assigning values to options in a subsection.
*
* \param[in] name Name of the subsection to start assigning to.
+ * \throws InvalidInputError if such a subsection is not found.
+ *
+ * Strong exception safety guarantee.
*/
void startSubSection(const char *name);
/*! \brief
* Start assigning values for an option.
*
* \param[in] name Name of the option to start assigning to.
+ * \throws InvalidInputError if such an option is not found, or if the
+ * option is specified more than once but doesn't support it.
+ *
+ * Strong exception safety guarantee.
*/
void startOption(const char *name);
/*! \brief
* Appends a value to the value list of the current option.
*
* \param[in] value String representation of the value to assign.
+ * \throws InvalidInputError if the value cannot be converted or if
+ * there are too many values for an option.
+ *
+ * Basic exception safety guarantee:
+ * If this method throws, erroneous values are ignored, but it is
+ * possible to continue assigning values to the same option. However,
+ * if \p value would result in more than one value, and some of them
+ * can be converted, but some result in errors, it is currently
+ * possible that some values have been added to the option even if an
+ * exception is thrown.
+ *
+ * Strong exception safety guarantee if the option provides value
+ * conversion with the same guarantee. All options where a single
+ * input value always results in a single output value provide this.
+ *
+ * \internal
+ * This method provides the same exception safety guarantee as the
+ * OptionStorageTemplate::convertValue() method of the storage class
+ * implementing the option where the value is assigned to.
*/
void appendValue(const std::string &value);
/*! \brief
* Finish assigning values for the current option.
+ *
+ * \throws InvalidInputError if the set of values since startOption()
+ * is not valid.
+ *
+ * If this method throws, it returns to the state where the option was
+ * before startOption(), i.e., all values added with appendValue()
+ * since the last startOption() are discarded.
+ *
+ * Independent of whether the method throws, the option opened with
+ * startOption() will be closed after the call.
*/
void finishOption();
/*! \brief
* Finish assigning values to a subsection.
+ *
+ * Does not throw.
*/
void finishSubSection();
/*! \brief
* Finish assigning options through the object.
+ *
+ * Does not throw.
*/
void finish();
class Options;
-/*! \brief
+/*! \libinternal \brief
* Templated base class for constructing option value storage classes.
*
* \tparam T Assignable type that stores a single option value.
*
- * Provides an implementation of the clearSet() and valueCount() methods of
- * AbstractOptionStorage, as well as a basic implementation of processSet() and
- * processAll(). This leaves typeString(), formatValue(), and convertValue()
- * to be implemented in derived classes.
+ * Provides an implementation of the clearSet(), valueCount(), and processSet()
+ * methods of AbstractOptionStorage, as well as a basic no-action
+ * implementation of processAll(). Two new virtual methods are added:
+ * processSetValues() and refreshValues(). The default implementation of
+ * processSetValues() does nothing, and refreshValues() is used to update
+ * secondary storage after values have been added/changed.
+ * This leaves typeString(), formatValue(), and convertValue() to be
+ * implemented in derived classes. processSetValues() and processAll() can
+ * also be implemented if necessary.
+ *
+ * Implements transaction support for adding values within a set: all calls to
+ * addValue() add the value to a temporary storage, processSetValues() operates
+ * on this temporary storage, and commitValues() then copies these values to
+ * the real storage. commitValues() provides a strong exception safety
+ * guarantee for the process (and it only throws if it runs out of memory).
*
* \inlibraryapi
* \ingroup module_options
/*! \brief
* Initializes the storage from option settings.
*
- * \retval 0 on success.
- *
- * \see OptionTemplate::createDefaultStorage()
+ * \param[in] settings Option settings.
+ * \param[in] options Option collection that will contain the
+ * option.
+ * \param[in] staticFlags Option flags that are always set and specify
+ * generic behavior of the option.
+ * \throws APIError if invalid settings have been provided.
*/
template <class U>
OptionStorageTemplate(const OptionTemplate<T, U> &settings, Options *options,
/*! \copydoc AbstractOptionStorage::convertValue()
*
* Derived classes should call addValue() after they have converted
- * \p value to the storage type.
+ * \p value to the storage type. It is allowed to call addValue()
+ * more than once, or not at all. OptionsAssigner::appendValue()
+ * provides the same exception safety guarantee as this method, so it
+ * should be considered whether the implementation can be made strongly
+ * exception safe.
*/
virtual void convertValue(const std::string &value) = 0;
/*! \brief
* Processes values for a set after all have been converted.
*
* \param[in,out] values Valid values in the set.
+ * \throws InvalidInputError if the values do not form a valid set.
*
* This method is called after all convertValue() calls for a set.
* \p values contains all values that were validly converted by
- * convertValue(). The derived class may alter the values.
+ * convertValue(). The derived class may alter the values, but should
+ * in such a case ensure that a correct number of values is produced.
+ * If the derived class throws, all values in \p values are discarded.
*/
virtual void processSetValues(ValueList *values)
{
}
/*! \copydoc AbstractOptionStorage::processSet()
*
- * OptionStorage template implements this method, and provides a more
- * detailed processSetValues() method that can be overridden in
- * subclasses.
+ * OptionStorage template implements transaction support for a set of
+ * values in this method (see the class description), and provides a
+ * more detailed processSetValues() method that can be overridden in
+ * subclasses to process the actual values. Derived classes should
+ * override that method instead of this one if set value processing is
+ * necessary.
*/
virtual void processSet();
/*! \copydoc AbstractOptionStorage::processAll()
/*! \brief
* Removes all values from the storage.
+ *
+ * Does not throw.
*/
void clear() { _values->clear(); }
/*! \brief
- * Adds a value to the storage.
+ * Adds a value to a temporary storage.
*
* \param[in] value Value to add. A copy is made.
- * \retval 0 on success.
- * \retval ::eeInvalidInput if the maximum value count has been reached.
+ * \throws InvalidInputError if the maximum value count has been reached.
*
* Derived classes should call this function from the convertValue()
* implementation to add converted values to the storage.
+ * If the maximum value cont has been reached, the value is discarded
+ * and an exception is thrown.
+ *
+ * If adding values outside convertValue() (e.g., to set a custom
+ * default value), derived classes should call clearSet() before adding
+ * values (unless in the constructor) and commitValues() once all
+ * values are added.
*/
void addValue(const T &value);
/*! \brief
* Commits values added with addValue().
*
- * Derived classes should call this method if they use addValue()
- * outside convertValue(), e.g., to set a default value.
+ * If this function succeeds, values added with addValue() since the
+ * previous clearSet() are added to the storage for the option.
+ * Only throws in out-of-memory conditions, and provides the strong
+ * exception safety guarantee.
+ *
+ * See addValue() for cases where this method should be used in derived
+ * classes.
+ *
+ * Calls refreshValues() and clearSet() if it is successful.
*/
void commitValues();
/*! \brief
- * Store values in alternate locations.
+ * Updates alternative store locations.
+ *
+ * Derived classes should override this method if they implement
+ * alternative store locations, and copy/translate values from the
+ * values() vector to these alternative storages. They should also
+ * call the base class implementation as part of their implementation.
+ *
+ * Should be called in derived classes if values are modified directly
+ * through the values() method, e.g., in processAll(). Does not need
+ * to be called if commitValues() is used.
+ *
+ * Does not throw, and derived classes should not change that.
*/
virtual void refreshValues();
- //! Provides derived classes access to the current list of values.
+ /*! \brief
+ * Provides derived classes access to the current list of values.
+ *
+ * The non-const variant should only be used from processAll() in
+ * derived classes if necessary, and refreshValues() should be called
+ * if any changes are made.
+ */
ValueList &values() { return *_values; }
//! Provides derived classes access to the current list of values.
const ValueList &values() const { return *_values; }
* Vector for primary storage of option values.
*
* Is never NULL; points either to externally provided vector, or an
- * internally allocated one. The allocation is performed by init().
+ * internally allocated one. The allocation is performed by the
+ * constructor.
*
- * addValue() adds values only to this storage. Other memory locations
- * are updated only when processAll() is called.
+ * Primarily, modifications to values are done only to this storage,
+ * and other storage locations are updated only when refreshValues() is
+ * called.
*/
ValueList *_values;
T *_store;
"Internal inconsistency");
_values = new std::vector<T>;
}
- GMX_RELEASE_ASSERT(!hasFlag(efNoDefaultValue)
- || (settings._defaultValue == NULL
- && settings._defaultValueIfSet == NULL),
- "Option does not support default value, but one is set");
+ if (hasFlag(efNoDefaultValue)
+ && (settings._defaultValue != NULL
+ || settings._defaultValueIfSet != NULL))
+ {
+ GMX_THROW(APIError("Option does not support default value, but one is set"));
+ }
if (_store != NULL && _countptr == NULL && !hasFlag(efVector)
&& minValueCount() != maxValueCount())
{