carl  24.04
Computer ARithmetic Library
SettingsParser.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "settings_utils.h"
4 
5 #include <boost/program_options.hpp>
6 
7 #include <chrono>
8 #include <vector>
9 
10 #if __has_include(<filesystem>)
11 #include <filesystem>
12 // NOLINTNEXTLINE(cert-dcl58-cpp): sorry but we need this.
13 namespace std {
14 inline void validate(boost::any& v, const std::vector<std::string>& values, std::filesystem::path* /*unused*/, int /*unused*/) {
15  namespace pov = boost::program_options::validators;
16  namespace fs = std::filesystem;
17  fs::path p(pov::get_single_string(values));
18  if (fs::exists(p)) {
19  v = fs::canonical(p);
20  } else {
21  v = fs::absolute(p);
22  }
23 }
24 }
25 #endif
26 
27 namespace carl::settings {
28 
29 namespace po = boost::program_options;
30 
31 /**
32  * Inserts value into variables_map if it is not yet set.
33  * This method is intended as a helper for finalizer functions.
34  */
35 template<typename T>
36 void default_to(po::variables_map& values, const std::string& name, const T& value) {
37  auto it = values.find(name);
38  if (it == values.end()) {
39  values.emplace(name, po::variable_value(boost::any(value), true));
40  } else if (it->second.defaulted()) {
41  values.at(name) = po::variable_value(boost::any(value), false);
42  }
43 }
44 
45 /**
46  * Inserts or overwrites value into variables_map.
47  * This method is intended as a helper for finalizer functions.
48  */
49 template<typename T>
50 void overwrite_to(po::variables_map& values, const std::string& name, const T& value) {
51  auto it = values.find(name);
52  if (it == values.end()) {
53  values.emplace(name, po::variable_value(boost::any(value), true));
54  } else {
55  values.at(name) = po::variable_value(boost::any(value), false);
56  }
57 }
58 
59 // Predeclaration.
60 class SettingsParser;
61 
62 /// Helper class to nicely print the options that are available.
63 struct OptionPrinter {
64  /// Reference to parser.
66 };
67 /// Streaming operator for a option printer.
68 std::ostream& operator<<(std::ostream& os, OptionPrinter op);
69 
70 /// Helper class to nicely print the settings that were parsed.
72  /// Reference to parser.
74 };
75 /// Streaming operator for a settings printer.
76 std::ostream& operator<<(std::ostream& os, SettingsPrinter sp);
77 
78 /**
79  * Base class for a settings parser.
80  */
82  friend std::ostream& settings::operator<<(std::ostream& os, settings::OptionPrinter op);
83  friend std::ostream& settings::operator<<(std::ostream& os, settings::SettingsPrinter sp);
84 protected:
85  /// Stores the name of the current binary.
86  char* argv_zero = nullptr;
87  /// Stores the positional arguments.
88  po::positional_options_description mPositional;
89  /// Accumulates all available options.
90  po::options_description mAllOptions;
91  /// Stores the parsed values.
92  po::variables_map mValues;
93  /// Stores the individual options until the parser is finalized.
94  std::vector<po::options_description> mOptions;
95  /// Stores hooks for setting object finalizer functions.
96  std::vector<std::function<bool()>> mFinalizer;
97 
98  /// Checks for unrecognized options that were found.
99  void warn_for_unrecognized(const po::parsed_options& parsed) const;
100  /// Parses the command line.
101  void parse_command_line(int argc, char* argv[], bool allow_unregistered);
102  /// Parses the config file if one was configured.
103  void parse_config_file(bool allow_unregistered);
104  /// Calls the finalizer functions.
105  bool finalize_settings();
106 
107  /// Prints a warning if an option was unrecognized. Can be overridden.
108  virtual void warn_for_unrecognized_option(const std::string& s) const {
109  std::cerr << "Ignoring unrecognized option " << s << std::endl;
110  }
111  /// Prints a warning if loading the config file failed. Can be overridden.
112  virtual void warn_config_file(const std::string& file) const {
113  std::cerr << "Could not load config file " << file << std::endl;
114  }
115  /// Gives the option name for the config file name. Can be overridden.
116  virtual std::string name_of_config_file() const {
117  return "config";
118  }
119 public:
120  /// Virtual destructor.
121  virtual ~SettingsParser() = default;
122 
123  /// Finalizes the parser.
124  void finalize();
125 
126  /**
127  * Adds a new options_description with a title and a reference to the settings object.
128  * The settings object is needed to pass it to the finalizer function.
129  */
130  po::options_description& add(const std::string& title) {
131  return mOptions.emplace_back(po::options_description(title));
132  }
133  /**
134  * Adds a finalizer function to be called after parsing.
135  * boost::program_options::notify() is called before running the finalizer functions.
136  * The finalizer function should accept a boost::program_options::variables_map as its only argument and should return a bool indicating whether it changed the variables map.
137  * If any finalizer changed the variables map, boost::program_options::notify() is called again afterwards.
138  */
139  template<typename F>
140  void add_finalizer(F&& f) {
141  mFinalizer.emplace_back([this,f](){ return f(mValues); });
142  }
143 
144  /**
145  * Parse the options.
146  * If allow_unregistered is set to true, we allow them but call warn_for_unrecognized_option() for each one.
147  * Otherwise an exception is raised when an unrecognized option is encountered.
148  */
149  void parse_options(int argc, char* argv[], bool allow_unregistered = true);
150 
151  /**
152  * Print a help page.
153  * Returns a helper object so that it can be used as follows:
154  * `std::cout << parser.print_help() << std::endl;`
155  */
157  return OptionPrinter{*this};
158  }
159 
160  /**
161  * Print the parsed settings.
162  * Returns a helper object so that it can be used as follows:
163  * `std::cout << parser.print_options() << std::endl;`
164  */
166  return SettingsPrinter{*this};
167  }
168 };
169 
170 }
void default_to(po::variables_map &values, const std::string &name, const T &value)
Inserts value into variables_map if it is not yet set.
void validate(boost::any &v, const std::vector< std::string > &values, carl::settings::duration *, int)
Custom validator for duration that wraps some std::chrono::duration.
void overwrite_to(po::variables_map &values, const std::string &name, const T &value)
Inserts or overwrites value into variables_map.
std::ostream & operator<<(std::ostream &os, const duration &d)
Streaming operator for duration. Auto-detects proper time suffix.
Helper class to nicely print the options that are available.
const SettingsParser & parser
Reference to parser.
Helper class to nicely print the settings that were parsed.
const SettingsParser & parser
Reference to parser.
Base class for a settings parser.
void parse_options(int argc, char *argv[], bool allow_unregistered=true)
Parse the options.
virtual ~SettingsParser()=default
Virtual destructor.
SettingsPrinter print_options() const
Print the parsed settings.
void finalize()
Finalizes the parser.
bool finalize_settings()
Calls the finalizer functions.
po::options_description & add(const std::string &title)
Adds a new options_description with a title and a reference to the settings object.
std::vector< po::options_description > mOptions
Stores the individual options until the parser is finalized.
void parse_config_file(bool allow_unregistered)
Parses the config file if one was configured.
void add_finalizer(F &&f)
Adds a finalizer function to be called after parsing.
virtual void warn_config_file(const std::string &file) const
Prints a warning if loading the config file failed. Can be overridden.
po::options_description mAllOptions
Accumulates all available options.
po::positional_options_description mPositional
Stores the positional arguments.
virtual void warn_for_unrecognized_option(const std::string &s) const
Prints a warning if an option was unrecognized. Can be overridden.
virtual std::string name_of_config_file() const
Gives the option name for the config file name. Can be overridden.
po::variables_map mValues
Stores the parsed values.
void warn_for_unrecognized(const po::parsed_options &parsed) const
Checks for unrecognized options that were found.
void parse_command_line(int argc, char *argv[], bool allow_unregistered)
Parses the command line.
OptionPrinter print_help() const
Print a help page.
std::vector< std::function< bool()> > mFinalizer
Stores hooks for setting object finalizer functions.
char * argv_zero
Stores the name of the current binary.