From 8160c134587584992a232078acc8ca471ddb56b4 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Tue, 17 Mar 2020 17:28:04 -0600 Subject: [PATCH] More documentation around Configuration/ConfigDefinition --- llarp/config/definition.cpp | 2 +- llarp/config/definition.hpp | 80 ++++++++++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/llarp/config/definition.cpp b/llarp/config/definition.cpp index 3b6457afc..c09b6c7e5 100644 --- a/llarp/config/definition.cpp +++ b/llarp/config/definition.cpp @@ -77,7 +77,7 @@ Configuration::generateINIConfig(bool useValues) visitDefinitions(section, [&](const std::string& name, const ConfigDefinition_ptr& def) { if (useValues and def->numFound > 0) { - oss << name << "=" << def->writeValue(false) << "\n"; + oss << name << "=" << def->valueAsString(false) << "\n"; } else { diff --git a/llarp/config/definition.hpp b/llarp/config/definition.hpp index 3d5cb6ba2..64e83925d 100644 --- a/llarp/config/definition.hpp +++ b/llarp/config/definition.hpp @@ -13,7 +13,10 @@ namespace llarp { - /// non-templated base class for all config definition types. + /// A base class for specifying config options and their constraints. The basic to/from string + /// type functions are provided pure-virtual. The type-aware implementations which implement these + /// functions are templated classes. One reason for providing a non-templated base class is so + /// that they can all be mixed into the same containers (albiet as pointers). struct ConfigDefinitionBase { ConfigDefinitionBase(std::string section_, @@ -24,17 +27,25 @@ namespace llarp virtual ~ConfigDefinitionBase() {} - /// subclasses should provide their default value as a string + /// Subclasses should provide their default value as a string + /// + /// @return the option's default value represented as a string virtual std::string defaultValueAsString() = 0; - /// subclasses should parse and store the provided input + /// Subclasses should parse and store the provided input + /// + /// @param input is the string input to interpret virtual void parseValue(const std::string& input) = 0; - /// subclasess should write their parsed value (not default value) as a string + /// Subclasess should write their parsed value as a string, optionally falling back to any + /// specified default if `useDefault` is true. + /// + /// @param useDefault should specify whether to fallback to default when possible + /// @return the option's value as a string virtual std::string - writeValue(bool useDefault) = 0; + valueAsString(bool useDefault) = 0; std::string section; std::string name; @@ -43,9 +54,21 @@ namespace llarp size_t numFound = 0; }; + /// The primary type-aware implementation of ConfigDefinitionBase, this templated class allows + /// for implementations which can use the std::ostringstream and std::istringstream for to/from + /// string functionality. + /// + /// Note that types (T) used as template parameters here must be used verbatim when calling + /// Configuration::getConfigValue(). Similar types such as uint32_t and int32_t cannot be mixed. template struct ConfigDefinition : public ConfigDefinitionBase { + /// Constructor. Arguments are passed directly to ConfigDefinitionBase. Also accepts a default + /// value, which is used in the following situations: + /// + /// 1) as the return value for getValue() if there is no parsed value and required==false + /// 2) as the output in defaultValueAsString(), used to generate config files + /// 3) as the output in valueAsString(), used to generate config files ConfigDefinition(std::string section_, std::string name_, bool required_, @@ -56,6 +79,10 @@ namespace llarp { } + /// Returns the parsed value, if available. Otherwise, provides the default value if the option + /// is not required. Otherwise, returns an empty optional. + /// + /// @return an optional with the parsed value, the default value, or no value. nonstd::optional getValue() const { @@ -68,7 +95,7 @@ namespace llarp } std::string - defaultValueAsString() + defaultValueAsString() override { std::ostringstream oss; if (defaultValue.has_value()) @@ -78,7 +105,7 @@ namespace llarp } void - parseValue(const std::string& input) + parseValue(const std::string& input) override { if (not multiValued and parsedValue.has_value()) { @@ -102,7 +129,7 @@ namespace llarp } std::string - writeValue(bool useDefault) + valueAsString(bool useDefault) override { std::ostringstream oss; if (parsedValue.has_value()) @@ -126,15 +153,46 @@ namespace llarp // map of section-name to map-of-definitions using SectionMap = std::unordered_map; - /// A configuration holds an ordered set of ConfigDefinitions defining the allowable values and - /// their constraints and an optional set defining overrides of those values (e.g. the results - /// of a parsed config file). + /// A Configuration holds an ordered set of ConfigDefinitions defining the allowable values and + /// their constraints (specified through calls to addConfigOption()). + /// + /// The layout and grouping of the config options are modelled after the INI file format; each + /// option has a name and is grouped under a section. Duplicate option names are allowed only if + /// they exist in a different section. The configuration can be serialized in the INI file format + /// using the generateINIConfig() function. + /// + /// Configured values (e.g. those encountered when parsing a file) can be provided through calls + /// to addConfigValue(). These take a std::string as a value, which is automatically parsed. + /// + /// The Configuration can be used to print out a full config string (or file), including fields + /// with defaults and optionally fields which have a specified value (values provided through + /// calls to addConfigValue()). struct Configuration { SectionMap m_definitions; + /// Spefify the parameters and type of a configuration option. The parameters are members of + /// ConfigDefinitionBase; the type is inferred from ConfigDefinition's template parameter T. + /// + /// This function should be called for every option that this Configuration supports, and should + /// be done before any other interractions involving that option. + /// + /// @param def should be a unique_ptr to a valid subclass of ConfigDefinitionBase + /// @return `*this` for chaining calls + /// @throws std::invalid_argument if the option already exists Configuration& addConfigOption(ConfigDefinition_ptr def); + /// Specify a config value for the given section and name. The value should be a valid string + /// representing the type used by the option (e.g. the type provided when addConfigOption() was + /// called). + /// + /// If the specified option doesn't exist, an exception will be thrown. Otherwise, the option's + /// parsedValue() will be invoked, and should throw an exception if the string can't be parsed. + /// + /// @param section is the section this value resides in + /// @param name is the name of the value + /// @return `*this` for chaining calls + /// @throws if the option doesn't exist or the provided string isn't parseable Configuration& addConfigValue(string_view section, string_view name,