--- /dev/null
+New: ParameterHandler: add functionality ensuring that parameters are
+indeed set in order to avoid errors that remain unrecognized otherwise.
+By extending the interfaces of declare_entry() and add_parameter(),
+parameters can be declared as mandatory parameters that must be set
+when parsing from an input file or by a set() function.
+<br>
+(Niklas Fehn, 2020/03/23)
* Declare a new entry with name <tt>entry</tt>, default and for which any
* input has to match the <tt>pattern</tt> (default: any pattern).
*
- * The last parameter defaulting to an empty string is used to add a
- * documenting text to each entry which will be printed as a comment when
- * this class is asked to write out all declarations to a stream using the
- * print_parameters() function.
- *
* The function generates an exception of type ExcValueDoesNotMatchPattern
* if the default value doesn't match the given pattern, using the C++ throw
* mechanism. However, this exception is only generated <i>after</i> the
* for a parameter is possible, you can then catch and ignore this
* exception.
*
+ * The parameter @p documentation defaulting to an empty string is used
+ * to add a documenting text to each entry which will be printed as a comment
+ * when this class is asked to write out all declarations to a stream using
+ * the print_parameters() function.
+ *
+ * The parameter @p has_to_be_set can be used in order to declare this
+ * parameter as a parameter whose default value has to be overwritten by
+ * one of the methods provided by this class. Whether a parameter has been set
+ * succesfully can be queried by the functions get_entries_wrongly_not_set()
+ * and assert_that_entries_have_been_set().
+ *
* @note An entry can be declared more than once without generating an
* error, for example to override an earlier default value.
*/
declare_entry(const std::string & entry,
const std::string & default_value,
const Patterns::PatternBase &pattern = Patterns::Anything(),
- const std::string & documentation = "");
+ const std::string & documentation = "",
+ const bool has_to_be_set = false);
/**
* Attach an action to the parameter with name @p entry in the current
*
* By default, the pattern to use is obtained by calling the function
* Patterns::Tools::Convert<T>::to_pattern(), but a custom one can be used.
+ *
+ * The parameter @p has_to_be_set can be used in order to declare this
+ * parameter as a parameter whose default value has to be overwritten by
+ * one of the methods provided by this class. Whether a parameter has been set
+ * succesfully can be queried by the functions get_entries_wrongly_not_set()
+ * and assert_that_entries_have_been_set().
*/
template <class ParameterType>
void
ParameterType & parameter,
const std::string & documentation = "",
const Patterns::PatternBase &pattern =
- *Patterns::Tools::Convert<ParameterType>::to_pattern());
+ *Patterns::Tools::Convert<ParameterType>::to_pattern(),
+ const bool has_to_be_set = false);
/**
* Create an alias for an existing entry. This provides a way to refer to a
bool
operator==(const ParameterHandler &prm2) const;
+ /**
+ * Return a set of parameter names (including subsection names) corresponding
+ * to those entries of the parameter handler that have not been set by one of
+ * the functions parsing parameters from an input file or by an explicit call
+ * to one of the set() functions, but that have been declared as mandatory
+ * parameters that must be set (through the last argument of the
+ * declare_entry() function or add_parameter() function).
+ */
+ std::set<std::string>
+ get_entries_wrongly_not_set() const;
+
+ /**
+ * Asserts that those entries of the parameter handler with flag
+ * `has_to_be_set = true` have been set. An exception is invoked
+ * if at least one of these parameters has not been set.
+ */
+ void
+ assert_that_entries_have_been_set() const;
+
/**
* @addtogroup Exceptions
* @{
*/
std::unique_ptr<boost::property_tree::ptree> entries;
+ /**
+ * A map that stores a pair of boolean variables for each entry
+ * added to the parameter handler. The first bool describes whether the
+ * parameter has to be set according to the last argument of the functions
+ * declare_entry() or add_parameter(), and the second bool contains the
+ * information whether the parameter has been set by any of the functions
+ * parsing input parameters or by a set function of this class.
+ */
+ std::map<std::string, std::pair<bool, bool>> entries_set_status;
+
/**
* A list of patterns that are used to describe the parameters of this
* object. Every nodes in the property tree corresponding to a parameter
ParameterHandler::add_parameter(const std::string & entry,
ParameterType & parameter,
const std::string & documentation,
- const Patterns::PatternBase &pattern)
+ const Patterns::PatternBase &pattern,
+ const bool has_to_be_set)
{
static_assert(std::is_const<ParameterType>::value == false,
"You tried to add a parameter using a type "
Patterns::Tools::Convert<ParameterType>::to_string(
parameter, pattern.clone()),
pattern,
- documentation);
+ documentation,
+ has_to_be_set);
std::string path = get_current_full_path(entry);
const unsigned int pattern_index =
}
// While it does not make much sense for anyone to actually do this, allow
- // the last line to end in a backslash. to do do, we need to parse
+ // the last line to end in a backslash. To do so, we need to parse
// whatever was left in the stash of concatenated lines
if (is_concatenated)
scan_line_or_cleanup(fully_concatenated_line, filename, current_line_n);
ParameterHandler::clear()
{
entries = std_cxx14::make_unique<boost::property_tree::ptree>();
+ entries_set_status.clear();
}
ParameterHandler::declare_entry(const std::string & entry,
const std::string & default_value,
const Patterns::PatternBase &pattern,
- const std::string & documentation)
+ const std::string & documentation,
+ const bool has_to_be_set)
{
entries->put(get_current_full_path(entry) + path_separator + "value",
default_value);
entries->put(get_current_full_path(entry) + path_separator + "documentation",
documentation);
+ // initialize with false
+ const std::pair<bool, bool> set_status =
+ std::pair<bool, bool>(has_to_be_set, false);
+ entries_set_status.insert(
+ std::pair<std::string, std::pair<bool, bool>>(get_current_full_path(entry),
+ set_status));
+
patterns.reserve(patterns.size() + 1);
patterns.emplace_back(pattern.clone());
entries->put(get_current_full_path(entry) + path_separator + "pattern",
// finally write the new value into the database
entries->put(path + path_separator + "value", new_value);
+
+ auto map_iter = entries_set_status.find(path);
+ if (map_iter != entries_set_status.end())
+ map_iter->second = std::pair<bool, bool>(map_iter->second.first, true);
+ else
+ AssertThrow(false,
+ ExcMessage("Could not find parameter " + path +
+ " in map entries_set_status."));
}
else
AssertThrow(false, ExcEntryUndeclared(entry_string));
+std::set<std::string>
+ParameterHandler::get_entries_wrongly_not_set() const
+{
+ std::set<std::string> entries_wrongly_not_set;
+
+ for (auto it : entries_set_status)
+ if (it.second.first == true && it.second.second == false)
+ entries_wrongly_not_set.insert(it.first);
+
+ return entries_wrongly_not_set;
+}
+
+
+
+void
+ParameterHandler::assert_that_entries_have_been_set() const
+{
+ const std::set<std::string> entries_wrongly_not_set =
+ this->get_entries_wrongly_not_set();
+
+ if (entries_wrongly_not_set.size() > 0)
+ {
+ std::string list_of_missing_parameters = "\n\n";
+ for (const auto &it : entries_wrongly_not_set)
+ list_of_missing_parameters += " " + it + "\n";
+ list_of_missing_parameters += "\n";
+
+ AssertThrow(
+ entries_wrongly_not_set.size() == 0,
+ ExcMessage(
+ "Not all entries of the parameter handler that were declared with "
+ "`has_to_be_set = true` have been set. The following parameters " +
+ list_of_missing_parameters +
+ " have not been set. "
+ "A possible reason might be that you did not add these parameter to "
+ "the input file or that their spelling is not correct."));
+ }
+}
+
+
+
MultipleParameterLoop::MultipleParameterLoop()
: n_branches(0)
{}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2019 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#include <deal.II/base/parameter_handler.h>
+
+#include <map>
+
+#include "../tests.h"
+
+void
+success()
+{
+ unsigned int dim = 2;
+ std::string precision = "double";
+
+ ParameterHandler prm;
+ prm.enter_subsection("General");
+ // this one does not have to be set
+ prm.add_parameter("dim",
+ dim,
+ "Number of space dimensions",
+ Patterns::Integer(2, 3));
+ // this one has to be set
+ prm.declare_entry("Precision",
+ precision,
+ Patterns::Selection("float|double"),
+ "Floating point precision",
+ true);
+ prm.leave_subsection();
+
+ // this try-catch simulates parsing from an incomplete/incorrect input file
+ try
+ {
+ prm.enter_subsection("General");
+ prm.set("Precision", "float");
+ prm.leave_subsection();
+ }
+ catch (std::exception &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+
+ // check set status
+ try
+ {
+ prm.assert_that_entries_have_been_set();
+ }
+ catch (std::exception &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+
+ deallog << std::endl << "successful" << std::endl;
+}
+
+void
+fail()
+{
+ unsigned int dim = 2;
+ std::string precision = "double";
+
+ ParameterHandler prm;
+ prm.enter_subsection("General");
+ // both parameters have to be set
+ prm.add_parameter(
+ "dim", dim, "Number of space dimensions", Patterns::Integer(2, 3), true);
+ prm.add_parameter("Precision",
+ precision,
+ "Floating point precision",
+ Patterns::Selection("float|double"),
+ true);
+ prm.leave_subsection();
+
+ // this try-catch simulates parsing from an incomplete/incorrect input file
+ try
+ {
+ prm.enter_subsection("General");
+ prm.set("Precison", "float"); // here is a typo!
+ // dim is not set!
+ prm.leave_subsection();
+ }
+ catch (std::exception &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+
+ // check set status
+ try
+ {
+ prm.assert_that_entries_have_been_set();
+ }
+ catch (std::exception &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+}
+
+
+int
+main()
+{
+ initlog();
+ deallog.get_file_stream().precision(3);
+
+ try
+ {
+ success();
+ fail();
+ }
+ catch (std::exception &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+}
--- /dev/null
+
+DEAL::
+DEAL::successful
+DEAL::
+--------------------------------------------------------
+An error occurred in file <parameter_handler.cc> in function
+ void dealii::ParameterHandler::set(const string&, const string&)
+The violated condition was:
+ false
+Additional information:
+ You can't ask for entry <Precison> you have not yet declared.
+--------------------------------------------------------
+
+DEAL::
+--------------------------------------------------------
+An error occurred in file <parameter_handler.cc> in function
+ void dealii::ParameterHandler::assert_that_entries_have_been_set() const
+The violated condition was:
+ entries_wrongly_not_set.size() == 0
+Additional information:
+ Not all entries of the parameter handler that were declared with `has_to_be_set = true` have been set. The following parameters
+
+ General.Precision
+ General.dim
+
+ have not been set. A possible reason might be that you did not add these parameter to the input file or that their spelling is not correct.
+--------------------------------------------------------
+