• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #882

31 Aug 2023 07:44PM UTC coverage: 41.798% (-44.7%) from 86.546%
#882

push

19442 of 46514 relevant lines covered (41.8%)

126375.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/libs/core/program_options/examples/options_hierarchy.cpp
1
// Copyright Thomas Kent 2016
2
//  SPDX-License-Identifier: BSL-1.0
3
// Distributed under the Boost Software License, Version 1.0.
4
// (See accompanying file LICENSE_1_0.txt
5
// or copy at http://www.boost.org/LICENSE_1_0.txt)
6

7
//
8
// This is an example of a program that uses multiple facets of the boost
9
// program_options library. It will go through different types of config
10
// options in a hierarchal manner:
11
// 1. Default options are set.
12
// 2. Command line options are set (they override defaults).
13
// 3. Environment options are set (they override defaults but not command
14
//    line options).
15
// 4. Config files specified on the command line are read, if present, in
16
//    the order specified. (these override defaults but not options from the
17
//    other steps).
18
// 5. Default config file (default.cfg) is read, if present (it overrides
19
//    defaults but not options from the other steps).
20
//
21
// See the bottom of this file for full usage examples
22
//
23

24
#include <hpx/modules/program_options.hpp>
25

26
#include <algorithm>
27
#include <fstream>
28
#include <functional>
29
#include <iostream>
30
#include <map>
31
#include <stdexcept>
32
#include <string>
33
#include <vector>
34

35
#include <hpx/config/warnings_prefix.hpp>
36

37
namespace po = hpx::program_options;
38

39
std::string const version("1.0");
40

×
41
// Used to exit the program if the help/version option is set
42
class OptionsExitsProgram : public std::exception
43
{
44
};
45

46
struct GuiOpts
47
{
48
    unsigned int width;
49
    unsigned int height;
50
};
×
51

52
struct NetworkOpts
53
{
54
    std::string address;
55
    unsigned short port;
56
};
57

