QwAnalysis
QwOptions.h
Go to the documentation of this file.
1 /*!
2  * \file QwOptions.h
3  * \brief An options class which parses command line, config file and environment
4  *
5  * \author Wouter Deconinck
6  * \date 2009-12-01
7  */
8 
9 #ifndef QWOPTIONS_H
10 #define QWOPTIONS_H
11 
12 // System headers
13 #include <iostream>
14 #include <string>
15 #include <vector>
16 
17 // Boost headers
18 #include <boost/program_options.hpp>
19 namespace po = boost::program_options;
20 
21 // ROOT headers
22 #include <TString.h>
23 
24 // Qweak headers
25 #include "QwLog.h"
26 
27 // Helper functions for safe environment variable parsing
28 inline const char* getenv_safe (const char* name) {
29  if (getenv(name))
30  return getenv(name);
31  else {
32  QwError << "Environment variable " << name << " undefined!" << QwLog::endl;
33  QwWarning << "Using current directory instead." << QwLog::endl;
34  return ".";
35  }
36 }
37 inline const std::string getenv_safe_string (const char* name) {
38  return std::string(getenv_safe(name));
39 }
40 inline const TString getenv_safe_TString (const char* name) {
41  return TString(getenv_safe(name));
42 }
43 
44 
45 // Starting with boost::program_options 1.35.0 there is support for the semantic
46 // implicit_value. An explicit option value is optional, but if given, must be
47 // strictly adjacent to the option.
48 #if BOOST_VERSION >= 103500
49 #define default_bool_value(b) default_value(b)->implicit_value(true)
50 #else
51 #define default_bool_value(b) default_value(b)
52 #endif
53 
54 
55 /**
56  * \class QwOptions
57  * \ingroup QwAnalysis
58  * \brief An options class
59  *
60  * On instantiation of the global gQwOptions object the argc and argv are passed.
61  * The filename is set to a default value, but on instantiation a check is done
62  * for the option --config filename. After this no parsing is done without the
63  * user requesting a value.
64  *
65  * To use this class in your own programs, just include the header file. This
66  * will make the global object gQwOptions available. Set the command line and
67  * config file with the methods SetCommandLine() and SetConfigFile() in the main
68  * routine. In other classes you can add subsystem-specific config files with
69  * the AddConfigFile() method.
70  * \code
71  * gQwOptions.SetCommandLine(argc, argv); // store command line arguments
72  * gQwOptions.SetConfigFile("qwtracking.conf"); // set the default config file
73  * gQwOptions.AddConfigFile("qweak-tracking.conf"); // add another config file
74  * \endcode
75  *
76  * Define your own options by calling the AddOptions() method. Look in the
77  * documentation of boost::program_options for the allowed syntax if you need
78  * more than just '--key value' pairs. Positional arguments are supported
79  * (untested), default values can and should preferably be given, and multiple
80  * values can be put into a vector corresponding to a single key (untested).
81  * \code
82  * gQwOptions.AddOptions()("int,i", po::value<int>(), "integer-valued option");
83  * gQwOptions.AddOptions()("bool,b", po::value<bool>(), "boolean-valued option");
84  * gQwOptions.AddOptions()("float,f", po::value<float>(), "float-valued option");
85  * gQwOptions.AddOptions()("string,s", po::value<string>(), "string-valued option");
86  * gQwOptions.AddOptions()("range,r", po::value<string>(), "range-valued option");
87  * gQwOptions.AddOptions()("def-int", po::value<int>()->default_value(1), "int-valued option, default of 1");
88  * \endcode
89  * For boolean-valued options you might want to use zero_tokens() to allow the
90  * option to be specified as --bool instead of --bool yes.
91  * \code
92  * gQwOptions.AddOptions()("bool,b", po::value<bool>()->zero_tokens(), "boolean-valued option");
93  * \endcode
94  * Keep in mind, though, that this then ignores the value after the option completely,
95  * and it will not allow you to unset settings in the default configuration file.
96  * A better approach is to use the implicit_value(b) semantic that will still accept
97  * the value after the option. However, this was only introduced in boost 1.35.0.
98  * To avoid the need to test for this, use the syntac default_bool_value(b), with b
99  * the default value. If the flag is specified, the option will be set to true
100  * regardless of the specified default value.
101  *
102  * It is easiest if you define your options in a static function DefineOptions()
103  * and then call that function in the QwOptionsTracking.h or QwOptionsParity.h
104  * header files. This ensures that only a single call to gQwOptions.DefineOptions()
105  * will load all the options and provide the information on a --help call.
106  *
107  * To use the options, check first for the existence with HasValue(key), then
108  * get their value using GetValue<type>(key). Flags defined without type are
109  * treated as 'existence-only' keys, so use HasValue(key) on them instead of
110  * GetValue<bool>(key). If you define an option as bool, you should still use
111  * the regular GetValue<bool>(key) to retrieve the value.
112  * \code
113  * // Test for presence of the option before using its value
114  * if (gQwOptions.HasValue("string")) string my_string = gQwOptions.GetValue<string>("string");
115  * // Or use options with default values (preferable)
116  * int my_int = gQwOptions.GetValue<int>("def-int");
117  * \endcode
118  *
119  * To get the results of an integer range argument (strictly speaking a string
120  * argument), you can use GetIntValuePair() which returns a pair<int,int>, or
121  * the more specific GetIntValuePairFirst() and GetIntValuePairLast().
122  * \code
123  * std::pair<int,int> my_run_pair = gQwOptions.GetIntValuePair("run");
124  * gQwOptions.GetIntValuePair("run").first == gQwOptions.GetIntValuePairFirst("run");
125  * gQwOptions.GetIntValuePair("run").second == gQwOptions.GetIntValuePairLast("run");
126  * \endcode
127  *
128  * The default allowed options are:
129  * - --usage: print a help message
130  * - --help, -h: print a help message
131  * - --config, -c: configuration file to read
132  */
133 class QwOptions {
134 
135  public:
136 
137  /// \brief Default constructor
138  QwOptions();
139 
140  /// \brief Constructor with command line arguments
141  QwOptions(int argc, char* argv[]) {
142  SetCommandLine(argc, argv);
143  };
144  /// \brief Constructor with configuration file
145  QwOptions(const std::string& configfile) {
146  SetConfigFile(configfile);
147  };
148  /// \brief Constructor with command line arguments and configuration file
149  QwOptions(int argc, char* argv[], const std::string& configfile) {
150  SetCommandLine(argc, argv);
151  SetConfigFile(configfile);
152  };
153 
154  /// \brief Default destructor
155  virtual ~QwOptions();
156 
157 
158  /// \brief Add a default option
159  po::options_description_easy_init AddDefaultOptions() {
160  return AddOptions("Default options");
161  };
162  /// \brief Add an option to a named block or create new block
163  po::options_description_easy_init
164  AddOptions(const std::string& blockname = "Specialized options") {
165  // Clear the parsed options
166  Clear();
167  // Note: Would like to solve this with find_if(b,e,bind()) but no access to block name
168  // Search for the block name if it exists
169  for (size_t i = 0; i < fOptionBlockName.size(); i++)
170  if (blockname == fOptionBlockName.at(i))
171  return fOptionBlock.at(i)->add_options();
172  // Create new block if not existing yet
173  fOptionBlock.push_back(new po::options_description(blockname));
174  fOptionBlockName.push_back(blockname);
175  return fOptionBlock.back()->add_options();
176  };
177 
178 
179  /// \brief Print usage information
180  void Usage();
181 
182  /// \brief Print version string
183  void Version();
184 
185  /// \brief Define the options
186  static void DefineOptions(QwOptions& options);
187 
188  /// \brief Set the command line arguments
189  void SetCommandLine(int argc, char* argv[], bool default_config_file = true);
190 
191  /// \brief Set a configuration file
192  void SetConfigFile(const std::string& configfile) {
193  fConfigFiles.clear();
194  fConfigFiles.push_back(configfile);
195  fParsed = false;
196  };
197 
198  /// \brief Add a configuration file
199  void AddConfigFile(const std::string& configfile) {
200  QwMessage << "Adding user-defined configuration file "
201  << configfile << QwLog::endl;
202  fConfigFiles.push_back(configfile);
203  fParsed = false;
204  };
205 
206  /// \brief Add some configuration files
207  void AddConfigFile(std::vector<std::string> configfiles) {
208  for (size_t i = 0; i < configfiles.size(); i++)
209  fConfigFiles.push_back(configfiles.at(i));
210  fParsed = false;
211  };
212 
213  /// \brief List the configuration files
215  QwMessage << "Config files:" << QwLog::endl;
216  for (size_t i = 0; i < fConfigFiles.size(); i++)
217  QwMessage << fConfigFiles.at(i) << QwLog::endl;
218  };
219 
220 
221  /// \brief Parse all sources of options
222  void Parse(bool force = false) {
223  if (! fParsed || force) {
226  ParseConfigFile();
227  fParsed = true;
228  }
229  };
230 
231 
232  /// \brief Has this key been defined
233  bool HasValue(const std::string& key) {
234  if (fParsed == false) Parse();
235  return (fVariablesMap.count(key) > 0);
236  };
237 
238  /// \brief Get a templated value
239  template < class T >
240  T GetValue(const std::string& key) {
241  if (fParsed == false) Parse();
242  if (fVariablesMap.count(key)) {
243  QwVerbose << "Option " << key << ": "
244  << fVariablesMap[key].as<T>() << QwLog::endl;
245  return fVariablesMap[key].as<T>();
246  } else {
247  QwError << "Variable " << key << " unknown" << QwLog::endl;
248  return 0;
249  }
250  }
251  /// \brief Get a list of templated values
252  template < class T >
253  std::vector < T > GetValueVector(const std::string& key) {
254  std::vector<T> list;
255  if (fParsed == false) Parse();
256  if (fVariablesMap.count(key)) {
257  list = fVariablesMap[key].as< std::vector<T> >();
258  }
259  return list;
260  }
261 
262  /// \brief Get a pair of integer values
263  std::pair<int,int> GetIntValuePair(const std::string& key);
264 
265  /// \brief Get the first of a pair of integer values
266  int GetIntValuePairFirst(const std::string& key) {
267  return GetIntValuePair(key).first;
268  };
269 
270  /// \brief Get the last of a pair of integer values
271  int GetIntValuePairLast(const std::string& key) {
272  return GetIntValuePair(key).second;
273  };
274 
275  /// \brief Get the number of command line arguments
276  int GetArgc() { return (int) fArgc; };
277  /// \brief Get the vector of command line arguments
278  char** GetArgv() { return (char**) fArgv; };
279 
280  /// \brief Clear the parsed variables
281  void Clear() {
282  fVariablesMap.clear();
283  fParsed = false;
284  };
285 
286  private:
287 
288  /// \brief Combine the various option description in one
289  po::options_description* CombineOptions();
290 
291  /// \brief Parse the command line arguments
292  void ParseCommandLine();
293 
294  /// \brief Parse the configuration file
295  void ParseConfigFile();
296 
297  /// \brief Parse the environment variables
298  void ParseEnvironment();
299 
300  /// \brief Configuration file
301  std::vector<std::string> fConfigFiles;
302 
303  /** \brief Command line arguments
304  *
305  * Because argc and argv are only available in the main routine, we need
306  * to store a copy of them for later parsing. The copy is static to have
307  * them available in local copies of the options object.
308  */
309  static int fArgc;
310  static char** fArgv;
311 
312  // Vector with option blocks
313  // Notes:
314  // - boost::po cannot work with object array
315  // - no access to the name of the block after creation
316  std::vector<po::options_description*> fOptionBlock;
317  std::vector<std::string> fOptionBlockName;
318 
319  // Options descriptions
320  po::variables_map fVariablesMap;
321 
322  bool fParsed;
323 };
324 
325 extern QwOptions gQwOptions;
326 
327 #endif // QWOPTIONS_H
int GetIntValuePairFirst(const std::string &key)
Get the first of a pair of integer values.
Definition: QwOptions.h:266
#define QwMessage
Predefined log drain for regular messages.
Definition: QwLog.h:50
QwOptions(int argc, char *argv[])
Constructor with command line arguments.
Definition: QwOptions.h:141
void ParseCommandLine()
Parse the command line arguments.
Definition: QwOptions.cc:167
std::vector< po::options_description * > fOptionBlock
Definition: QwOptions.h:316
std::vector< std::string > fOptionBlockName
Definition: QwOptions.h:317
An options class.
Definition: QwOptions.h:133
const TString getenv_safe_TString(const char *name)
Definition: QwOptions.h:40
bool HasValue(const std::string &key)
Has this key been defined.
Definition: QwOptions.h:233
po::options_description_easy_init AddDefaultOptions()
Add a default option.
Definition: QwOptions.h:159
void ParseConfigFile()
Parse the configuration file.
Definition: QwOptions.cc:247
QwOptions()
Default constructor.
Definition: QwOptions.cc:51
std::vector< T > GetValueVector(const std::string &key)
Get a list of templated values.
Definition: QwOptions.h:253
QwOptions(const std::string &configfile)
Constructor with configuration file.
Definition: QwOptions.h:145
virtual ~QwOptions()
Default destructor.
Definition: QwOptions.cc:91
#define QwVerbose
Predefined log drain for verbose messages.
Definition: QwLog.h:55
QwOptions gQwOptions
Definition: QwOptions.cc:27
char ** GetArgv()
Get the vector of command line arguments.
Definition: QwOptions.h:278
static char ** fArgv
Definition: QwOptions.h:310
std::vector< std::string > fConfigFiles
Configuration file.
Definition: QwOptions.h:301
po::options_description_easy_init AddOptions(const std::string &blockname="Specialized options")
Add an option to a named block or create new block.
Definition: QwOptions.h:164
void ParseEnvironment()
Parse the environment variables.
Definition: QwOptions.cc:229
bool fParsed
Definition: QwOptions.h:322
T GetValue(const std::string &key)
Get a templated value.
Definition: QwOptions.h:240
void Usage()
Print usage information.
Definition: QwOptions.cc:288
void ListConfigFiles()
List the configuration files.
Definition: QwOptions.h:214
std::pair< int, int > GetIntValuePair(const std::string &key)
Get a pair of integer values.
Definition: QwOptions.cc:332
void SetConfigFile(const std::string &configfile)
Set a configuration file.
Definition: QwOptions.h:192
const char * getenv_safe(const char *name)
Definition: QwOptions.h:28
A logfile class, based on an identical class in the Hermes analyzer.
void Version()
Print version string.
Definition: QwOptions.cc:301
void AddConfigFile(const std::string &configfile)
Add a configuration file.
Definition: QwOptions.h:199
QwOptions(int argc, char *argv[], const std::string &configfile)
Constructor with command line arguments and configuration file.
Definition: QwOptions.h:149
void Parse(bool force=false)
Parse all sources of options.
Definition: QwOptions.h:222
void AddConfigFile(std::vector< std::string > configfiles)
Add some configuration files.
Definition: QwOptions.h:207
po::options_description * CombineOptions()
Combine the various option description in one.
Definition: QwOptions.cc:149
static const double T
Magnetic field: base unit is T.
Definition: QwUnits.h:111
int GetArgc()
Get the number of command line arguments.
Definition: QwOptions.h:276
void Clear()
Clear the parsed variables.
Definition: QwOptions.h:281
static std::ostream & endl(std::ostream &)
End of the line.
Definition: QwLog.cc:299
const std::string getenv_safe_string(const char *name)
Definition: QwOptions.h:37
program_options
Definition: CMakeCache.txt:395
#define QwWarning
Predefined log drain for warnings.
Definition: QwLog.h:45
static void DefineOptions(QwOptions &options)
Define the options.
Definition: QwOptions.cc:69
static int fArgc
Command line arguments.
Definition: QwOptions.h:309
int GetIntValuePairLast(const std::string &key)
Get the last of a pair of integer values.
Definition: QwOptions.h:271
po::variables_map fVariablesMap
Definition: QwOptions.h:320
#define QwError
Predefined log drain for errors.
Definition: QwLog.h:40
void SetCommandLine(int argc, char *argv[], bool default_config_file=true)
Set the command line arguments.
Definition: QwOptions.cc:112