1 // Copyright Thomas Kent 2016
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt
4 // or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 // This example shows a config file (in ini format) being parsed by the
7 // program_options library. It includes a numebr of different value types.
8 
9 #include <boost/program_options.hpp>
10 namespace po = boost::program_options;
11 
12 #include <assert.h>
13 #include <iostream>
14 #include <sstream>
15 using namespace std;
16 
17 const double FLOAT_SEPERATION = 0.00000000001;
check_float(double test,double expected)18 bool check_float(double test, double expected)
19 {
20    double seperation = expected * (1 + FLOAT_SEPERATION) / expected;
21    if ((test < expected + seperation) && (test > expected - seperation))
22    {
23       return true;
24    }
25    return false;
26 }
27 
make_file()28 stringstream make_file()
29 {
30    stringstream ss;
31    ss << "# This file checks parsing of various types of config values\n";
32    //FAILS: ss << "; a windows style comment\n";
33 
34    ss << "global_string = global value\n";
35    ss << "unregistered_entry = unregistered value\n";
36 
37    ss << "\n[strings]\n";
38    ss << "word = word\n";
39    ss << "phrase = this is a phrase\n";
40    ss << "quoted = \"quotes are in result\"\n";
41 
42    ss << "\n[ints]\n";
43    ss << "positive = 41\n";
44    ss << "negative = -42\n";
45    //FAILS: Lexical cast doesn't support hex, oct, or bin
46    //ss << "hex = 0x43\n";
47    //ss << "oct = 044\n";
48    //ss << "bin = 0b101010\n";
49 
50    ss << "\n[floats]\n";
51    ss << "positive = 51.1\n";
52    ss << "negative = -52.1\n";
53    ss << "double = 53.1234567890\n";
54    ss << "int = 54\n";
55    ss << "int_dot = 55.\n";
56    ss << "dot = .56\n";
57    ss << "exp_lower = 57.1e5\n";
58    ss << "exp_upper = 58.1E5\n";
59    ss << "exp_decimal = .591e5\n";
60    ss << "exp_negative = 60.1e-5\n";
61    ss << "exp_negative_val = -61.1e5\n";
62    ss << "exp_negative_negative_val = -62.1e-5\n";
63 
64    ss << "\n[booleans]\n";
65    ss << "number_true = 1\n";
66    ss << "number_false = 0\n";
67    ss << "yn_true = yes\n";
68    ss << "yn_false = no\n";
69    ss << "tf_true = true\n";
70    ss << "tf_false = false\n";
71    ss << "onoff_true = on\n";
72    ss << "onoff_false = off\n";
73    ss << "present_equal_true = \n";
74    //FAILS: Must be an =
75    //ss << "present_no_equal_true\n";
76 
77    ss.seekp(ios_base::beg);
78    return ss;
79 }
80 
set_options()81 po::options_description set_options()
82 {
83    po::options_description opts;
84    opts.add_options()
85       ("global_string", po::value<string>())
86 
87       ("strings.word", po::value<string>())
88       ("strings.phrase", po::value<string>())
89       ("strings.quoted", po::value<string>())
90 
91       ("ints.positive", po::value<int>())
92       ("ints.negative", po::value<int>())
93       ("ints.hex", po::value<int>())
94       ("ints.oct", po::value<int>())
95       ("ints.bin", po::value<int>())
96 
97       ("floats.positive", po::value<float>())
98       ("floats.negative", po::value<float>())
99       ("floats.double", po::value<double>())
100       ("floats.int", po::value<float>())
101       ("floats.int_dot", po::value<float>())
102       ("floats.dot", po::value<float>())
103       ("floats.exp_lower", po::value<float>())
104       ("floats.exp_upper", po::value<float>())
105       ("floats.exp_decimal", po::value<float>())
106       ("floats.exp_negative", po::value<float>())
107       ("floats.exp_negative_val", po::value<float>())
108       ("floats.exp_negative_negative_val", po::value<float>())
109 
110       // Load booleans as value<bool>, so they will require a --option=value on the command line
111       //("booleans.number_true", po::value<bool>())
112       //("booleans.number_false", po::value<bool>())
113       //("booleans.yn_true", po::value<bool>())
114       //("booleans.yn_false", po::value<bool>())
115       //("booleans.tf_true", po::value<bool>())
116       //("booleans.tf_false", po::value<bool>())
117       //("booleans.onoff_true", po::value<bool>())
118       //("booleans.onoff_false", po::value<bool>())
119       //("booleans.present_equal_true", po::value<bool>())
120       //("booleans.present_no_equal_true", po::value<bool>())
121 
122       // Load booleans as bool_switch, so that a --option will set it true on the command line
123       // The difference between these two types does not show up when parsing a file
124       ("booleans.number_true", po::bool_switch())
125       ("booleans.number_false", po::bool_switch())
126       ("booleans.yn_true", po::bool_switch())
127       ("booleans.yn_false", po::bool_switch())
128       ("booleans.tf_true", po::bool_switch())
129       ("booleans.tf_false", po::bool_switch())
130       ("booleans.onoff_true", po::bool_switch())
131       ("booleans.onoff_false", po::bool_switch())
132       ("booleans.present_equal_true", po::bool_switch())
133       ("booleans.present_no_equal_true", po::bool_switch())
134       ;
135    return opts;
136 }
137 
parse_file(stringstream & file,po::options_description & opts,po::variables_map & vm)138 vector<string> parse_file(stringstream &file, po::options_description &opts, po::variables_map &vm)
139 {
140    const bool ALLOW_UNREGISTERED = true;
141    cout << file.str() << endl;
142 
143    po::parsed_options parsed = parse_config_file(file, opts, ALLOW_UNREGISTERED);
144    store(parsed, vm);
145    vector<string> unregistered = po::collect_unrecognized(parsed.options, po::exclude_positional);
146    notify(vm);
147 
148    return unregistered;
149 }
150 
check_results(po::variables_map & vm,vector<string> unregistered)151 void check_results(po::variables_map &vm, vector<string> unregistered)
152 {
153    // Check that we got the correct values back
154    string expected_global_string = "global value";
155 
156    string expected_unreg_option = "unregistered_entry";
157    string expected_unreg_value = "unregistered value";
158 
159    string expected_strings_word = "word";
160    string expected_strings_phrase = "this is a phrase";
161    string expected_strings_quoted = "\"quotes are in result\"";
162 
163    int expected_int_postitive = 41;
164    int expected_int_negative = -42;
165    int expected_int_hex = 0x43;
166    int expected_int_oct = 044;
167    int expected_int_bin = 0b101010;
168 
169    float expected_float_positive = 51.1f;
170    float expected_float_negative = -52.1f;
171    double expected_float_double = 53.1234567890;
172    float expected_float_int = 54.0f;
173    float expected_float_int_dot = 55.0f;
174    float expected_float_dot = .56f;
175    float expected_float_exp_lower = 57.1e5f;
176    float expected_float_exp_upper = 58.1E5f;
177    float expected_float_exp_decimal = .591e5f;
178    float expected_float_exp_negative = 60.1e-5f;
179    float expected_float_exp_negative_val = -61.1e5f;
180    float expected_float_exp_negative_negative_val = -62.1e-5f;
181 
182    bool expected_number_true = true;
183    bool expected_number_false = false;
184    bool expected_yn_true = true;
185    bool expected_yn_false = false;
186    bool expected_tf_true = true;
187    bool expected_tf_false = false;
188    bool expected_onoff_true = true;
189    bool expected_onoff_false = false;
190    bool expected_present_equal_true = true;
191    bool expected_present_no_equal_true = true;
192 
193    assert(vm["global_string"].as<string>() == expected_global_string);
194 
195    assert(unregistered[0] == expected_unreg_option);
196    assert(unregistered[1] == expected_unreg_value);
197 
198    assert(vm["strings.word"].as<string>() == expected_strings_word);
199    assert(vm["strings.phrase"].as<string>() == expected_strings_phrase);
200    assert(vm["strings.quoted"].as<string>() == expected_strings_quoted);
201 
202    assert(vm["ints.positive"].as<int>() == expected_int_postitive);
203    assert(vm["ints.negative"].as<int>() == expected_int_negative);
204    //assert(vm["ints.hex"].as<int>() == expected_int_hex);
205    //assert(vm["ints.oct"].as<int>() == expected_int_oct);
206    //assert(vm["ints.bin"].as<int>() == expected_int_bin);
207 
208    assert(check_float(vm["floats.positive"].as<float>(), expected_float_positive));
209    assert(check_float(vm["floats.negative"].as<float>(), expected_float_negative));
210    assert(check_float(vm["floats.double"].as<double>(), expected_float_double));
211    assert(check_float(vm["floats.int"].as<float>(), expected_float_int));
212    assert(check_float(vm["floats.int_dot"].as<float>(), expected_float_int_dot));
213    assert(check_float(vm["floats.dot"].as<float>(), expected_float_dot));
214    assert(check_float(vm["floats.exp_lower"].as<float>(), expected_float_exp_lower));
215    assert(check_float(vm["floats.exp_upper"].as<float>(), expected_float_exp_upper));
216    assert(check_float(vm["floats.exp_decimal"].as<float>(), expected_float_exp_decimal));
217    assert(check_float(vm["floats.exp_negative"].as<float>(), expected_float_exp_negative));
218    assert(check_float(vm["floats.exp_negative_val"].as<float>(), expected_float_exp_negative_val));
219    assert(check_float(vm["floats.exp_negative_negative_val"].as<float>(), expected_float_exp_negative_negative_val));
220 
221    assert(vm["booleans.number_true"].as<bool>() == expected_number_true);
222    assert(vm["booleans.number_false"].as<bool>() == expected_number_false);
223    assert(vm["booleans.yn_true"].as<bool>() == expected_yn_true);
224    assert(vm["booleans.yn_false"].as<bool>() == expected_yn_false);
225    assert(vm["booleans.tf_true"].as<bool>() == expected_tf_true);
226    assert(vm["booleans.tf_false"].as<bool>() == expected_tf_false);
227    assert(vm["booleans.onoff_true"].as<bool>() == expected_onoff_true);
228    assert(vm["booleans.onoff_false"].as<bool>() == expected_onoff_false);
229    assert(vm["booleans.present_equal_true"].as<bool>() == expected_present_equal_true);
230    //assert(vm["booleans.present_no_equal_true"].as<bool>() == expected_present_no_equal_true);
231 }
232 
main(int ac,char * av[])233 int main(int ac, char* av[])
234 {
235    auto file = make_file();
236    auto opts = set_options();
237    po::variables_map vars;
238    auto unregistered = parse_file(file, opts, vars);
239    check_results(vars, unregistered);
240 
241    return 0;
242 }
243