Program.hpp
4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#pragma once
#include <string>
#include <boost/program_options.hpp>
// This is unreachable outside this file; you should not use Combiner directly
namespace {
struct Combiner {
int positional;
bool required;
bool defaulted;
/// Can be or-ed together
Combiner operator | (Combiner b) const {
Combiner self;
self.positional = positional + b.positional;
self.required = required || b.required;
self.defaulted = defaulted || b.defaulted;
return self;
}
/// Call to give the number of arguments expected on cli
Combiner operator() (int n) const {
return Combiner{n, required, defaulted};
}
Combiner operator, (Combiner b) const {
return *this | b;
}
};
}
/// Creates a command line program, with very few defaults.
/** To use, create a new Program() instance with argc, argv, and a help description. The templated
* add_option methods make it easy to prepare options. Remember to call `.start` before starting your
* program, so that the options can be evaluated and the help option doesn't accidentally run your program. */
class Program {
public:
static constexpr Combiner REQUIRED{0,true,false};
static constexpr Combiner DEFAULT{0,false,true};
static constexpr Combiner POSITIONAL{1,false,false};
protected:
boost::program_options::options_description desc;
boost::program_options::positional_options_description p;
boost::program_options::variables_map vm;
int argc;
char **argv;
/// Parses the command line (internal function)
void parse() {
try {
boost::program_options::store(boost::program_options::command_line_parser(argc, argv)
.options(desc).positional(p).run(), vm);
if(vm.count("help")){
std::cout << desc;
exit(0);
}
boost::program_options::notify(vm);
} catch(const boost::program_options::error& e) {
std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
std::cerr << desc << std::endl;
exit(1);
}
}
public:
/// Create a new program. Pass in the same arguments as main(), along with a help string.
Program(int argc, char** argv, std::string discription)
: argc(argc), argv(argv), desc(discription) {
desc.add_options()
("help,h", "Display this help message");
}
/// Allows you to manually add options in the boost style.
/** Usually the specialized methods are easier, but this remains for people used to Boost and for
* unusual situations. */
boost::program_options::options_description_easy_init add_options() {
return desc.add_options();
}
/// Add an option, will automatically understand the type for common types.
/** To use, create a variable with the expected type, and pass it in after the name.
* After start is called, you can use count to see if the value was passed, and
* the value will be initialized properly.
*
* Program::REQUIRED, Program::DEFAULT, and Program::POSITIONAL are options, and can be `|`
* together. The positional options take an optional number of arguments.
*
* For example,
*
* std::string filename
* program.add_option("filename", filename, "description of filename");
*/
template<typename T>
void add_option(
std::string name, ///< The name, long,short
T &value, ///< The value
std::string description, ///< Discription string
Combiner options ///< The options (REQUIRED, DEFAULT, POSITIONAL)
) {
auto po_value = boost::program_options::value<T>(&value);
if(options.defaulted)
po_value = po_value->default_value(value);
if(options.required)
po_value = po_value->required();
desc.add_options()(name.c_str(),po_value,description.c_str());
if(options.positional!=0)
p.add(name.c_str(), options.positional);
}
/// Adds a flag style option
void add_option(std::string name, std::string description) {
desc.add_options()(name.c_str(),description.c_str());
}
/// This must be called after the options are in but before the rest of the program.
/** Calls the Boost boost::program_options initialization, causing the program to exit
* if -h or an invalid option is passed. */
void start() {
parse();
}
/// Counts the number of times the given option was passed.
int count(std::string name) const {
return vm.count(name.c_str());
}
};