58
class OptionsHierarchy
59
{
60
public:
×
61
    // The constructor sets up all the various options that will be parsed
×
62
    OptionsHierarchy()
×
63
    {
×
64
        SetOptions();
65
    }
66

×
67
    // Parse options runs through the hierarchy doing all the parsing
68
    void ParseOptions(int argc, char* argv[])
×
69
    {
×
70
        ParseCommandLine(argc, argv);
×
71
        CheckForHelp();
×
72
        CheckForVersion();
×
73
        ParseEnvironment();
×
74
        ParseConfigFiles();
×
75
        ParseDefaultConfigFile();
76
    }
77

×
78
    // Below is the interface to access the data, once ParseOptions has been run
79
    std::string Path()
×
80
    {
81
        return results["path"].as<std::string>();
×
82
    }
83
    std::string Verbosity()
×
84
    {
85
        return results["verbosity"].as<std::string>();
×
86
    }
87
    std::vector<std::string> IncludePath()
×
88
    {
89
        if (results.count("include-path"))
×
90
        {
91
            return results["include-path"].as<std::vector<std::string>>();
×
92
        }
93
        return std::vector<std::string>();
×
94
    }
95
    std::string MasterFile()
×
96
    {
97
        if (results.count("master-file"))
×
98
        {
99
            return results["master-file"].as<std::string>();
×
100
        }
101
        return "";
×
102
    }
103
    std::vector<std::string> Files()
×
104
    {
105
        if (results.count("file"))
×
106
        {
107
            return results["file"].as<std::vector<std::string>>();
×
108
        }
109
        return std::vector<std::string>();
×
110
    }
111
    bool GUI()
×
112
    {
113
        if (results["run-gui"].as<bool>())
114
        {
115
            return true;
116
        }
117
        return false;
×
118
    }
119
    GuiOpts GuiValues()
120
    {
×
121
        GuiOpts opts;
×
122
        opts.width = results["gui.width"].as<unsigned int>();
×
123
        opts.height = results["gui.height"].as<unsigned int>();
124
        return opts;
×
125
    }
126
    NetworkOpts NetworkValues()
127
    {
×
128
        NetworkOpts opts;
×
129
        opts.address = results["network.ip"].as<std::string>();
×
130
        opts.port = results["network.port"].as<unsigned short>();
131
        return opts;
132
    }
133

×
134
private:
135
    void SetOptions()
×
136
    {
×
137
        SetCommandLineOptions();
×
138
        SetCommonOptions();
×
139
        SetConfigOnlyOptions();
×
140
        SetEnvMapping();
141
    }
×
142

143
    void SetCommandLineOptions()
144
    {
×
145
        // clang-format off
×
146
        command_line_options.add_options()
×
147
            ("help,h", "display this help message")
×
148
            ("version,v", "show program version")
149
            ("config,c", po::value<std::vector<std::string>>(),
150
                "config files to parse (always parses default.cfg)")
151
            ;
×
152

×
153
        hidden_command_line_options.add_options()
×
154
            ("master-file", po::value<std::string>())
155
            ("file", po::value<std::vector<std::string>>())
156
            ;
157
        // clang-format on
×
158

×
159
        positional_options.add("master-file", 1);
×
160
        positional_options.add("file", -1);
161
    }
×
162

163
    void SetCommonOptions()
164
    {
×
165
        // clang-format off
×
166
        common_options.add_options()
167
            ("path", po::value<std::string>()->default_value(""),
168
                "the execution path to use (imports from environment if not "
×
169
                "specified)")
170
            ("verbosity", po::value<std::string>()->default_value("INFO"),
×
171
                "set verbosity: DEBUG, INFO, WARN, ERROR, FATAL")
172
            ("include-path,I", po::value<std::vector<std::string>>()->composing(),
×
173
                "paths to search for include files")
174
            ("run-gui", po::bool_switch(), "start the GUI")
175
            ;
×
176
        // clang-format on
177
    }
×
178

179
    void SetConfigOnlyOptions()
180
    {
×
181
        // clang-format off
×
182
        config_only_options.add_options()
×
183
            ("log-dir", po::value<std::string>()->default_value("log"))
×
184
            ("gui.height", po::value<unsigned int>()->default_value(100))
×
185
            ("gui.width", po::value<unsigned int>()->default_value(100))
×
186
            ("network.ip", po::value<std::string>()->default_value("127.0.0.1"))
187
            ("network.port", po::value<unsigned short>()->default_value(12345))
188
            ;
189
        // clang-format on
190

191
        // Run a parser here (with no command line options) to add these defaults into
×
192
        // results, this way they will be enabled even if no config files are parsed.
×
193
        char const* argv[] = {"options_hierarchy", nullptr};
194
        store(po::command_line_parser(1, argv)
195
                  .allow_unregistered()
×
196
                  .options(config_only_options)
×
197
                  .run(),
×
198
            results);
×
199
        notify(results);
200
    }
×
201

202
    void SetEnvMapping()
×
203
    {
×
204
        env_to_option["PATH"] = "path";
×
205
        env_to_option["EXAMPLE_VERBOSE"] = "verbosity";
206
    }
×
207

208
    void ParseCommandLine(int argc, char* argv[])
×
209
    {
×
210
        po::options_description cmd_opts;
×
211
        cmd_opts.add(command_line_options)
×
212
            .add(hidden_command_line_options)
×
213
            .add(common_options);
214
        store(po::command_line_parser(argc, argv)
215
                  .allow_unregistered()
×
216
                  .options(cmd_opts)
×
217
                  .positional(positional_options)
×
218
                  .run(),
×
219
            results);
×
220
        notify(results);
221
    }
×
222

223
    void CheckForHelp()
×
224
    {
225
        if (results.count("help"))
×
226
        {
227
            PrintHelp();
×
228
        }
229
    }
×
230

231
    void PrintHelp()
232
    {
×
233
        std::cout << "Program Options Example" << std::endl;
×
234
        std::cout << "Usage: example [OPTION]... MASTER-FILE [FILE]...\n";
235
        std::cout << "  or   example [OPTION] --run-gui\n";
×
236

×
237
        po::options_description help_opts;
×
238
        help_opts.add(command_line_options).add(common_options);
×
239
        std::cout << help_opts << std::endl;
×
240
        throw OptionsExitsProgram();
241
    }
×
242

243
    void CheckForVersion()
×
244
    {
245
        if (results.count("version"))
×
246
        {
247
            PrintVersion();
×
248
        }
249
    }
×
250

251
    void PrintVersion()
252
    {
×
253
        std::cout << "Program Options Example " << version << std::endl;
254
        throw OptionsExitsProgram();
255
    }
×
256

257
    void ParseEnvironment()
×
258
    {
×
259
        store(po::parse_environment(common_options,
260
                  std::bind(&OptionsHierarchy::EnvironmentMapper, this,
×
261
                      std::placeholders::_1)),
×
262
            results);
×
263
        notify(results);
264
    }
×
265

266
    std::string EnvironmentMapper(std::string env_var)
267
    {
268
        // ensure the env_var is all caps
269
        std::transform(
270
            env_var.begin(), env_var.end(), env_var.begin(), ::toupper);
271

×
272
        auto entry = env_to_option.find(env_var);
273
        if (entry != env_to_option.end())
274
        {
275
            return entry->second;
×
276
        }
277
        return "";
278
    }
×
279

280
    void ParseConfigFiles()
×
281
    {
282
        if (results.count("config"))
×
283
        {
×
284
            auto files = results["config"].as<std::vector<std::string>>();
285
            for (auto& file : files)
×
286
            {
287
                LoadAConfigFile(file);
×
288
            }
×
289
        }
290
    }
×
291

292
    void LoadAConfigFile(std::string filename)
293
    {
294
        bool ALLOW_UNREGISTERED = true;
×
295

×
296
        po::options_description config_opts;
297
        config_opts.add(config_only_options).add(common_options);
×
298

×
299
        std::ifstream cfg_file(filename.c_str());
300
        if (cfg_file)
×
301
        {
×
302
            store(parse_config_file(cfg_file, config_opts, ALLOW_UNREGISTERED),
×
303
                results);
304
            notify(results);
×
305
        }
306
    }
×
307

308
    void ParseDefaultConfigFile()
×
309
    {
×
310
        LoadAConfigFile("default.cfg");
311
    }
312

313
    std::map<std::string, std::string> env_to_option;
314
    po::options_description config_only_options;
315
    po::options_description common_options;
316
    po::options_description command_line_options;
317
    po::options_description hidden_command_line_options;
318
    po::positional_options_description positional_options;
319
    po::variables_map results;
320
};
×
321

