Updated options module documentation.
authorTeemu Murtola <teemu.murtola@cbr.su.se>
Sun, 31 Jul 2011 17:35:32 +0000 (20:35 +0300)
committerTeemu Murtola <teemu.murtola@cbr.su.se>
Mon, 1 Aug 2011 17:45:12 +0000 (20:45 +0300)
Documentation now includes information on how errors are reported using
exceptions from the module.  Also some other updates and added comments.

12 files changed:
src/gromacs/options/abstractoption.cpp
src/gromacs/options/abstractoption.h
src/gromacs/options/abstractoptionstorage.h
src/gromacs/options/basicoptions.h
src/gromacs/options/cmdlineparser.h
src/gromacs/options/globalproperties.h
src/gromacs/options/options-impl.h
src/gromacs/options/options.cpp
src/gromacs/options/options.h
src/gromacs/options/optionsassigner.cpp
src/gromacs/options/optionsassigner.h
src/gromacs/options/optionstoragetemplate.h

index 57e5d2bdf4be2066edef8c6c50be6c05eb675c5e..bf897e8cedb15708d4949d9402e244c6cc7785d0 100644 (file)
@@ -89,7 +89,6 @@ void AbstractOptionStorage::startSource()
 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.
@@ -97,6 +96,7 @@ void AbstractOptionStorage::startSet()
     {
         GMX_THROW(InvalidInputError("Option specified multiple times"));
     }
+    clearSet();
     _inSet = true;
 }
 
@@ -110,6 +110,7 @@ void AbstractOptionStorage::finishSet()
 {
     GMX_RELEASE_ASSERT(_inSet, "startSet() not called");
     _inSet = false;
+    // TODO: Should this be done only when processSet() does not throw?
     setFlag(efSet);
     processSet();
 }
index 28611830854e06df8b7f9484c3446d9c14601454..348a01ef6909e11a69fe2351af8d9fd0a74626a0 100644 (file)
@@ -41,7 +41,7 @@
  *
  * 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
@@ -95,22 +95,24 @@ class AbstractOption
          *
          * \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.
@@ -188,7 +190,7 @@ class ConcreteOption : public OptionTemplate<int, ConcreteOption>
  * \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.
@@ -270,6 +272,9 @@ class OptionTemplate : public AbstractOption
          * \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.
          */
@@ -280,6 +285,9 @@ class OptionTemplate : public AbstractOption
          *
          * \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.
          */
@@ -290,8 +298,10 @@ class OptionTemplate : public AbstractOption
          *
          * \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.
index c608416a39709b62fdec9c825444a6f4b1c44f1d..749972ae403882fd70c405c36b75fd23be172b7b 100644 (file)
@@ -114,25 +114,41 @@ class AbstractOptionStorage
          * 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.
          */
@@ -140,7 +156,10 @@ class AbstractOptionStorage
         /*! \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();
@@ -151,10 +170,13 @@ class AbstractOptionStorage
          *
          * \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); }
@@ -171,14 +193,14 @@ class AbstractOptionStorage
          * 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
@@ -186,14 +208,14 @@ class AbstractOptionStorage
          *
          * \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);
 
@@ -207,29 +229,48 @@ class AbstractOptionStorage
          *
          * 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;
 
index 1cf69dc5a5c20eae2dac813767911b90b3f4e2a2..1a82604a0c9219c6452443d31afe44c771a8fbd0 100644 (file)
@@ -72,6 +72,8 @@ using gmx::BooleanOption;
 options.addOption(BooleanOption("pbc").store(&bPBC));
  * \endcode
  *
+ * Public methods in this class do not throw.
+ *
  * \inpublicapi
  */
 class BooleanOption : public OptionTemplate<bool, BooleanOption>
@@ -84,6 +86,7 @@ class BooleanOption : public OptionTemplate<bool, BooleanOption>
         }
 
     protected:
+        //! Creates a BooleanOptionStorage object.
         virtual AbstractOptionStorage *createDefaultStorage(Options *options) const;
 };
 
@@ -101,6 +104,8 @@ int  box[3] = {1, 1, 1};  // Default value
 options.addOption(IntegerOption("box").store(box).vector());
  * \endcode
  *
+ * Public methods in this class do not throw.
+ *
  * \inpublicapi
  */
 class IntegerOption : public OptionTemplate<int, IntegerOption>
@@ -120,6 +125,7 @@ class IntegerOption : public OptionTemplate<int, IntegerOption>
         MyClass &vector() { setVector(); return me(); }
 
     protected:
