Skip to content

Commit 3277249

Browse files
ecatmurvprus
authored andcommitted
Support boost::optional option variables.
1 parent 1c2472b commit 3277249

File tree

5 files changed

+124
-1
lines changed

5 files changed

+124
-1
lines changed

doc/howto.xml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,58 @@ vector<string> to_pass_further = collect_unrecognized(parsed.options, incl
435435

436436
</section>
437437

438+
<section>
439+
<title>Testing Option Presence</title>
440+
441+
<para>Until now we have tested whether an option has been set using the
442+
<methodname alt="boost::program_options::variables_map::count">count</methodname> method on the &variables_map;
443+
class; as you are repeating the (string literal) name of the option this is prone to typos and/or errors
444+
resulting from renaming the option in one place but not the other:
445+
<programlisting><![CDATA[
446+
po::options_description desc("Allowed options");
447+
desc.add_options()
448+
("compression", po::value<int>(), "set compression level")
449+
;
450+
451+
po::variables_map vm;
452+
po::store(po::parse_command_line(ac, av, desc), vm);
453+
po::notify(vm);
454+
455+
if (vm.count("compression")) {
456+
cout << "Compression level was set to "
457+
<< vm["compression"].as<int>() << ".\n";
458+
} else {
459+
cout << "Compression level was not set.\n";
460+
}
461+
]]>
462+
</programlisting>
463+
</para>
464+
465+
<para>Instead, you can use a variable of type <classname alt="boost::optional">boost::optional</classname>;
466+
<libraryname>Program_options</libraryname> provides special support for <libraryname>Boost.Optional</libraryname>
467+
such that if the user specifies the option the <classname alt="boost::optional">boost::optional</classname>
468+
variable will be initialized to the appropriate value:
469+
<programlisting><![CDATA[
470+
po::options_description desc("Allowed options");
471+
boost::optional<int> compression;
472+
desc.add_options()
473+
("compression", po::value(&compression), "set compression level")
474+
;
475+
476+
po::variables_map vm;
477+
po::store(po::parse_command_line(ac, av, desc), vm);
478+
po::notify(vm);
479+
480+
if (compression)) {
481+
cout << "Compression level was set to " << *compression << ".\n";
482+
} else {
483+
cout << "Compression level was not set.\n";
484+
}
485+
]]>
486+
</programlisting>
487+
</para>
488+
</section>
489+
438490
</section>
439491

440492
<!--

doc/post_review_plan.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,4 @@
178178

179179
12. Deferred
180180

181-
- storing value to boost::optional
182181
- setting a flag when option is found

include/boost/program_options/detail/value_semantic.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
#include <boost/throw_exception.hpp>
1010

11+
// forward declaration
12+
namespace boost { template<class T> class optional; }
13+
1114
namespace boost { namespace program_options {
1215

1316
extern BOOST_PROGRAM_OPTIONS_DECL std::string arg;
@@ -152,6 +155,20 @@ namespace boost { namespace program_options {
152155
}
153156
}
154157

158+
/** Validates optional arguments. */
159+
template<class T, class charT>
160+
void validate(boost::any& v,
161+
const std::vector<std::basic_string<charT> >& s,
162+
boost::optional<T>*,
163+
int)
164+
{
165+
validators::check_first_occurrence(v);
166+
validators::get_single_string(s);
167+
boost::any a;
168+
validate(a, s, (T*)0, 0);
169+
v = boost::any(boost::optional<T>(boost::any_cast<T>(a)));
170+
}
171+
155172
template<class T, class charT>
156173
void
157174
typed_value<T, charT>::

test/Jamfile.v2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import testing ;
12

23
project
34
: requirements
@@ -33,6 +34,7 @@ test-suite program_options :
3334
[ po-test unrecognized_test.cpp ]
3435
[ po-test required_test.cpp : required_test.cfg ]
3536
[ po-test exception_txt_test.cpp ]
37+
[ po-test optional_test.cpp ]
3638
[ run options_description_test.cpp : : : <rtti>off <define>BOOST_NO_RTTI <define>BOOST_NO_TYPEID : options_description_no_rtti_test ]
3739
;
3840

test/optional_test.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Distributed under the Boost Software License, Version 1.0.
2+
// (See accompanying file LICENSE_1_0.txt
3+
// or copy at http://www.boost.org/LICENSE_1_0.txt)
4+
5+
#include <boost/program_options.hpp>
6+
namespace po = boost::program_options;
7+
8+
#include <boost/optional.hpp>
9+
10+
#include <string>
11+
12+
#include "minitest.hpp"
13+
14+
std::vector<std::string> sv(const char* array[], unsigned size)
15+
{
16+
std::vector<std::string> r;
17+
for (unsigned i = 0; i < size; ++i)
18+
r.push_back(array[i]);
19+
return r;
20+
}
21+
22+
void test_optional()
23+
{
24+
boost::optional<int> foo, bar, baz;
25+
26+
po::options_description desc;
27+
desc.add_options()
28+
("foo,f", po::value(&foo), "")
29+
("bar,b", po::value(&bar), "")
30+
("baz,z", po::value(&baz), "")
31+
;
32+
33+
const char* cmdline1_[] = { "--foo=12", "--bar", "1"};
34+
std::vector<std::string> cmdline1 = sv(cmdline1_,
35+
sizeof(cmdline1_)/sizeof(const char*));
36+
po::variables_map vm;
37+
po::store(po::command_line_parser(cmdline1).options(desc).run(), vm);
38+
po::notify(vm);
39+
40+
BOOST_REQUIRE(!!foo);
41+
BOOST_CHECK(*foo == 12);
42+
43+
BOOST_REQUIRE(!!bar);
44+
BOOST_CHECK(*bar == 1);
45+
46+
BOOST_CHECK(!baz);
47+
}
48+
49+
int main(int, char*[])
50+
{
51+
test_optional();
52+
return 0;
53+
}

0 commit comments

Comments
 (0)