322
void get_env_options() {}
×
323

324
void PrintOptions(OptionsHierarchy options)
×
325
{
×
326
    auto path = options.Path();
327
    if (path.length())
×
328
    {
×
329
        std::cout << "First 75 chars of the system path: \n";
330
        std::cout << options.Path().substr(0, 75) << std::endl;
331
    }
×
332

×
333
    std::cout << "Verbosity: " << options.Verbosity() << std::endl;
×
334
    std::cout << "Include Path:\n";
×
335
    auto includePaths = options.IncludePath();
336
    for (auto& includePath : includePaths)
337
    {
338
        std::cout << "   " << includePath << std::endl;
339
    }
×
340

×
341
    std::cout << "Master-File: " << options.MasterFile() << std::endl;
×
342
    std::cout << "Additional Files:\n";
×
343
    auto files = options.Files();
344
    for (auto& file : files)
345
    {
346
        std::cout << "   " << file << std::endl;
347
    }
×
348

349
    std::cout << "GUI Enabled: " << std::boolalpha << options.GUI()
×
350
              << std::endl;
351
    if (options.GUI())
×
352
    {
×
353
        auto gui_values = options.GuiValues();
×
354
        std::cout << "GUI Height: " << gui_values.height << std::endl;
355
        std::cout << "GUI Width: " << gui_values.width << std::endl;
356
    }
×
357

358
    auto network_values = options.NetworkValues();
×
359
    std::cout << "Network Address: " << network_values.address << std::endl;
×
360
    std::cout << "Network Port: " << network_values.port << std::endl;
361
}
×
362