+        //! Creates an IntegerOptionStorage object.
         virtual AbstractOptionStorage *createDefaultStorage(Options *options) const;
 
         /*! \brief
@@ -132,6 +138,8 @@ class IntegerOption : public OptionTemplate<int, IntegerOption>
 /*! \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>
@@ -146,10 +154,19 @@ 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;
@@ -178,6 +195,8 @@ options.addOption(StringOption("type").enumValue(allowed).store(&str)
                      .storeEnumIndex(&type));
  * \endcode
  *
+ * Public methods in this class do not throw.
+ *
  * \inpublicapi
  */
 class StringOption : public OptionTemplate<std::string, StringOption>
@@ -187,13 +206,14 @@ 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
@@ -233,6 +253,7 @@ class StringOption : public OptionTemplate<std::string, StringOption>
         { _enumIndexStore = store; return me(); }
 
     protected:
+        //! Creates a StringOptionStorage object.
         virtual AbstractOptionStorage *createDefaultStorage(Options *options) const;
         virtual std::string createDescription() const;
 
@@ -251,6 +272,10 @@ class StringOption : public OptionTemplate<std::string, StringOption>
 /*! \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>
@@ -289,6 +314,7 @@ 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:
index 5698b49dcf732244ddc1e10e69e90e3e6f084a6b..4f480f284d0b94e9e0a33d6e82a6f7f0605ff15f 100644 (file)
@@ -72,6 +72,11 @@ class CommandLineParser
 
         /*! \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[]);
 
index 0f2be17ed42db982efa53616c3aee4f82821ece8..a3d3f08c69c75748ae70294a70087ab5e1cc6e6e 100644 (file)
@@ -96,7 +96,7 @@ class OptionsGlobalProperties
          * code.
          *
          * Currently, the returned structure is always filled with default
-         * values.
+         * values for most fields.
          *
          * \deprecated
          */
@@ -114,6 +114,17 @@ class OptionsGlobalProperties
         {
             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.
index 53d1159280bded8bb141b62b8a71c5f35c232f21..f8b3343e5f714cb87b434e342f02b1087ff352fc 100644 (file)
@@ -74,6 +74,8 @@ class Options::Impl
          *
          * \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
@@ -81,11 +83,16 @@ class Options::Impl
          *
          * \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();
 
index b5e157038086e34eb486cdd340245cd520774829..2e39a824f33df7f10f594ff6b1c63b6e4c962a2f 100644 (file)
@@ -52,6 +52,7 @@
 namespace gmx
 {
 
+// TODO: Move this into the utility module
 static std::string composeString(const char *const *sarray)
 {
     std::string result;
index e71498b1c0dbef76b0a92d9e43dfc9067a797975..7dcef77677e1a146b3bb06144a0e9c477e513ef8 100644 (file)
@@ -109,15 +109,27 @@ class Options
         //! 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
@@ -128,13 +140,13 @@ class Options
          * 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.
          */
@@ -151,17 +163,21 @@ class Options
          */
         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();
 
@@ -172,6 +188,9 @@ class Options
          * 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();
index 809e2b42c69f216abc4722eef00033eb065e6349..af0991bedc1606b12599786e796a2ea44ddf4c63 100644 (file)
@@ -203,6 +203,8 @@ void OptionsAssigner::appendValue(const std::string &value)
 {
     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);
 }
@@ -216,6 +218,8 @@ void OptionsAssigner::finishOption()
     {
         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)
index 9872dee730f3fc12a40ae12a60a87a29ba241e2c..de6c557305410c90eee50e372f07a5881397407b 100644 (file)
@@ -54,32 +54,27 @@ class Options;
  * 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
  */
@@ -104,6 +99,8 @@ class OptionsAssigner
          *
          * 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
@@ -118,41 +115,85 @@ class OptionsAssigner
          *
          * 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();
 
index e2c1324c25ddf72a2ec88df3ca54a02afaf2babc..50c23c78292a8ce3882b2b243792fc34c7cf408a 100644 (file)
@@ -53,15 +53,26 @@ namespace gmx
 
 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
@@ -87,9 +98,12 @@ class OptionStorageTemplate : public AbstractOptionStorage
         /*! \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,
@@ -100,26 +114,36 @@ class OptionStorageTemplate : public AbstractOptionStorage
         /*! \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()
@@ -132,32 +156,64 @@ class OptionStorageTemplate : public AbstractOptionStorage
 
         /*! \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; }
@@ -171,10 +227,12 @@ class OptionStorageTemplate : public AbstractOptionStorage
          * 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;
@@ -203,10 +261,12 @@ OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U> &sett
                            "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())
     {