mirror of
https://github.com/boostorg/program_options.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
18 Commits
boost-1.63
...
boost-1.65
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9ef3853c6 | ||
|
|
d164a20bc9 | ||
|
|
3e1d2603e6 | ||
|
|
acaa4c94e1 | ||
|
|
a30cc1082f | ||
|
|
0565d1ee16 | ||
|
|
b35e654335 | ||
|
|
ed72cc2f92 | ||
|
|
c83abc21a0 | ||
|
|
7729850bb7 | ||
|
|
5dc325580b | ||
|
|
3277249932 | ||
|
|
1c2472b8d7 | ||
|
|
1f9413f532 | ||
|
|
dfdbcfca0f | ||
|
|
5a85b81fcf | ||
|
|
8ca8b3957a | ||
|
|
a495b0210a |
@@ -9,13 +9,8 @@ SOURCES =
|
||||
convert winmain split
|
||||
;
|
||||
|
||||
lib boost_program_options
|
||||
boost-lib program_options
|
||||
: $(SOURCES).cpp
|
||||
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
|
||||
# See https://svn.boost.org/trac/boost/ticket/5049
|
||||
: # See https://svn.boost.org/trac/boost/ticket/5049
|
||||
<target-os>hpux,<toolset>gcc:<define>_INCLUDE_STDC__SOURCE_199901
|
||||
:
|
||||
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1
|
||||
;
|
||||
|
||||
boost-install boost_program_options ;
|
||||
;
|
||||
@@ -435,6 +435,58 @@ vector<string> to_pass_further = collect_unrecognized(parsed.options, incl
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Testing Option Presence</title>
|
||||
|
||||
<para>Until now we have tested whether an option has been set using the
|
||||
<methodname alt="boost::program_options::variables_map::count">count</methodname> method on the &variables_map;
|
||||
class; as you are repeating the (string literal) name of the option this is prone to typos and/or errors
|
||||
resulting from renaming the option in one place but not the other:
|
||||
<programlisting><![CDATA[
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("compression", po::value<int>(), "set compression level")
|
||||
;
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||
po::notify(vm);
|
||||
|
||||
if (vm.count("compression")) {
|
||||
cout << "Compression level was set to "
|
||||
<< vm["compression"].as<int>() << ".\n";
|
||||
} else {
|
||||
cout << "Compression level was not set.\n";
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Instead, you can use a variable of type <classname alt="boost::optional">boost::optional</classname>;
|
||||
<libraryname>Program_options</libraryname> provides special support for <libraryname>Boost.Optional</libraryname>
|
||||
such that if the user specifies the option the <classname alt="boost::optional">boost::optional</classname>
|
||||
variable will be initialized to the appropriate value:
|
||||
<programlisting><![CDATA[
|
||||
po::options_description desc("Allowed options");
|
||||
boost::optional<int> compression;
|
||||
desc.add_options()
|
||||
("compression", po::value(&compression), "set compression level")
|
||||
;
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||
po::notify(vm);
|
||||
|
||||
if (compression)) {
|
||||
cout << "Compression level was set to " << *compression << ".\n";
|
||||
} else {
|
||||
cout << "Compression level was not set.\n";
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -178,5 +178,4 @@
|
||||
|
||||
12. Deferred
|
||||
|
||||
- storing value to boost::optional
|
||||
- setting a flag when option is found
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
// forward declaration
|
||||
namespace boost { template<class T> class optional; }
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
extern BOOST_PROGRAM_OPTIONS_DECL std::string arg;
|
||||
@@ -152,6 +155,20 @@ namespace boost { namespace program_options {
|
||||
}
|
||||
}
|
||||
|
||||
/** Validates optional arguments. */
|
||||
template<class T, class charT>
|
||||
void validate(boost::any& v,
|
||||
const std::vector<std::basic_string<charT> >& s,
|
||||
boost::optional<T>*,
|
||||
int)
|
||||
{
|
||||
validators::check_first_occurrence(v);
|
||||
validators::get_single_string(s);
|
||||
boost::any a;
|
||||
validate(a, s, (T*)0, 0);
|
||||
v = boost::any(boost::optional<T>(boost::any_cast<T>(a)));
|
||||
}
|
||||
|
||||
template<class T, class charT>
|
||||
void
|
||||
typed_value<T, charT>::
|
||||
|
||||
@@ -68,7 +68,11 @@ namespace boost {
|
||||
|
||||
|
||||
private: // iterator core operations
|
||||
friend class iterator_core_access;
|
||||
#ifdef __DCC__
|
||||
friend class boost::iterator_core_access;
|
||||
#else
|
||||
friend class iterator_core_access;
|
||||
#endif
|
||||
|
||||
void increment()
|
||||
{
|
||||
|
||||
@@ -26,7 +26,12 @@ namespace boost { namespace program_options {
|
||||
inline std::string strip_prefixes(const std::string& text)
|
||||
{
|
||||
// "--foo-bar" -> "foo-bar"
|
||||
return text.substr(text.find_first_not_of("-/"));
|
||||
std::string::size_type i = text.find_first_not_of("-/");
|
||||
if (i == std::string::npos) {
|
||||
return text;
|
||||
} else {
|
||||
return text.substr(i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Base class for all errors in the library. */
|
||||
@@ -169,7 +174,7 @@ namespace boost { namespace program_options {
|
||||
virtual void set_option_name(const std::string& option_name)
|
||||
{ set_substitute("option", option_name);}
|
||||
|
||||
std::string get_option_name() const throw()
|
||||
std::string get_option_name() const
|
||||
{ return get_canonical_option_name(); }
|
||||
|
||||
void set_original_token(const std::string& original_token)
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace boost { namespace program_options {
|
||||
basic_option(const std::string& xstring_key,
|
||||
const std::vector< std::string> &xvalue)
|
||||
: string_key(xstring_key)
|
||||
, position_key(-1)
|
||||
, value(xvalue)
|
||||
, unregistered(false)
|
||||
, case_insensitive(false)
|
||||
|
||||
@@ -38,11 +38,6 @@ namespace boost { namespace program_options {
|
||||
should be present on the command line. */
|
||||
virtual unsigned max_tokens() const = 0;
|
||||
|
||||
/** Returns true if the option should only take adjacent token,
|
||||
not one from further command-line arguments.
|
||||
*/
|
||||
virtual bool adjacent_tokens_only() const = 0;
|
||||
|
||||
/** Returns true if values from different sources should be composed.
|
||||
Otherwise, value from the first source is used and values from
|
||||
other sources are discarded.
|
||||
@@ -53,7 +48,7 @@ namespace boost { namespace program_options {
|
||||
|
||||
*/
|
||||
virtual bool is_required() const = 0;
|
||||
|
||||
|
||||
/** Parses a group of tokens that specify a value of option.
|
||||
Stores the result in 'value_store', using whatever representation
|
||||
is desired. May be be called several times if value of the same
|
||||
@@ -139,7 +134,6 @@ namespace boost { namespace program_options {
|
||||
|
||||
unsigned min_tokens() const;
|
||||
unsigned max_tokens() const;
|
||||
bool adjacent_tokens_only() const { return false; }
|
||||
|
||||
bool is_composing() const { return false; }
|
||||
|
||||
@@ -224,10 +218,7 @@ namespace boost { namespace program_options {
|
||||
|
||||
/** Specifies an implicit value, which will be used
|
||||
if the option is given, but without an adjacent value.
|
||||
Using this implies that an explicit value is optional, but if
|
||||
given, must be strictly adjacent to the option, i.e.: '-ovalue'
|
||||
or '--option=value'. Giving '-o' or '--option' will cause the
|
||||
implicit value to be applied.
|
||||
Using this implies that an explicit value is optional,
|
||||
*/
|
||||
typed_value* implicit_value(const T &v)
|
||||
{
|
||||
@@ -331,8 +322,6 @@ namespace boost { namespace program_options {
|
||||
}
|
||||
}
|
||||
|
||||
bool adjacent_tokens_only() const { return !m_implicit_value.empty(); }
|
||||
|
||||
bool is_required() const { return m_required; }
|
||||
|
||||
/** Creates an instance of the 'validator' class and calls
|
||||
|
||||
@@ -15,5 +15,8 @@
|
||||
#endif
|
||||
#define BOOST_PROGRAM_OPTIONS_VERSION 2
|
||||
|
||||
// Signal that implicit options will use values from next
|
||||
// token, if available.
|
||||
#define BOOST_PROGRAM_OPTIONS_IMPLICIT_VALUE_NEXT_TOKEN 1
|
||||
|
||||
#endif
|
||||
|
||||
@@ -313,9 +313,6 @@ namespace boost { namespace program_options { namespace detail {
|
||||
if (!xd)
|
||||
continue;
|
||||
|
||||
if (xd->semantic()->adjacent_tokens_only())
|
||||
continue;
|
||||
|
||||
unsigned min_tokens = xd->semantic()->min_tokens();
|
||||
unsigned max_tokens = xd->semantic()->max_tokens();
|
||||
if (min_tokens < max_tokens && opt.value.size() < max_tokens)
|
||||
@@ -437,9 +434,6 @@ namespace boost { namespace program_options { namespace detail {
|
||||
// (the value in --foo=1) counts as a separate token, and if present
|
||||
// must be consumed. The following tokens on the command line may be
|
||||
// left unconsumed.
|
||||
|
||||
// We don't check if those tokens look like option, or not!
|
||||
|
||||
unsigned min_tokens = d.semantic()->min_tokens();
|
||||
unsigned max_tokens = d.semantic()->max_tokens();
|
||||
|
||||
@@ -453,9 +447,8 @@ namespace boost { namespace program_options { namespace detail {
|
||||
invalid_command_line_syntax(invalid_command_line_syntax::extra_parameter));
|
||||
}
|
||||
|
||||
// If an option wants, at minimum, N tokens, we grab them there,
|
||||
// when adding these tokens as values to current option we check
|
||||
// if they look like options
|
||||
// Grab min_tokens values from other_tokens, but only if those tokens
|
||||
// are not recognized as options themselves.
|
||||
if (opt.value.size() <= min_tokens)
|
||||
{
|
||||
min_tokens -= static_cast<unsigned>(opt.value.size());
|
||||
@@ -465,7 +458,7 @@ namespace boost { namespace program_options { namespace detail {
|
||||
min_tokens = 0;
|
||||
}
|
||||
|
||||
// Everything's OK, move the values to the result.
|
||||
// Everything's OK, move the values to the result.
|
||||
for(;!other_tokens.empty() && min_tokens--; )
|
||||
{
|
||||
// check if extra parameter looks like a known option
|
||||
|
||||
@@ -42,16 +42,15 @@ namespace boost { namespace program_options {
|
||||
string option_name;
|
||||
string original_token;
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try
|
||||
#endif
|
||||
{
|
||||
|
||||
// First, convert/store all given options
|
||||
for (i = 0; i < options.options.size(); ++i) {
|
||||
|
||||
option_name = options.options[i].string_key;
|
||||
original_token = options.options[i].original_tokens.size() ?
|
||||
options.options[i].original_tokens[0] :
|
||||
option_name;
|
||||
// Skip positional options without name
|
||||
if (option_name.empty())
|
||||
continue;
|
||||
@@ -68,7 +67,7 @@ namespace boost { namespace program_options {
|
||||
if (xm.m_final.count(option_name))
|
||||
continue;
|
||||
|
||||
string original_token = options.options[i].original_tokens.size() ?
|
||||
original_token = options.options[i].original_tokens.size() ?
|
||||
options.options[i].original_tokens[0] : "";
|
||||
const option_description& d = desc.find(option_name, false,
|
||||
false, false);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import testing ;
|
||||
|
||||
project
|
||||
: requirements
|
||||
@@ -33,6 +34,7 @@ test-suite program_options :
|
||||
[ po-test unrecognized_test.cpp ]
|
||||
[ po-test required_test.cpp : required_test.cfg ]
|
||||
[ po-test exception_txt_test.cpp ]
|
||||
[ po-test optional_test.cpp ]
|
||||
[ run options_description_test.cpp : : : <rtti>off <define>BOOST_NO_RTTI <define>BOOST_NO_TYPEID : options_description_no_rtti_test ]
|
||||
;
|
||||
|
||||
|
||||
@@ -63,8 +63,7 @@ struct test_case {
|
||||
The "boost::program_options" in parameter type is needed because CW9
|
||||
has std::detail and it causes an ambiguity.
|
||||
*/
|
||||
void apply_syntax(options_description& desc,
|
||||
positional_options_description & m_positional,
|
||||
void apply_syntax(options_description& desc,
|
||||
const char* syntax)
|
||||
{
|
||||
|
||||
@@ -78,8 +77,7 @@ void apply_syntax(options_description& desc,
|
||||
v = value<string>();
|
||||
s.resize(s.size()-1);
|
||||
} else if (*(s.end()-1) == '?') {
|
||||
v = value<string>()->implicit_value("bar");
|
||||
m_positional.add("positional", -1);
|
||||
v = value<string>()->implicit_value("default");
|
||||
s.resize(s.size()-1);
|
||||
} else if (*(s.end()-1) == '*') {
|
||||
v = value<vector<string> >()->multitoken();
|
||||
@@ -114,14 +112,12 @@ void test_cmdline(const char* syntax,
|
||||
}
|
||||
}
|
||||
options_description desc;
|
||||
positional_options_description m_positional;
|
||||
apply_syntax(desc, m_positional, syntax);
|
||||
apply_syntax(desc, syntax);
|
||||
|
||||
cmdline cmd(xinput);
|
||||
cmd.style(style);
|
||||
cmd.set_options_description(desc);
|
||||
if(m_positional.max_total_count())
|
||||
cmd.set_positional_options(m_positional);
|
||||
|
||||
|
||||
string result;
|
||||
int status = 0;
|
||||
@@ -133,9 +129,7 @@ void test_cmdline(const char* syntax,
|
||||
{
|
||||
option opt = options[j];
|
||||
|
||||
if (opt.position_key != -1
|
||||
&& (m_positional.max_total_count() == 0 || (size_t)opt.position_key >= m_positional.max_total_count()
|
||||
|| m_positional.name_for_position(opt.position_key) != "positional")) {
|
||||
if (opt.position_key != -1) {
|
||||
if (!result.empty())
|
||||
result += " ";
|
||||
result += opt.value[0];
|
||||
@@ -233,7 +227,7 @@ void test_long_options()
|
||||
{"--giz", s_success, "Giz:"},
|
||||
{0, 0, 0}
|
||||
};
|
||||
test_cmdline("foo bar= Giz", style, test_cases4);
|
||||
test_cmdline("foo bar= baz? Giz", style, test_cases4);
|
||||
}
|
||||
|
||||
void test_short_options()
|
||||
@@ -353,7 +347,7 @@ void test_disguised_long()
|
||||
{"-bee=x -by", s_success, "bee:x bee:y"},
|
||||
{0, 0, 0}
|
||||
};
|
||||
test_cmdline("foo,f goo,g= bee,b=", style, test_cases1);
|
||||
test_cmdline("foo,f goo,g= bee,b?", style, test_cases1);
|
||||
|
||||
style = cmdline::style_t(style | allow_slash_for_short);
|
||||
test_case test_cases2[] = {
|
||||
@@ -361,7 +355,7 @@ void test_disguised_long()
|
||||
{"/goo=x", s_success, "goo:x"},
|
||||
{0, 0, 0}
|
||||
};
|
||||
test_cmdline("foo,f goo,g=", style, test_cases2);
|
||||
test_cmdline("foo,f goo,g= bee,b?", style, test_cases2);
|
||||
}
|
||||
|
||||
void test_guessing()
|
||||
@@ -622,23 +616,22 @@ void test_implicit_value()
|
||||
);
|
||||
|
||||
test_case test_cases1[] = {
|
||||
{"--foo bar", s_success, "foo: positional:bar"},
|
||||
{"--foo=bar foobar", s_success, "foo:bar positional:foobar"},
|
||||
// 'bar' does not even look like option, so is consumed
|
||||
{"--foo bar", s_success, "foo:bar"},
|
||||
// '--bar' looks like option, and such option exists, so we don't consume this token
|
||||
{"--foo --bar", s_success, "foo: bar:"},
|
||||
// '--biz' looks like option, but does not match any existing one.
|
||||
// Presently this results in parse error, since
|
||||
// (1) in cmdline.cpp:finish_option, we only consume following tokens if they are
|
||||
// requires
|
||||
// (2) in cmdline.cpp:run, we let options consume following positional options
|
||||
// For --biz, an exception is thrown between 1 and 2.
|
||||
// We might want to fix that in future.
|
||||
{"--foo --biz", s_unknown_option, ""},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
test_cmdline("positional= foo?", style, test_cases1);
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | allow_dash_for_short
|
||||
| short_allow_adjacent);
|
||||
|
||||
test_case test_cases2[] = {
|
||||
{"-f bar", s_success, "-f: positional:bar"},
|
||||
{"-fbar foobar", s_success, "-f:bar positional:foobar"},
|
||||
{0, 0, 0}
|
||||
};
|
||||
test_cmdline("positional= ,f?", style, test_cases2);
|
||||
test_cmdline("foo? bar?", style, test_cases1);
|
||||
}
|
||||
|
||||
int main(int /*ac*/, char** /*av*/)
|
||||
|
||||
@@ -620,7 +620,27 @@ void test_invalid_command_line_style_exception_msg()
|
||||
}
|
||||
}
|
||||
|
||||
void test_empty_value_inner(options_description &opts, variables_map& vm) {
|
||||
positional_options_description popts;
|
||||
opts.add_options()("foo", value<uint32_t>()->value_name("<time>")->required());
|
||||
popts.add("foo", 1);
|
||||
vector<string> tokens{""};
|
||||
parsed_options parsed = command_line_parser(tokens)
|
||||
.style(command_line_style::default_style & ~command_line_style::allow_guessing)
|
||||
.options(opts)
|
||||
.positional(popts)
|
||||
.run();
|
||||
store(parsed, vm);
|
||||
}
|
||||
|
||||
void test_empty_value() {
|
||||
// Test that passing empty token for an option that requires integer does not result
|
||||
// in out-of-range error in error reporting code.
|
||||
test_exception<invalid_option_value>(
|
||||
"test_empty_value",
|
||||
"the argument for option '--foo' is invalid",
|
||||
test_empty_value_inner);
|
||||
}
|
||||
|
||||
int main(int /*ac*/, char** /*av*/)
|
||||
{
|
||||
@@ -633,6 +653,7 @@ int main(int /*ac*/, char** /*av*/)
|
||||
test_multiple_values_not_allowed_exception_msg();
|
||||
test_required_option_exception_msg();
|
||||
test_at_least_one_value_required_exception_msg();
|
||||
test_empty_value();
|
||||
|
||||
string test_name;
|
||||
string expected_message;
|
||||
|
||||
53
test/optional_test.cpp
Normal file
53
test/optional_test.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
namespace po = boost::program_options;
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "minitest.hpp"
|
||||
|
||||
std::vector<std::string> sv(const char* array[], unsigned size)
|
||||
{
|
||||
std::vector<std::string> r;
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
r.push_back(array[i]);
|
||||
return r;
|
||||
}
|
||||
|
||||
void test_optional()
|
||||
{
|
||||
boost::optional<int> foo, bar, baz;
|
||||
|
||||
po::options_description desc;
|
||||
desc.add_options()
|
||||
("foo,f", po::value(&foo), "")
|
||||
("bar,b", po::value(&bar), "")
|
||||
("baz,z", po::value(&baz), "")
|
||||
;
|
||||
|
||||
const char* cmdline1_[] = { "--foo=12", "--bar", "1"};
|
||||
std::vector<std::string> cmdline1 = sv(cmdline1_,
|
||||
sizeof(cmdline1_)/sizeof(const char*));
|
||||
po::variables_map vm;
|
||||
po::store(po::command_line_parser(cmdline1).options(desc).run(), vm);
|
||||
po::notify(vm);
|
||||
|
||||
BOOST_REQUIRE(!!foo);
|
||||
BOOST_CHECK(*foo == 12);
|
||||
|
||||
BOOST_REQUIRE(!!bar);
|
||||
BOOST_CHECK(*bar == 1);
|
||||
|
||||
BOOST_CHECK(!baz);
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
test_optional();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user