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