363
int main(int ac, char* av[])
×
364
{
365
    OptionsHierarchy options;
366
    try
×
367
    {
×
368
        options.ParseOptions(ac, av);
369
        PrintOptions(options);
×
370
    }
371
    // NOLINTNEXTLINE(bugprone-empty-catch)
×
372
    catch (OptionsExitsProgram const&)
373
    {
374
    }
×
375

376
    return 0;
377
}
378

379
/*
380
Full Usage Examples
381
===================
382

383
These were run on windows, so some results may show that environment, but
384
results should be similar on POSIX platforms.
385

386
Help
387
----
388
To see the help screen, with the available options just pass the --help (or -h)
389
parameter. The program will then exit.
390

391
    > example.exe --help
392
    Program Options Example
393
    Usage: example [OPTION]... MASTER-FILE [FILE]...
394
      or   example [OPTION] --run-gui
395

396
      -h [ --help ]              display this help message
397
      -v [ --version ]           show program version
398
      -c [ --config ] arg        config files to parse (always parses default.cfg)
399

400
      --path arg                 the execution path to use (imports from
401
                                 environment if not specified)
402
      --verbosity arg (=INFO)    set verbosity: DEBUG, INFO, WARN, ERROR, FATAL
403
      -I [ --include-path ] arg  paths to search for include files
404
      --run-gui                  start the GUI
405

406
Version is similar to help (--version or -v).
407

408
    > example.exe -v
409
    Program Options Example 1.0
410

411
Basics
412
------
413
Running without any options will get the default values (path is set from the
414
environment):
415

416
    > example.exe
417
    First 75 chars of the system path:
418
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
419
    Verbosity: INFO
420
    Include Path:
421
    Master-File:
422
    Additional Files:
423
    GUI Enabled: false
424
    Network Address: 127.0.0.1
425
    Network Port: 12345
426

427
We can easily override that environment path with a simple option:
428

429
    > example.exe --path a/b/c;d/e/f
430
    First 75 chars of the system path:
431
    a/b/c;d/e/f
432
    Verbosity: INFO
433
    Include Path:
434
    Master-File:
435
    Additional Files:
436
    GUI Enabled: false
437
    Network Address: 127.0.0.1
438
    Network Port: 12345
439

440
You can use a space or equals sign after long options, also backslashes are
441
treated literally on windows, on POSIX they need to be escaped.
442

443
    > example.exe --path=a\b\c\;d\e\\f
444
    First 75 chars of the system path:
445
    a\b\c\;d\e\\f
446
    Verbosity: INFO
447
    Include Path:
448
    Master-File:
449
    Additional Files:
450
    GUI Enabled: false
451
    Network Address: 127.0.0.1
452
    Network Port: 12345
453

454
For short options you can use a space:
455

456
    > example.exe -I path/to/includes
457
    First 75 chars of the system path:
458
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
459
    Verbosity: INFO
460
    Include Path:
461
        path\to\includes
462
    Master-File:
463
    Additional Files:
464
    GUI Enabled: false
465
    Network Address: 127.0.0.1
466
    Network Port: 12345
467

468
Or you can put the option immediately after it:
469

470
    > example.exe -Ipath/to/includes
471
    First 75 chars of the system path:
472
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
473
    Verbosity: INFO
474
    Include Path:
475
        path\to\includes
476
    Master-File:
477
    Additional Files:
478
    GUI Enabled: false
479
    Network Address: 127.0.0.1
480
    Network Port: 12345
481

482
The include path (--include-path or -I) option allows for multiple paths to be
483
specified (both on the command line and in config files) and combined into a
484
vector for use by the program.
485

486
    > example.exe --include-path=a/b/c --include-path d/e/f -I g/h/i -Ij/k/l
487
    First 75 chars of the system path:
488
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
489
    Verbosity: INFO
490
    Include Path:
491
       a/b/c
492
       d/e/f
493
       g/h/i
494
       j/k/l
495
    Master-File:
496
    Additional Files:
497
    GUI Enabled: false
498
    Network Address: 127.0.0.1
499
    Network Port: 12345
500

501
There are also the option of flags that do not take parameters and just set a
502
boolean value to true. In this case, running the gui also causes default values
503
for the gui to be output to the screen.
504

505
    > example.exe --run-gui
506
    First 75 chars of the system path:
507
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
508
    Verbosity: INFO
509
    Include Path:
510
    Master-File:
511
    Additional Files:
512
    GUI Enabled: true
513
    GUI Height: 100
514
    GUI Width: 100
515
    Network Address: 127.0.0.1
516
    Network Port: 12345
517

518
There are also "positional" options at the end of the command line. The first
519
one specifies the "master" file the others are additional files.
520

521
    > example.exe --path=a-path -I an-include master.cpp additional1.cpp additional2.cpp
522
    First 75 chars of the system path:
523
    a-path
524
    Verbosity: INFO
525
    Include Path:
526
       an-include
527
    Master-File: master.cpp
528
    Additional Files:
529
       additional1.cpp
530
       additional2.cpp
531
    GUI Enabled: false
532
    Network Address: 127.0.0.1
533
    Network Port: 12345
534

535
Environment Variables
536
---------------------
537
In addition to the PATH environment variable, it also knows how to read the
538
EXAMPLE_VERBOSE environmental variable and use that to set the verbosity
539
option/
540

541
    > set EXAMPLE_VERBOSE=DEBUG
542
    > example.exe
543
    First 75 chars of the system path:
544
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
545
    Verbosity: DEBUG
546
    Include Path:
547
    Master-File:
548
    Additional Files:
549
    GUI Enabled: false
550
    Network Address: 127.0.0.1
551
    Network Port: 12345
552

553
However, if the --verboseity flag is also set, it will override the env
554
variable. This illustrates an important example, the way program_options works,
555
is that a parser will not override a value that has previously been set by
556
another parser. Thus the env parser doesn't override the command line parser.
557
(We will see this again in config files.) Default values are separate from this
558
hierarchy, they only apply if no parser has set the value and it is being read.
559

560
    > set EXAMPLE_VERBOSE=DEBUG
561
    > example.exe --verbosity=WARN
562
    First 75 chars of the system path:
563
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
564
    Verbosity: WARN
565
    Include Path:
566
    Master-File:
567
    Additional Files:
568
    GUI Enabled: false
569
    Network Address: 127.0.0.1
570
    Network Port: 12345
571

572
(You can unset an environmental variable with an empty set command)
573

574
    > set EXAMPLE_VERBOSE=
575
    > example.exe
576
    First 75 chars of the system path:
577
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
578
    Verbosity: INFO
579
    Include Path:
580
    Master-File:
581
    Additional Files:
582
    GUI Enabled: false
583
    Network Address: 127.0.0.1
584
    Network Port: 12345
585

586

587
Config Files
588
------------
589
Config files generally follow the [INI file format]
590
(https://en.wikipedia.org/wiki/INI_file) with a few exceptions.
591

592
Values can be simply added tp options with an equal sign. Here are two include
593
paths added via the default config file (default.cfg), you can have optional
594
spaces around the equal sign.
595

596
    # You can use comments in a config file
597
    include-path=first/default/path
598
    include-path = second/default/path
599

600
Results in
601

602
    > example.exe
603
    First 75 chars of the system path:
604
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
605
    Verbosity: INFO
606
    Include Path:
607
       first/default/path
608
       second/default/path
609
    Master-File:
610
    Additional Files:
611
    GUI Enabled: false
612
    Network Address: 127.0.0.1
613
    Network Port: 12345
614

615
Values can also be in sections of the config file. Again, editing default.cfg
616

617
    include-path=first/default/path
618
    include-path = second/default/path
619

620
    [network]
621
    ip=1.2.3.4
622
    port=3000
623

624
Results in
625

626
    > example.exe
627
    First 75 chars of the system path:
628
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
629
    Verbosity: INFO
630
    Include Path:
631
       first/default/path
632
       second/default/path
633
    Master-File:
634
    Additional Files:
635
    GUI Enabled: false
636
    Network Address: 1.2.3.4
637
    Network Port: 3000
638

639
This example is also setup to allow multiple config files to be specified on
640
the command line, which are checked before the default.cfg file is read (but
641
after the environment and command line parsing). Thus we can set the first.cfg
642
file to contain the following:
643

644
    verbosity=ERROR
645

646
    [network]
647
    ip = 5.6.7.8
648

649
Results in:
650

651
    > example.exe --config first.cfg
652
    First 75 chars of the system path:
653
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
654
    Verbosity: ERROR
655
    Include Path:
656
       first/default/path
657
       second/default/path
658
    Master-File:
659
    Additional Files:
660
    GUI Enabled: false
661
    Network Address: 5.6.7.8
662
    Network Port: 3000
663

664
But since the config files are read after the command line, setting the
665
verbosity there causes the value in the file to be ignored.
666

667
    > example.exe --config first.cfg --verbosity=WARN
668
    First 75 chars of the system path:
669
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
670
    Verbosity: WARN
671
    Include Path:
672
       first/default/path
673
       second/default/path
674
    Master-File:
675
    Additional Files:
676
    GUI Enabled: false
677
    Network Address: 5.6.7.8
678
    Network Port: 3000
679

680
The config files are parsed in the order they are received on the command line.
681
So adding the second.cfg file:
682

683
    verbosity=FATAL
684
    run-gui=true
685

686
    [gui]
687
    height=720
688
    width=1280
689

690
Results in a combination of all three config files:
691

692
    > example.exe --config first.cfg --config second.cfg
693
    First 75 chars of the system path:
694
    C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
695
    Verbosity: ERROR
696
    Include Path:
697
       first/default/path
698
       second/default/path
699
    Master-File:
700
    Additional Files:
701
    GUI Enabled: true
702
    GUI Height: 720
703
    GUI Width: 1280
704
    Network Address: 5.6.7.8
705
    Network Port: 3000
706

707
Incidentally the boolean run-gui option could have been set a number of ways
708
that all result in the C++ boolean value of true:
709

710
    run-gui=true
711
    run-gui=on
712
    run-gui=1
713
    run-gui=yes
714
    run-gui=
715

716
Since run-gui is an option that was set with the bool_switch type, which
717
forces its use on the command line without a parameter (i.e. --run-gui instead
718
of --run-gui=true) it can't be given a "false" option, bool_switch values can
719
only be turned true. If instead we had a value ("my-switch", po::value<bool>())
720
that could be set at the command line --my-switch=true or --my-switch=false, or
721
any of the other types of boolean keywords true: true, on, 1, yes;
722
false: false, off, 0, no. In a config file this could look like:
723

724
    my-switch=true
725
    my-switch=on
726
    my-switch=1
727
    my-switch=yes
728
    my-switch=
729

730
    my-switch=false
731
    my-switch=off
732
    my-switch=0
733
    my-switch=no
734

735
*/
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc