Compare commits

...

82 Commits

Author SHA1 Message Date
Vladimir Prus
9d7c987526 Remove tabs. 2013-12-04 09:17:17 +04:00
Vladimir Prus
0a7005d7c6 Merge branch 'develop' 2013-12-04 09:13:36 +04:00
Michel Morin
fb4f36f3ee Merge r86524 (Correct broken links to C++ standard papers); fixes #9212
[SVN r86673]
2013-11-13 03:22:55 +00:00
Vladimir Prus
6bf4607eac Align columns across groups in --help output.
Patch from Leo Goodstadt.
Fixes #6114.


[SVN r86571]
2013-11-06 09:23:14 +00:00
Michel Morin
81844db902 Correct broken links to C++ standard papers. Refs #9212.
[SVN r86524]
2013-10-30 12:51:24 +00:00
Stephen Kelly
d3f32ca813 ProgramOptions: Remove obsolete GCC version check.
[SVN r86115]
2013-10-01 08:47:31 +00:00
Stephen Kelly
da03a78b66 Remove obsolete MSVC check from pragma guard
git grep -h -B1 "^#\s*pragma once" | grep -v pragma | sort | uniq

is now clean.

[SVN r85952]
2013-09-26 13:02:51 +00:00
Vladimir Prus
5059a8f393 Silence gcc warning. Thanks to Chris Stylianou.
Closes #8725.


[SVN r84903]
2013-06-24 19:37:37 +00:00
Vladimir Prus
b430a83dfb Fix report of error for options with dashes.
Fixes #8009.
Patch from Markus Roth.


[SVN r82805]
2013-02-10 12:07:05 +00:00
Vladimir Prus
96b365ce17 Revive exception_txt_test.
Found while working on #8009.


[SVN r82804]
2013-02-10 11:59:51 +00:00
Vladimir Prus
2ceb54f9a2 Merge: Really fixes #7049.
[SVN r82556]
2013-01-20 07:13:55 +00:00
Vladimir Prus
e8e538036d Merge program_options revisions 79280 and 79283.
[SVN r79758]
2012-07-26 16:22:04 +00:00
Vladimir Prus
f11104084b Really fixes #7049.
[SVN r79477]
2012-07-13 19:06:30 +00:00
Vladimir Prus
a9993a8f01 Fix indent
[SVN r79289]
2012-07-05 17:00:50 +00:00
Vladimir Prus
d27da53324 Fix warnings on 64-bit systems.
Closes #7077.


[SVN r79283]
2012-07-05 13:17:24 +00:00
Vladimir Prus
94fc1c677d Qualify 'distance' with 'std'.
Fixes #7086.


[SVN r79280]
2012-07-05 12:39:16 +00:00
Vladimir Prus
3360ad0ea2 Fix compilation error.
Fixes #7049. Patch from Deniz Bahadir.


[SVN r79164]
2012-06-29 09:04:09 +00:00
Vladimir Prus
f271e1ffce Merge from trunk.
[SVN r78362]
2012-05-06 18:13:25 +00:00
Vladimir Prus
5a470bcc41 Adjust spelling.
[SVN r78359]
2012-05-06 17:32:47 +00:00
Vladimir Prus
2eda0313c7 Include necessary header, don't rely on two-phase lookup not being there.
Fixes #6790.


[SVN r78331]
2012-05-05 07:17:28 +00:00
Dave Abrahams
63e8f1c954 Summary: Moved libs/detail/utf8_codecvt_facet.cpp to boost/detail/utf……8_codecvt_facet.ipp
Author: Dave Abrahams <dave@boostpro.com>


[SVN r78119]
2012-04-21 22:36:59 +00:00
Dave Abrahams
a70de616b0 Summary: Moved libs/detail/utf8_codecvt_facet.cpp to boost/detail/utf8_codecvt_facet.ipp
Author: Dave Abrahams <dave@boostpro.com>


[SVN r78081]
2012-04-19 18:19:20 +00:00
Vladimir Prus
43e53664f2 Merge: Allow to specify how option's value is named in help message.
Fixes #4781.


[SVN r77932]
2012-04-12 09:40:34 +00:00
Vladimir Prus
5cbfa80841 Allow to specify how option's value is named in help message.
Fixes #4781.


[SVN r77931]
2012-04-12 08:37:34 +00:00
Vladimir Prus
cddd2c593f Merge from trunk.
[SVN r77829]
2012-04-08 10:02:26 +00:00
Vladimir Prus
fd7b310993 Improve error reporting.
The name of option is now shown in most cases when it's feasible, and
clarify of the error messages has been improved throughout.

Patch from Leo Goodstadt.


[SVN r77827]
2012-04-08 08:42:39 +00:00
Vladimir Prus
ae0ecf6581 Make vector print function in examples be more correct.
Fixes #6677.


[SVN r77826]
2012-04-08 08:17:53 +00:00
Jürgen Hunold
c054a3b034 Merge 73299,73301,73308 from ^/trunk
------------------------------------------------------------------------
r73299 | jhunold | 2011-07-22 14:08:03 +0200 (Fr, 22 Jul 2011) | 2 lines

Enable visibility support. Refs #2114.

------------------------------------------------------------------------
r73301 | jhunold | 2011-07-22 16:15:45 +0200 (Fr, 22 Jul 2011) | 2 lines

Enable visibility support. Refs #2114.

------------------------------------------------------------------------
r73308 | jhunold | 2011-07-23 11:24:35 +0200 (Sa, 23 Jul 2011) | 2 lines

Enable visibility support. Refs #2114.

------------------------------------------------------------------------


[SVN r73381]
2011-07-26 17:40:21 +00:00
Marshall Clow
cf09cfde1f Merging fixes for #3909 to release branch
[SVN r68077]
2011-01-13 01:46:49 +00:00
Marshall Clow
ec3192a6c8 fix typo
[SVN r67037]
2010-12-05 20:39:48 +00:00
Marshall Clow
abd32c88fc Merge doc patches to release; fixes #3992, fixes #4858
[SVN r67034]
2010-12-05 20:36:54 +00:00
Jürgen Hunold
0c04bde7e0 Merge 65318,65319 from ^/trunk
------------------------------------------------------------------------
r65318 | jhunold | 2010-09-06 10:43:06 +0200 (Mo, 06 Sep 2010) | 3 lines

Suppress msvc warning 4251 about templates as base classes not having a dll-interface.
Suppress msvc warning 4275 about base class std::logic_error not having a dll-interface.

------------------------------------------------------------------------
r65319 | jhunold | 2010-09-06 16:14:09 +0200 (Mo, 06 Sep 2010) | 2 lines

Fix: windows does not have wait(), use own macros instead.

------------------------------------------------------------------------


[SVN r65326]
2010-09-07 05:52:45 +00:00
Vladimir Prus
cd1b58aac2 Merge from trunk
[SVN r63911]
2010-07-12 07:14:14 +00:00
Daniel James
310a638dc8 Merge documentation fixes.
* Use `doc/src/*.css` instead of `doc/html/*.css`.
* Remove wiki and people directories.
* Some documentation fixes.
* Left out `minimal.css` changes and boostbook changes because of clashes.


[SVN r63347]
2010-06-26 12:30:09 +00:00
Vladimir Prus
cbacc90d8f Merge from trunk
[SVN r61962]
2010-05-14 06:55:15 +00:00
Vladimir Prus
ab9901f553 Merge from trunk.
[SVN r58818]
2010-01-08 21:00:57 +00:00
Troy D. Straszheim
5820ee9f7f rm cmake from the release branch before it goes out broken. Policy dictates that you never commit to release, you commit to trunk and merge to release.
[SVN r56941]
2009-10-17 01:10:45 +00:00
Troy D. Straszheim
67ba23b8d9 Add basic copyright/license to keep cmake out of the inspection report
[SVN r55095]
2009-07-22 21:51:01 +00:00
Vladimir Prus
c3cb7cf05d Merge from trunk
[SVN r54884]
2009-07-11 12:00:18 +00:00
Jeremiah Willcock
bdbd3dfc42 Fixed almost all tab and min/max issues found by inspect tool
[SVN r53142]
2009-05-20 19:41:20 +00:00
Douglas Gregor
15c990b7af Fix CMake build of Program Options DLL tests
[SVN r52847]
2009-05-08 06:13:08 +00:00
Douglas Gregor
1a884cef74 Fix CMake test builds for ProgramOptions and Units libraries.
[SVN r52843]
2009-05-07 23:53:56 +00:00
Vladimir Prus
3c6401b19a Prevent multitoken options from eating tokens after '--' separator.
Thanks to Gevorg Voskanyan for the bug report and the testcase.


[SVN r52440]
2009-04-17 09:45:00 +00:00
Vladimir Prus
ed4847e0a7 Fix warnings
[SVN r52439]
2009-04-17 09:35:10 +00:00
Vladimir Prus
6eef67f5ba Merge from trunk
[SVN r52211]
2009-04-06 09:43:03 +00:00
Vladimir Prus
91d9cabb51 When processing value multitoken options, don't eat futher options.
Fixes #469.


[SVN r52154]
2009-04-03 13:42:29 +00:00
John Maddock
22e8d11888 Merge PDF build changes from Trunk.
[SVN r51417]
2009-02-23 18:39:32 +00:00
Troy D. Straszheim
b55001a061 merge of cmake build files from trunk per beman
[SVN r50756]
2009-01-24 18:57:20 +00:00
Vladimir Prus
2a16575f98 Merge from trunk. No code changes, only properties and docs and examples
[SVN r47336]
2008-07-12 09:03:18 +00:00
Vladimir Prus
f14a369077 Merge r43992
[SVN r47335]
2008-07-12 08:58:04 +00:00
Vladimir Prus
9df5124fed Don't hardcode cout.
Suggested by Guido Ziliotti.


[SVN r47285]
2008-07-10 09:40:04 +00:00
Vladimir Prus
86487c7b63 Add example of zero_tokens
[SVN r45450]
2008-05-17 07:28:17 +00:00
Vladimir Prus
6cd68a2fd9 Clarify docs. Fixes #1530.
[SVN r45448]
2008-05-17 07:06:59 +00:00
Vladimir Prus
f0eae2ccfe Merge from trunk:
Tolerate argc being zero.

Patch from C. K. Jester-Young.


[SVN r43331]
2008-02-20 14:55:25 +00:00
Beman Dawes
b2a01d9405 Full merge from trunk at revision 41356 of entire boost-root tree.
[SVN r41370]
2007-11-25 18:38:02 +00:00
Beman Dawes
2d0c627d34 Full merge from trunk at revision 41356 of entire boost-root tree.
[SVN r41369]
2007-11-25 18:07:19 +00:00
Beman Dawes
2cc29f6dfa Starting point for releases
[SVN r39706]
2007-10-05 14:25:06 +00:00
nobody
198b29d107 This commit was manufactured by cvs2svn to create tag
'Version_1_34_1'.

[SVN r38286]
2007-07-24 19:28:14 +00:00
Thomas Witt
f407b6ce47 Applying patch to fix example build.
[SVN r38153]
2007-07-06 19:20:51 +00:00
Vladimir Prus
87938cfa8e Merge: Add missing include, to try to fix compilation on sun
[SVN r37006]
2007-02-19 19:28:49 +00:00
Hartmut Kaiser
9a73a1c412 Trying to fix sun-5.8 error.
[SVN r36943]
2007-02-15 01:25:10 +00:00
Daniel James
37143a449d Fix a broken link
[SVN r36653]
2007-01-07 22:51:27 +00:00
Vladimir Prus
2e0e9fd30b Merge: Fix dynamic linking
[SVN r35992]
2006-11-10 20:31:43 +00:00
Vladimir Prus
1bd588d677 Merge from HEAD.
Allow building of shared versions of some Boost.Test libraries.
Adjust tests to use always use static linking to Boost.Test, since
linking to the shared version requires test changes.

Patch from Juergen Hunold.


[SVN r35990]
2006-11-10 19:59:52 +00:00
Beman Dawes
d83e0dea37 Merged copyright and license addition
[SVN r35907]
2006-11-07 19:27:00 +00:00
Rene Rivera
54daca4c09 Remove obsolete Boost.Build v1 files.
[SVN r35880]
2006-11-06 17:10:46 +00:00
John Maddock
f4eac99310 Fix for Borland compilers.
[SVN r35652]
2006-10-18 12:33:54 +00:00
Vladimir Prus
a367a1b021 Make intel happy
[SVN r35034]
2006-09-07 08:06:16 +00:00
Gennaro Prota
720d0455dd avoid bogus detection of min/max guideline violation (Inspect tool)
[SVN r34784]
2006-07-29 21:00:31 +00:00
Hartmut Kaiser
aad1a60172 Just another fix for the Intel DLL issue.
[SVN r34196]
2006-06-06 14:30:28 +00:00
Hartmut Kaiser
1e4d1dee3d Fixed intel 9.1 dll export problem.
[SVN r34168]
2006-06-04 15:50:47 +00:00
Hartmut Kaiser
e718d0a8a5 Try to fix Intel dll issue on Windows.
[SVN r34104]
2006-05-26 16:52:54 +00:00
Hartmut Kaiser
ab30ec28eb Fixed a dllimport/dllexport problem.
[SVN r34049]
2006-05-20 22:14:41 +00:00
Vladimir Prus
682f1b7670 Merge from trunk
[SVN r33992]
2006-05-18 06:06:53 +00:00
Vladimir Prus
43577d0ca8 Merge from trunk
[SVN r33970]
2006-05-15 14:06:24 +00:00
Vladimir Prus
d05b400b13 Merge from trunk
[SVN r33787]
2006-04-24 09:51:01 +00:00
Vladimir Prus
4863727509 Merge from trunk
[SVN r33785]
2006-04-24 09:41:50 +00:00
Vladimir Prus
7d90a1b1b2 Merge from trunk
[SVN r33783]
2006-04-24 09:15:27 +00:00
Vladimir Prus
ac9830625b Merge from trunk
[SVN r33781]
2006-04-24 08:52:43 +00:00
Vladimir Prus
252a3f9ebd Merge from trunk
[SVN r33779]
2006-04-24 08:29:05 +00:00
Vladimir Prus
b1dc87da3c Merge from trunk
[SVN r33777]
2006-04-24 08:02:07 +00:00
nobody
1fbf955272 This commit was manufactured by cvs2svn to create branch 'RC_1_34_0'.
[SVN r33417]
2006-03-21 02:26:31 +00:00
27 changed files with 1653 additions and 526 deletions

View File

@@ -20,7 +20,7 @@ int main(int ac, char* av[])
po::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("compression", po::value<int>(), "set compression level")
("compression", po::value<double>(), "set compression level")
;
po::variables_map vm;
@@ -34,7 +34,7 @@ int main(int ac, char* av[])
if (vm.count("compression")) {
cout << "Compression level was set to "
<< vm["compression"].as<int>() << ".\n";
<< vm["compression"].as<double>() << ".\n";
} else {
cout << "Compression level was not set.\n";
}

View File

@@ -18,7 +18,7 @@ using namespace std;
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
copy(v.begin(), v.end(), ostream_iterator<T>(os, " "));
return os;
}

View File

@@ -18,7 +18,7 @@ using namespace std;
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
copy(v.begin(), v.end(), ostream_iterator<T>(os, " "));
return os;
}

View File

@@ -8,7 +8,7 @@
#ifndef PROGRAM_OPTIONS_VP_2003_05_19
#define PROGRAM_OPTIONS_VP_2003_05_19
#if defined(_MSC_VER) && _MSC_VER >= 1020
#if defined(_MSC_VER)
#pragma once
#endif

View File

@@ -81,6 +81,18 @@ namespace boost { namespace program_options { namespace detail {
cmdline(int argc, const char*const * argv);
void style(int style);
/** returns the canonical option prefix associated with the command_line_style
* In order of precedence:
* allow_long : allow_long
* allow_long_disguise : allow_long_disguise
* allow_dash_for_short : allow_short | allow_dash_for_short
* allow_slash_for_short: allow_short | allow_slash_for_short
*
* This is mainly used for the diagnostic messages in exceptions
*/
int get_canonical_option_prefix();
void allow_unregistered();
void set_options_description(const options_description& desc);

View File

@@ -17,9 +17,7 @@
#include <boost/program_options/eof_iterator.hpp>
#include <boost/detail/workaround.hpp>
#if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3202))
#include <boost/program_options/detail/convert.hpp>
#endif
#if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042))
#include <istream> // std::getline

View File

@@ -100,7 +100,11 @@ namespace boost { namespace program_options {
basic_parsed_options<charT>
basic_command_line_parser<charT>::run()
{
parsed_options result(m_desc);
// save the canonical prefixes which were used by this cmdline parser
// eventually inside the parsed results
// This will be handy to format recognisable options
// for diagnostic messages if everything blows up much later on
parsed_options result(m_desc, detail::cmdline::get_canonical_option_prefix());
result.options = detail::cmdline::run();
// Presense of parsed_options -> wparsed_options conversion

View File

@@ -16,16 +16,17 @@ namespace boost { namespace program_options {
std::string
typed_value<T, charT>::name() const
{
std::string const& var = (m_value_name.empty() ? arg : m_value_name);
if (!m_implicit_value.empty() && !m_implicit_value_as_text.empty()) {
std::string msg = "[=arg(=" + m_implicit_value_as_text + ")]";
std::string msg = "[=" + var + "(=" + m_implicit_value_as_text + ")]";
if (!m_default_value.empty() && !m_default_value_as_text.empty())
msg += " (=" + m_default_value_as_text + ")";
return msg;
}
else if (!m_default_value.empty() && !m_default_value_as_text.empty()) {
return arg + " (=" + m_default_value_as_text + ")";
return var + " (=" + m_default_value_as_text + ")";
} else {
return arg;
return var;
}
}
@@ -104,12 +105,9 @@ namespace boost { namespace program_options {
int);
#endif
// For some reason, this declaration, which is require by the standard,
// cause gcc 3.2 to not generate code to specialization defined in
// cause msvc 7.1 to not generate code to specialization defined in
// value_semantic.cpp
#if ! ( ( BOOST_WORKAROUND(__GNUC__, <= 3) &&\
BOOST_WORKAROUND(__GNUC_MINOR__, < 3) ) || \
( BOOST_WORKAROUND(BOOST_MSVC, == 1310) ) \
)
#if ! ( BOOST_WORKAROUND(BOOST_MSVC, == 1310) )
BOOST_PROGRAM_OPTIONS_DECL void validate(boost::any& v,
const std::vector<std::string>& xs,
std::string*,

View File

@@ -12,6 +12,8 @@
#include <string>
#include <stdexcept>
#include <vector>
#include <map>
#if defined(BOOST_MSVC)
# pragma warning (push)
@@ -21,170 +23,18 @@
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("-/"));
}
/** Base class for all errors in the library. */
class BOOST_PROGRAM_OPTIONS_DECL error : public std::logic_error {
public:
error(const std::string& xwhat) : std::logic_error(xwhat) {}
};
class BOOST_PROGRAM_OPTIONS_DECL invalid_syntax : public error {
public:
enum kind_t {
long_not_allowed = 30,
long_adjacent_not_allowed,
short_adjacent_not_allowed,
empty_adjacent_parameter,
missing_parameter,
extra_parameter,
unrecognized_line
};
invalid_syntax(const std::string& tokens, kind_t kind);
// gcc says that throw specification on dtor is loosened
// without this line
~invalid_syntax() throw() {}
kind_t kind() const;
const std::string& tokens() const;
protected:
/** Used to convert kind_t to a related error text */
static std::string error_message(kind_t kind);
private:
// TODO: copy ctor might throw
std::string m_tokens;
kind_t m_kind;
};
/** Class thrown when option name is not recognized. */
class BOOST_PROGRAM_OPTIONS_DECL unknown_option : public error {
public:
unknown_option(const std::string& name)
: error(std::string("unknown option ").append(name)),
m_option_name(name)
{}
// gcc says that throw specification on dtor is loosened
// without this line
~unknown_option() throw() {}
const std::string& get_option_name() const throw();
private:
std::string m_option_name;
};
/** Class thrown when there's ambiguity amoung several possible options. */
class BOOST_PROGRAM_OPTIONS_DECL ambiguous_option : public error {
public:
ambiguous_option(const std::string& name,
const std::vector<std::string>& xalternatives)
: error(std::string("ambiguous option ").append(name))
, m_alternatives(xalternatives)
, m_option_name(name)
{}
~ambiguous_option() throw() {}
const std::string& get_option_name() const throw();
const std::vector<std::string>& alternatives() const throw();
private:
// TODO: copy ctor might throw
std::vector<std::string> m_alternatives;
std::string m_option_name;
};
/** Class thrown when there are several option values, but
user called a method which cannot return them all. */
class BOOST_PROGRAM_OPTIONS_DECL multiple_values : public error {
public:
multiple_values()
: error("multiple values")
, m_option_name() {}
~multiple_values() throw() {}
void set_option_name(const std::string& option);
const std::string& get_option_name() const throw();
private:
std::string m_option_name; // The name of the option which
// caused the exception.
};
/** Class thrown when there are several occurrences of an
option, but user called a method which cannot return
them all. */
class BOOST_PROGRAM_OPTIONS_DECL multiple_occurrences : public error {
public:
multiple_occurrences()
: error("multiple occurrences")
, m_option_name() {}
~multiple_occurrences() throw() {}
void set_option_name(const std::string& option);
const std::string& get_option_name() const throw();
private:
std::string m_option_name; // The name of the option which
// caused the exception.
};
/** Class thrown when value of option is incorrect. */
class BOOST_PROGRAM_OPTIONS_DECL validation_error : public error {
public:
enum kind_t {
multiple_values_not_allowed = 30,
at_least_one_value_required,
invalid_bool_value,
invalid_option_value,
invalid_option
};
validation_error(kind_t kind,
const std::string& option_value = "",
const std::string& option_name = "");
~validation_error() throw() {}
void set_option_name(const std::string& option);
const std::string& get_option_name() const throw();
const char* what() const throw();
protected:
/** Used to convert kind_t to a related error text */
static std::string error_message(kind_t kind);
private:
kind_t m_kind;
std::string m_option_name; // The name of the option which
// caused the exception.
std::string m_option_value; // Optional: value of the option m_options_name
mutable std::string m_message; // For on-demand formatting in 'what'
};
/** Class thrown if there is an invalid option value givenn */
class BOOST_PROGRAM_OPTIONS_DECL invalid_option_value
: public validation_error
{
public:
invalid_option_value(const std::string& value);
#ifndef BOOST_NO_STD_WSTRING
invalid_option_value(const std::wstring& value);
#endif
};
/** Class thrown when there are too many positional options.
This is a programming error.
@@ -192,16 +42,10 @@ namespace boost { namespace program_options {
class BOOST_PROGRAM_OPTIONS_DECL too_many_positional_options_error : public error {
public:
too_many_positional_options_error()
: error("too many positional options")
: error("too many positional options have been specified on the command line")
{}
};
/** Class thrown when there are syntax errors in given command line */
class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_syntax : public invalid_syntax {
public:
invalid_command_line_syntax(const std::string& tokens, kind_t kind);
};
/** Class thrown when there are programming error related to style */
class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_style : public error {
public:
@@ -214,26 +58,355 @@ namespace boost { namespace program_options {
class BOOST_PROGRAM_OPTIONS_DECL reading_file : public error {
public:
reading_file(const char* filename)
: error(std::string("can not read file ").append(filename))
: error(std::string("can not read options configuration file '").append(filename).append("'"))
{}
};
/** Class thrown when a required/mandatory option is missing */
class BOOST_PROGRAM_OPTIONS_DECL required_option : public error {
public:
required_option(const std::string& name)
: error(std::string("missing required option ").append(name))
, m_option_name(name)
{}
~required_option() throw() {}
const std::string& get_option_name() const throw();
/** Base class for most exceptions in the library.
*
* Substitutes the values for the parameter name
* placeholders in the template to create the human
* readable error message
*
* Placeholders are surrounded by % signs: %example%
* Poor man's version of boost::format
*
* If a parameter name is absent, perform default substitutions
* instead so ugly placeholders are never left in-place.
*
* Options are displayed in "canonical" form
* This is the most unambiguous form of the
* *parsed* option name and would correspond to
* option_description::format_name()
* i.e. what is shown by print_usage()
*
* The "canonical" form depends on whether the option is
* specified in short or long form, using dashes or slashes
* or without a prefix (from a configuration file)
*
* */
class BOOST_PROGRAM_OPTIONS_DECL error_with_option_name : public error {
protected:
/** can be
* 0 = no prefix (config file options)
* allow_long
* allow_dash_for_short
* allow_slash_for_short
* allow_long_disguise */
int m_option_style;
/** substitutions
* from placeholders to values */
std::map<std::string, std::string> m_substitutions;
typedef std::pair<std::string, std::string> string_pair;
std::map<std::string, string_pair > m_substitution_defaults;
public:
/** template with placeholders */
std::string m_error_template;
error_with_option_name(const std::string& template_,
const std::string& option_name = "",
const std::string& original_token = "",
int option_style = 0);
/** gcc says that throw specification on dtor is loosened
* without this line
* */
~error_with_option_name() throw() {}
//void dump() const
//{
// std::cerr << "m_substitution_defaults:\n";
// for (std::map<std::string, string_pair>::const_iterator iter = m_substitution_defaults.begin();
// iter != m_substitution_defaults.end(); ++iter)
// std::cerr << "\t" << iter->first << ":" << iter->second.first << "=" << iter->second.second << "\n";
// std::cerr << "m_substitutions:\n";
// for (std::map<std::string, std::string>::const_iterator iter = m_substitutions.begin();
// iter != m_substitutions.end(); ++iter)
// std::cerr << "\t" << iter->first << "=" << iter->second << "\n";
// std::cerr << "m_error_template:\n";
// std::cerr << "\t" << m_error_template << "\n";
// std::cerr << "canonical_option_prefix:[" << get_canonical_option_prefix() << "]\n";
// std::cerr << "canonical_option_name:[" << get_canonical_option_name() <<"]\n";
// std::cerr << "what:[" << what() << "]\n";
//}
/** Substitute
* parameter_name->value to create the error message from
* the error template */
void set_substitute(const std::string& parameter_name, const std::string& value)
{ m_substitutions[parameter_name] = value; }
/** If the parameter is missing, then make the
* from->to substitution instead */
void set_substitute_default(const std::string& parameter_name,
const std::string& from,
const std::string& to)
{
m_substitution_defaults[parameter_name] = std::make_pair(from, to);
}
/** Add context to an exception */
void add_context(const std::string& option_name,
const std::string& original_token,
int option_style)
{
set_option_name(option_name);
set_original_token(original_token);
set_prefix(option_style);
}
void set_prefix(int option_style)
{ m_option_style = option_style;}
/** Overridden in error_with_no_option_name */
virtual void set_option_name(const std::string& option_name)
{ set_substitute("option", option_name);}
std::string get_option_name() const throw()
{ return get_canonical_option_name(); }
void set_original_token(const std::string& original_token)
{ set_substitute("original_token", original_token);}
/** Creates the error_message on the fly
* Currently a thin wrapper for substitute_placeholders() */
virtual const char* what() const throw();
protected:
/** Used to hold the error text returned by what() */
mutable std::string m_message; // For on-demand formatting in 'what'
/** Makes all substitutions using the template */
virtual void substitute_placeholders(const std::string& error_template) const;
// helper function for substitute_placeholders
void replace_token(const std::string& from, const std::string& to) const;
/** Construct option name in accordance with the appropriate
* prefix style: i.e. long dash or short slash etc */
std::string get_canonical_option_name() const;
std::string get_canonical_option_prefix() const;
};
/** Class thrown when there are several option values, but
user called a method which cannot return them all. */
class BOOST_PROGRAM_OPTIONS_DECL multiple_values : public error_with_option_name {
public:
multiple_values()
: error_with_option_name("option '%canonical_option%' only takes a single argument"){}
~multiple_values() throw() {}
};
/** Class thrown when there are several occurrences of an
option, but user called a method which cannot return
them all. */
class BOOST_PROGRAM_OPTIONS_DECL multiple_occurrences : public error_with_option_name {
public:
multiple_occurrences()
: error_with_option_name("option '%canonical_option%' cannot be specified more than once"){}
~multiple_occurrences() throw() {}
};
/** Class thrown when a required/mandatory option is missing */
class BOOST_PROGRAM_OPTIONS_DECL required_option : public error_with_option_name {
public:
// option name is constructed by the option_descriptor and never on the fly
required_option(const std::string& option_name)
: error_with_option_name("the option '%canonical_option%' is required but missing", "", option_name)
{
}
~required_option() throw() {}
};
/** Base class of unparsable options,
* when the desired option cannot be identified.
*
*
* It makes no sense to have an option name, when we can't match an option to the
* parameter
*
* Having this a part of the error_with_option_name hierachy makes error handling
* a lot easier, even if the name indicates some sort of conceptual dissonance!
*
* */
class BOOST_PROGRAM_OPTIONS_DECL error_with_no_option_name : public error_with_option_name {
public:
error_with_no_option_name(const std::string& template_,
const std::string& original_token = "")
: error_with_option_name(template_, "", original_token)
{
}
/** Does NOT set option name, because no option name makes sense */
virtual void set_option_name(const std::string&) {}
~error_with_no_option_name() throw() {}
};
/** Class thrown when option name is not recognized. */
class BOOST_PROGRAM_OPTIONS_DECL unknown_option : public error_with_no_option_name {
public:
unknown_option(const std::string& original_token = "")
: error_with_no_option_name("unrecognised option '%canonical_option%'", original_token)
{
}
~unknown_option() throw() {}
};
/** Class thrown when there's ambiguity amoung several possible options. */
class BOOST_PROGRAM_OPTIONS_DECL ambiguous_option : public error_with_no_option_name {
public:
ambiguous_option(const std::vector<std::string>& xalternatives)
: error_with_no_option_name("option '%canonical_option%' is ambiguous"),
m_alternatives(xalternatives)
{}
~ambiguous_option() throw() {}
const std::vector<std::string>& alternatives() const throw() {return m_alternatives;}
protected:
/** Makes all substitutions using the template */
virtual void substitute_placeholders(const std::string& error_template) const;
private:
// TODO: copy ctor might throw
std::vector<std::string> m_alternatives;
};
/** Class thrown when there's syntax error either for command
* line or config file options. See derived children for
* concrete classes. */
class BOOST_PROGRAM_OPTIONS_DECL invalid_syntax : public error_with_option_name {
public:
enum kind_t {
long_not_allowed = 30,
long_adjacent_not_allowed,
short_adjacent_not_allowed,
empty_adjacent_parameter,
missing_parameter,
extra_parameter,
unrecognized_line
};
invalid_syntax(kind_t kind,
const std::string& option_name = "",
const std::string& original_token = "",
int option_style = 0):
error_with_option_name(get_template(kind), option_name, original_token, option_style),
m_kind(kind)
{
}
~invalid_syntax() throw() {}
kind_t kind() const {return m_kind;}
/** Convenience functions for backwards compatibility */
virtual std::string tokens() const {return get_option_name(); }
protected:
/** Used to convert kind_t to a related error text */
std::string get_template(kind_t kind);
kind_t m_kind;
};
class BOOST_PROGRAM_OPTIONS_DECL invalid_config_file_syntax : public invalid_syntax {
public:
invalid_config_file_syntax(const std::string& invalid_line, kind_t kind):
invalid_syntax(kind)
{
m_substitutions["invalid_line"] = invalid_line;
}
~invalid_config_file_syntax() throw() {}
/** Convenience functions for backwards compatibility */
virtual std::string tokens() const {return m_substitutions.find("invalid_line")->second; }
};
/** Class thrown when there are syntax errors in given command line */
class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_syntax : public invalid_syntax {
public:
invalid_command_line_syntax(kind_t kind,
const std::string& option_name = "",
const std::string& original_token = "",
int option_style = 0):
invalid_syntax(kind, option_name, original_token, option_style) {}
~invalid_command_line_syntax() throw() {}
};
/** Class thrown when value of option is incorrect. */
class BOOST_PROGRAM_OPTIONS_DECL validation_error : public error_with_option_name {
public:
enum kind_t {
multiple_values_not_allowed = 30,
at_least_one_value_required,
invalid_bool_value,
invalid_option_value,
invalid_option
};
private:
std::string m_option_name; // The name of the option which
// caused the exception.
};
public:
validation_error(kind_t kind,
const std::string& option_name = "",
const std::string& original_token = "",
int option_style = 0):
error_with_option_name(get_template(kind), option_name, original_token, option_style)
{
}
~validation_error() throw() {}
protected:
/** Used to convert kind_t to a related error text */
std::string get_template(kind_t kind);
kind_t m_kind;
};
/** Class thrown if there is an invalid option value given */
class BOOST_PROGRAM_OPTIONS_DECL invalid_option_value
: public validation_error
{
public:
invalid_option_value(const std::string& value);
#ifndef BOOST_NO_STD_WSTRING
invalid_option_value(const std::wstring& value);
#endif
};
/** Class thrown if there is an invalid bool value given */
class BOOST_PROGRAM_OPTIONS_DECL invalid_bool_value
: public validation_error
{
public:
invalid_bool_value(const std::string& value);
};
}}
#if defined(BOOST_MSVC)

View File

@@ -61,8 +61,8 @@ namespace program_options {
Alas, derived->base conversion for auto_ptr does not really work,
see
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2000/n1232.pdf
http://std.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#84
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2000/n1232.pdf
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#84
So, we have to use plain old pointers. Besides, users are not
expected to use the constructor directly.
@@ -102,6 +102,16 @@ namespace program_options {
*/
const std::string& key(const std::string& option) const;
/** Returns the canonical name for the option description to enable the user to
recognised a matching option.
1) For short options ('-', '/'), returns the short name prefixed.
2) For long options ('--' / '-') returns the long name prefixed
3) All other cases, returns the long name (if present) or the short name,
unprefixed.
*/
std::string canonical_display_name(int canonical_option_style = 0) const;
const std::string& long_name() const;
/// Explanation of this option
@@ -189,6 +199,10 @@ namespace program_options {
*/
options_description& add(const options_description& desc);
/** Find the maximum width of the option column, including options
in groups. */
unsigned get_option_column_width() const;
public:
/** Returns an object of implementation-defined type suitable for adding
options to options_description. The returned object will
@@ -219,7 +233,7 @@ namespace program_options {
/** Outputs 'desc' to the specified stream, calling 'f' to output each
option_description element. */
void print(std::ostream& os) const;
void print(std::ostream& os, unsigned width = 0) const;
private:
typedef std::map<std::string, int>::const_iterator name2index_iterator;

View File

@@ -36,8 +36,8 @@ namespace boost { namespace program_options {
template<class charT>
class basic_parsed_options {
public:
explicit basic_parsed_options(const options_description* xdescription)
: description(xdescription) {}
explicit basic_parsed_options(const options_description* xdescription, int options_prefix = 0)
: description(xdescription), m_options_prefix(options_prefix) {}
/** Options found in the source. */
std::vector< basic_option<charT> > options;
/** Options description that was used for parsing.
@@ -46,6 +46,17 @@ namespace boost { namespace program_options {
up to the caller. Can be NULL.
*/
const options_description* description;
/** Mainly used for the diagnostic messages in exceptions.
* The canonical option prefix for the parser which generated these results,
* depending on the settings for basic_command_line_parser::style() or
* cmdline::style(). In order of precedence of command_line_style enums:
* allow_long
* allow_long_disguise
* allow_dash_for_short
* allow_slash_for_short
*/
int m_options_prefix;
};
/** Specialization of basic_parsed_options which:
@@ -64,6 +75,17 @@ namespace boost { namespace program_options {
/** Stores UTF8 encoded options that were passed to constructor,
to avoid reverse conversion in some cases. */
basic_parsed_options<char> utf8_encoded_options;
/** Mainly used for the diagnostic messages in exceptions.
* The canonical option prefix for the parser which generated these results,
* depending on the settings for basic_command_line_parser::style() or
* cmdline::style(). In order of precedence of command_line_style enums:
* allow_long
* allow_long_disguise
* allow_dash_for_short
* allow_slash_for_short
*/
int m_options_prefix;
};
typedef basic_parsed_options<char> parsed_options;

View File

@@ -227,6 +227,13 @@ namespace boost { namespace program_options {
return this;
}
/** Specifies the name used to to the value in help message. */
typed_value* value_name(const std::string& name)
{
m_value_name = name;
return this;
}
/** 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
@@ -354,6 +361,7 @@ namespace boost { namespace program_options {
// Default value is stored as boost::any and not
// as boost::optional to avoid unnecessary instantiations.
std::string m_value_name;
boost::any m_default_value;
std::string m_default_value_as_text;
boost::any m_implicit_value;

View File

@@ -98,7 +98,7 @@ namespace boost { namespace program_options {
void store(const basic_parsed_options<char>& options,
variables_map& m, bool);
friend BOOST_PROGRAM_OPTIONS_DECL class variables_map;
friend class BOOST_PROGRAM_OPTIONS_DECL variables_map;
};
/** Implements string->string mapping with convenient value casting
@@ -174,8 +174,10 @@ namespace boost { namespace program_options {
bool utf8);
/** Names of required options, filled by parser which has
access to options_description. */
std::set<std::string> m_required;
access to options_description.
The map values are the "canonical" names for each corresponding option.
This is useful in creating diagnostic messages when the option is absent. */
std::map<std::string, std::string> m_required;
};

View File

@@ -34,64 +34,43 @@ namespace boost { namespace program_options {
using namespace std;
using namespace boost::program_options::command_line_style;
invalid_syntax::
invalid_syntax(const string& tokens, kind_t kind)
: error(error_message(kind).append(" in '").append(tokens).append("'"))
, m_tokens(tokens)
, m_kind(kind)
{}
string
invalid_syntax::error_message(kind_t kind)
invalid_syntax::get_template(kind_t kind)
{
// Initially, store the message in 'const char*' variable,
// to avoid conversion to string in all cases.
const char* msg;
switch(kind)
{
case long_not_allowed:
msg = "long options are not allowed";
break;
case long_adjacent_not_allowed:
msg = "parameters adjacent to long options not allowed";
break;
case short_adjacent_not_allowed:
msg = "parameters adjust to short options are not allowed";
break;
case empty_adjacent_parameter:
msg = "adjacent parameter is empty";
msg = "the argument for option '%canonical_option%' should follow immediately after the equal sign";
break;
case missing_parameter:
msg = "required parameter is missing";
break;
case extra_parameter:
msg = "extra parameter";
msg = "the required argument for option '%canonical_option%' is missing";
break;
case unrecognized_line:
msg = "unrecognized line";
msg = "the options configuration file contains an invalid line '%invalid_line%'";
break;
// none of the following are currently used:
case long_not_allowed:
msg = "the unabbreviated option '%canonical_option%' is not valid";
break;
case long_adjacent_not_allowed:
msg = "the unabbreviated option '%canonical_option%' does not take any arguments";
break;
case short_adjacent_not_allowed:
msg = "the abbreviated option '%canonical_option%' does not take any arguments";
break;
case extra_parameter:
msg = "option '%canonical_option%' does not take any arguments";
break;
default:
msg = "unknown error";
msg = "unknown command line syntax error for '%s'";
}
return msg;
}
invalid_syntax::kind_t
invalid_syntax::kind() const
{
return m_kind;
}
const string&
invalid_syntax::tokens() const
{
return m_tokens;
}
invalid_command_line_syntax::
invalid_command_line_syntax(const string& tokens, kind_t kind)
: invalid_syntax(tokens, kind)
{}
}}
@@ -156,15 +135,26 @@ namespace boost { namespace program_options { namespace detail {
const char* error = 0;
if (allow_some_long &&
!(style & long_allow_adjacent) && !(style & long_allow_next))
error = "style disallows parameters for long options";
error = "boost::program_options misconfiguration: "
"choose one or other of 'command_line_style::long_allow_next' "
"(whitespace separated arguments) or "
"'command_line_style::long_allow_adjacent' ('=' separated arguments) for "
"long options.";
if (!error && (style & allow_short) &&
!(style & short_allow_adjacent) && !(style & short_allow_next))
error = "style disallows parameters for short options";
error = "boost::program_options misconfiguration: "
"choose one or other of 'command_line_style::short_allow_next' "
"(whitespace separated arguments) or "
"'command_line_style::short_allow_adjacent' ('=' separated arguments) for "
"short options.";
if (!error && (style & allow_short) &&
!(style & allow_dash_for_short) && !(style & allow_slash_for_short))
error = "style disallows all characters for short options";
error = "boost::program_options misconfiguration: "
"choose one or other of 'command_line_style::allow_slash_for_short' "
"(slashes) or 'command_line_style::allow_dash_for_short' (dashes) for "
"short options.";
if (error)
boost::throw_exception(invalid_command_line_style(error));
@@ -192,6 +182,23 @@ namespace boost { namespace program_options { namespace detail {
m_positional = &positional;
}
int
cmdline::get_canonical_option_prefix()
{
if (m_style & allow_long)
return allow_long;
if (m_style & allow_long_disguise)
return allow_long_disguise;
if ((m_style & allow_short) && (m_style & allow_dash_for_short))
return allow_dash_for_short;
if ((m_style & allow_short) && (m_style & allow_slash_for_short))
return allow_slash_for_short;
return 0;
}
vector<option>
cmdline::run()
@@ -242,7 +249,7 @@ namespace boost { namespace program_options { namespace detail {
bool ok = false;
for(unsigned i = 0; i < style_parsers.size(); ++i)
{
unsigned current_size = args.size();
unsigned current_size = static_cast<unsigned>(args.size());
vector<option> next = style_parsers[i](args);
// Check that option names
@@ -288,11 +295,21 @@ namespace boost { namespace program_options { namespace detail {
if (opt.string_key.empty())
continue;
const option_description* xd =
m_desc->find_nothrow(opt.string_key,
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
const option_description* xd;
try
{
xd = m_desc->find_nothrow(opt.string_key,
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
}
catch(error_with_option_name& e)
{
// add context and rethrow
e.add_context(opt.string_key, opt.original_tokens[0], get_canonical_option_prefix());
throw;
}
if (!xd)
continue;
@@ -304,7 +321,7 @@ namespace boost { namespace program_options { namespace detail {
// We only allow to grab tokens that are not already
// recognized as key options.
int can_take_more = max_tokens - opt.value.size();
int can_take_more = max_tokens - static_cast<int>(opt.value.size());
unsigned j = i+1;
for (; can_take_more && j < result.size(); --can_take_more, ++j)
{
@@ -383,92 +400,112 @@ namespace boost { namespace program_options { namespace detail {
if (opt.string_key.empty())
return;
// First check that the option is valid, and get its description.
const option_description* xd = m_desc->find_nothrow(opt.string_key,
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
//
// Be defensive:
// will have no original token if option created by handle_additional_parser()
std::string original_token_for_exceptions = opt.string_key;
if (opt.original_tokens.size())
original_token_for_exceptions = opt.original_tokens[0];
if (!xd)
try
{
if (m_allow_unregistered) {
opt.unregistered = true;
return;
} else {
boost::throw_exception(unknown_option(opt.string_key));
}
}
const option_description& d = *xd;
// First check that the option is valid, and get its description.
const option_description* xd = m_desc->find_nothrow(opt.string_key,
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
// Canonize the name
opt.string_key = d.key(opt.string_key);
// We check that the min/max number of tokens for the option
// agrees with the number of tokens we have. The 'adjacent_value'
// (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();
unsigned present_tokens = opt.value.size() + other_tokens.size();
if (present_tokens >= min_tokens)
{
if (!opt.value.empty() && max_tokens == 0)
if (!xd)
{
boost::throw_exception(invalid_command_line_syntax(opt.string_key,
invalid_command_line_syntax::extra_parameter));
if (m_allow_unregistered) {
opt.unregistered = true;
return;
} else {
boost::throw_exception(unknown_option());
}
}
const option_description& d = *xd;
// Canonize the name
opt.string_key = d.key(opt.string_key);
// We check that the min/max number of tokens for the option
// agrees with the number of tokens we have. The 'adjacent_value'
// (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();
// 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
if (opt.value.size() <= min_tokens)
unsigned present_tokens = static_cast<unsigned>(opt.value.size() + other_tokens.size());
if (present_tokens >= min_tokens)
{
min_tokens -= opt.value.size();
if (!opt.value.empty() && max_tokens == 0)
{
boost::throw_exception(
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
if (opt.value.size() <= min_tokens)
{
min_tokens -= static_cast<unsigned>(opt.value.size());
}
else
{
min_tokens = 0;
}
// Everything's OK, move the values to the result.
for(;!other_tokens.empty() && min_tokens--; )
{
// check if extra parameter looks like a known option
// we use style parsers to check if it is syntactically an option,
// additionally we check if an option_description exists
vector<option> followed_option;
vector<string> next_token(1, other_tokens[0]);
for (unsigned i = 0; followed_option.empty() && i < style_parsers.size(); ++i)
{
followed_option = style_parsers[i](next_token);
}
if (!followed_option.empty())
{
original_token_for_exceptions = other_tokens[0];
const option_description* od = m_desc->find_nothrow(other_tokens[0],
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
if (od)
boost::throw_exception(
invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
}
opt.value.push_back(other_tokens[0]);
opt.original_tokens.push_back(other_tokens[0]);
other_tokens.erase(other_tokens.begin());
}
}
else
{
min_tokens = 0;
}
boost::throw_exception(
invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
// Everything's OK, move the values to the result.
for(;!other_tokens.empty() && min_tokens--; )
{
// check if extra parameter looks like a known option
// we use style parsers to check if it is syntactically an option,
// additionally we check if an option_description exists
vector<option> followed_option;
vector<string> next_token(1, other_tokens[0]);
for (unsigned i = 0; followed_option.empty() && i < style_parsers.size(); ++i)
{
followed_option = style_parsers[i](next_token);
}
if (!followed_option.empty())
{
const option_description* od = m_desc->find_nothrow(other_tokens[0],
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
if (od)
boost::throw_exception(invalid_command_line_syntax(opt.string_key,
invalid_command_line_syntax::missing_parameter));
}
opt.value.push_back(other_tokens[0]);
opt.original_tokens.push_back(other_tokens[0]);
other_tokens.erase(other_tokens.begin());
}
}
else
}
// use only original token for unknown_option / ambiguous_option since by definition
// they are unrecognised / unparsable
catch(error_with_option_name& e)
{
boost::throw_exception(invalid_command_line_syntax(opt.string_key,
invalid_command_line_syntax::missing_parameter));
// add context and rethrow
e.add_context(opt.string_key, original_token_for_exceptions, get_canonical_option_prefix());
throw;
}
}
vector<option>
@@ -486,8 +523,11 @@ namespace boost { namespace program_options { namespace detail {
name = tok.substr(2, p-2);
adjacent = tok.substr(p+1);
if (adjacent.empty())
boost::throw_exception( invalid_command_line_syntax(name,
invalid_command_line_syntax::empty_adjacent_parameter) );
boost::throw_exception( invalid_command_line_syntax(
invalid_command_line_syntax::empty_adjacent_parameter,
name,
name,
get_canonical_option_prefix()) );
}
else
{
@@ -523,9 +563,20 @@ namespace boost { namespace program_options { namespace detail {
// of token is considered to be value, not further grouped
// option.
for(;;) {
const option_description* d
= m_desc->find_nothrow(name, false, false,
is_style_active(short_case_insensitive));
const option_description* d;
try
{
d = m_desc->find_nothrow(name, false, false,
is_style_active(short_case_insensitive));
}
catch(error_with_option_name& e)
{
// add context and rethrow
e.add_context(name, name, get_canonical_option_prefix());
throw;
}
// FIXME: check for 'allow_sticky'.
if (d && (m_style & allow_sticky) &&
@@ -589,15 +640,24 @@ namespace boost { namespace program_options { namespace detail {
((tok[0] == '-' && tok[1] != '-') ||
((m_style & allow_slash_for_short) && tok[0] == '/')))
{
if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive)))
try
{
args[0].insert(0, "-");
if (args[0][1] == '/')
args[0][1] = '-';
return parse_long_option(args);
if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive)))
{
args[0].insert(0, "-");
if (args[0][1] == '/')
args[0][1] = '-';
return parse_long_option(args);
}
}
catch(error_with_option_name& e)
{
// add context and rethrow
e.add_context(tok, tok, get_canonical_option_prefix());
throw;
}
}
return vector<option>();

View File

@@ -57,7 +57,9 @@ namespace boost { namespace program_options { namespace detail {
bad_prefixes = true;
}
if (bad_prefixes)
boost::throw_exception(error("bad prefixes"));
boost::throw_exception(error("options '" + string(name) + "' and '" +
*i + "*' will both match the same "
"arguments from the configuration file"));
allowed_prefixes.insert(s);
}
}
@@ -117,7 +119,7 @@ namespace boost { namespace program_options { namespace detail {
break;
} else {
boost::throw_exception(invalid_syntax(s, invalid_syntax::unrecognized_line));
boost::throw_exception(invalid_config_file_syntax(s, invalid_syntax::unrecognized_line));
}
}
}

View File

@@ -137,6 +137,31 @@ namespace boost { namespace program_options {
return m_short_name;
}
std::string
option_description::canonical_display_name(int prefix_style) const
{
if (!m_long_name.empty())
{
if (prefix_style == command_line_style::allow_long)
return "--" + m_long_name;
if (prefix_style == command_line_style::allow_long_disguise)
return "-" + m_long_name;
}
// sanity check: m_short_name[0] should be '-' or '/'
if (m_short_name.length() == 2)
{
if (prefix_style == command_line_style::allow_slash_for_short)
return string("/") + m_short_name[1];
if (prefix_style == command_line_style::allow_dash_for_short)
return string("-") + m_short_name[1];
}
if (!m_long_name.empty())
return m_long_name;
else
return m_short_name;
}
const std::string&
option_description::long_name() const
{
@@ -292,7 +317,7 @@ namespace boost { namespace program_options {
const option_description* d = find_nothrow(name, approx,
long_ignore_case, short_ignore_case);
if (!d)
boost::throw_exception(unknown_option(name));
boost::throw_exception(unknown_option());
return *d;
}
@@ -340,8 +365,7 @@ namespace boost { namespace program_options {
}
}
if (full_matches.size() > 1)
boost::throw_exception(
ambiguous_option(name, full_matches));
boost::throw_exception(ambiguous_option(full_matches));
// If we have a full match, and an approximate match,
// ignore approximate match instead of reporting error.
@@ -349,8 +373,7 @@ namespace boost { namespace program_options {
// "--all" on the command line should select the first one,
// without ambiguity.
if (full_matches.empty() && approximate_matches.size() > 1)
boost::throw_exception(
ambiguous_option(name, approximate_matches));
boost::throw_exception(ambiguous_option(approximate_matches));
return found.get();
}
@@ -399,7 +422,7 @@ namespace boost { namespace program_options {
if (count(par.begin(), par.end(), '\t') > 1)
{
boost::throw_exception(program_options::error(
"Only one tab per paragraph is allowed"));
"Only one tab per paragraph is allowed in the options description"));
}
// erase tab from string
@@ -446,7 +469,7 @@ namespace boost { namespace program_options {
// Take care to never increment the iterator past
// the end, since MSVC 8.0 (brokenly), assumes that
// doing that, even if no access happens, is a bug.
unsigned remaining = distance(line_begin, par_end);
unsigned remaining = static_cast<unsigned>(std::distance(line_begin, par_end));
string::const_iterator line_end = line_begin +
((remaining < line_length) ? remaining : line_length);
@@ -466,7 +489,7 @@ namespace boost { namespace program_options {
{
// is last_space within the second half ot the
// current line
if (static_cast<unsigned>(distance(last_space, line_end)) <
if (static_cast<unsigned>(std::distance(last_space, line_end)) <
(line_length / 2))
{
line_end = last_space;
@@ -479,8 +502,8 @@ namespace boost { namespace program_options {
if (first_line)
{
indent += par_indent;
line_length -= par_indent; // there's less to work with now
indent += static_cast<unsigned>(par_indent);
line_length -= static_cast<unsigned>(par_indent); // there's less to work with now
first_line = false;
}
@@ -569,7 +592,7 @@ namespace boost { namespace program_options {
os.put(' ');
}
} else {
for(unsigned pad = first_column_width - ss.str().size(); pad > 0; --pad)
for(unsigned pad = first_column_width - static_cast<unsigned>(ss.str().size()); pad > 0; --pad)
{
os.put(' ');
}
@@ -581,12 +604,9 @@ namespace boost { namespace program_options {
}
}
void
options_description::print(std::ostream& os) const
unsigned
options_description::get_option_column_width() const
{
if (!m_caption.empty())
os << m_caption << ":\n";
/* Find the maximum width of the option column */
unsigned width(23);
unsigned i; // vc6 has broken for loop scoping
@@ -597,6 +617,11 @@ namespace boost { namespace program_options {
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
width = (max)(width, static_cast<unsigned>(ss.str().size()));
}
/* Get width of groups as well*/
for (unsigned j = 0; j < groups.size(); ++j)
width = max(width, groups[j]->get_option_column_width());
/* this is the column were description should start, if first
column is longer, we go to a new line */
const unsigned start_of_description_column = m_line_length - m_min_description_length;
@@ -605,9 +630,20 @@ namespace boost { namespace program_options {
/* add an additional space to improve readability */
++width;
return width;
}
void
options_description::print(std::ostream& os, unsigned width) const
{
if (!m_caption.empty())
os << m_caption << ":\n";
if (!width)
width = get_option_column_width();
/* The options formatting style is stolen from Subversion. */
for (i = 0; i < m_options.size(); ++i)
for (unsigned i = 0; i < m_options.size(); ++i)
{
if (belong_to_group[i])
continue;
@@ -620,7 +656,8 @@ namespace boost { namespace program_options {
}
for (unsigned j = 0; j < groups.size(); ++j) {
os << "\n" << *groups[j];
os << "\n";
groups[j]->print(os, width);
}
}

View File

@@ -88,7 +88,8 @@ namespace boost { namespace program_options {
basic_parsed_options<wchar_t>
::basic_parsed_options(const parsed_options& po)
: description(po.description),
utf8_encoded_options(po)
utf8_encoded_options(po),
m_options_prefix(po.m_options_prefix)
{
for (unsigned i = 0; i < po.options.size(); ++i)
options.push_back(woption_from_option(po.options[i]));
@@ -110,7 +111,7 @@ namespace boost { namespace program_options {
if (d.long_name().empty())
boost::throw_exception(
error("long name required for config file"));
error("abbreviated option names are not permitted in options configuration files"));
allowed_options.insert(d.long_name());
}

View File

@@ -34,7 +34,7 @@ namespace boost { namespace program_options {
positional_options_description::max_total_count() const
{
return m_trailing.empty() ?
m_names.size() : (std::numeric_limits<unsigned>::max)();
static_cast<unsigned>(m_names.size()) : (std::numeric_limits<unsigned>::max)();
}
const std::string&

View File

@@ -12,7 +12,7 @@
#define BOOST_UTF8_END_NAMESPACE }}}
#define BOOST_UTF8_DECL BOOST_PROGRAM_OPTIONS_DECL
#include "../../detail/utf8_codecvt_facet.cpp"
#include <boost/detail/utf8_codecvt_facet.ipp>
#undef BOOST_UTF8_BEGIN_NAMESPACE

View File

@@ -7,6 +7,8 @@
#include <boost/program_options/config.hpp>
#include <boost/program_options/value_semantic.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <boost/program_options/detail/cmdline.hpp>
#include <set>
#include <cctype>
@@ -14,6 +16,22 @@ namespace boost { namespace program_options {
using namespace std;
#ifndef BOOST_NO_STD_WSTRING
namespace
{
std::string convert_value(const std::wstring& s)
{
try {
return to_local_8_bit(s);
}
catch(const std::exception&) {
return "<unrepresentable unicode string>";
}
}
}
#endif
void
value_semantic_codecvt_helper<char>::
parse(boost::any& value_store,
@@ -139,7 +157,7 @@ namespace boost { namespace program_options {
else if (s == "off" || s == "no" || s == "0" || s == "false")
v = any(false);
else
boost::throw_exception(validation_error(validation_error::invalid_bool_value, s));
boost::throw_exception(invalid_bool_value(s));
}
// This is blatant copy-paste. However, templating this will cause a problem,
@@ -161,7 +179,7 @@ namespace boost { namespace program_options {
else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
v = any(false);
else
boost::throw_exception(validation_error(validation_error::invalid_bool_value));
boost::throw_exception(invalid_bool_value(convert_value(s)));
}
#endif
BOOST_PROGRAM_OPTIONS_DECL
@@ -194,120 +212,212 @@ namespace boost { namespace program_options {
invalid_option_value::
invalid_option_value(const std::string& bad_value)
: validation_error(validation_error::invalid_option_value, bad_value)
{}
: validation_error(validation_error::invalid_option_value)
{
set_substitute("value", bad_value);
}
#ifndef BOOST_NO_STD_WSTRING
namespace
invalid_option_value::
invalid_option_value(const std::wstring& bad_value)
: validation_error(validation_error::invalid_option_value)
{
std::string convert_value(const std::wstring& s)
set_substitute("value", convert_value(bad_value));
}
#endif
invalid_bool_value::
invalid_bool_value(const std::string& bad_value)
: validation_error(validation_error::invalid_bool_value)
{
set_substitute("value", bad_value);
}
error_with_option_name::error_with_option_name( const std::string& template_,
const std::string& option_name,
const std::string& original_token,
int option_style) :
error(template_),
m_option_style(option_style),
m_error_template(template_)
{
// parameter | placeholder | value
// --------- | ----------- | -----
set_substitute_default("canonical_option", "option '%canonical_option%'", "option");
set_substitute_default("value", "argument ('%value%')", "argument");
set_substitute_default("prefix", "%prefix%", "");
m_substitutions["option"] = option_name;
m_substitutions["original_token"] = original_token;
}
const char* error_with_option_name::what() const throw()
{
// will substitute tokens each time what is run()
substitute_placeholders(m_error_template);
return m_message.c_str();
}
void error_with_option_name::replace_token(const string& from, const string& to) const
{
while (1)
{
try {
return to_local_8_bit(s);
}
catch(const std::exception&) {
return "<unrepresentable unicode string>";
}
std::size_t pos = m_message.find(from.c_str(), 0, from.length());
// not found: all replaced
if (pos == std::string::npos)
return;
m_message.replace(pos, from.length(), to);
}
}
invalid_option_value::
invalid_option_value(const std::wstring& bad_value)
: validation_error(validation_error::invalid_option_value, convert_value(bad_value))
{}
#endif
const std::string&
unknown_option::get_option_name() const throw()
{
return m_option_name;
string error_with_option_name::get_canonical_option_prefix() const
{
switch (m_option_style)
{
case command_line_style::allow_dash_for_short:
return "-";
case command_line_style::allow_slash_for_short:
return "/";
case command_line_style::allow_long_disguise:
return "-";
case command_line_style::allow_long:
return "--";
case 0:
return "";
}
throw std::logic_error("error_with_option_name::m_option_style can only be "
"one of [0, allow_dash_for_short, allow_slash_for_short, "
"allow_long_disguise or allow_long]");
}
const std::string&
ambiguous_option::get_option_name() const throw()
{
return m_option_name;
}
const std::vector<std::string>&
ambiguous_option::alternatives() const throw()
string error_with_option_name::get_canonical_option_name() const
{
return m_alternatives;
if (!m_substitutions.find("option")->second.length())
return m_substitutions.find("original_token")->second;
string original_token = strip_prefixes(m_substitutions.find("original_token")->second);
string option_name = strip_prefixes(m_substitutions.find("option")->second);
// For long options, use option name
if (m_option_style == command_line_style::allow_long ||
m_option_style == command_line_style::allow_long_disguise)
return get_canonical_option_prefix() + option_name;
// For short options use first letter of original_token
if (m_option_style && original_token.length())
return get_canonical_option_prefix() + original_token[0];
// no prefix
return option_name;
}
void
multiple_values::set_option_name(const std::string& option_name)
void error_with_option_name::substitute_placeholders(const string& error_template) const
{
m_option_name = option_name;
m_message = error_template;
std::map<std::string, std::string> substitutions(m_substitutions);
substitutions["canonical_option"] = get_canonical_option_name();
substitutions["prefix"] = get_canonical_option_prefix();
//
// replace placeholder with defaults if values are missing
//
for (map<string, string_pair>::const_iterator iter = m_substitution_defaults.begin();
iter != m_substitution_defaults.end(); ++iter)
{
// missing parameter: use default
if (substitutions.count(iter->first) == 0 ||
substitutions[iter->first].length() == 0)
replace_token(iter->second.first, iter->second.second);
}
//
// replace placeholder with values
// placeholder are denoted by surrounding '%'
//
for (map<string, string>::iterator iter = substitutions.begin();
iter != substitutions.end(); ++iter)
replace_token('%' + iter->first + '%', iter->second);
}
const std::string&
multiple_values::get_option_name() const throw()
void ambiguous_option::substitute_placeholders(const string& original_error_template) const
{
return m_option_name;
}
void
multiple_occurrences::set_option_name(const std::string& option_name)
{
m_option_name = option_name;
// For short forms, all alternatives must be identical, by
// definition, to the specified option, so we don't need to
// display alternatives
if (m_option_style == command_line_style::allow_dash_for_short ||
m_option_style == command_line_style::allow_slash_for_short)
{
error_with_option_name::substitute_placeholders(original_error_template);
return;
}
string error_template = original_error_template;
// remove duplicates using std::set
std::set<std::string> alternatives_set (m_alternatives.begin(), m_alternatives.end());
std::vector<std::string> alternatives_vec (alternatives_set.begin(), alternatives_set.end());
error_template += " and matches ";
// Being very cautious: should be > 1 alternative!
if (alternatives_vec.size() > 1)
{
for (unsigned i = 0; i < alternatives_vec.size() - 1; ++i)
error_template += "'%prefix%" + alternatives_vec[i] + "', ";
error_template += "and ";
}
// there is a programming error if multiple options have the same name...
if (m_alternatives.size() > 1 && alternatives_vec.size() == 1)
error_template += "different versions of ";
error_template += "'%prefix%" + alternatives_vec.back() + "'";
// use inherited logic
error_with_option_name::substitute_placeholders(error_template);
}
const std::string&
multiple_occurrences::get_option_name() const throw()
{
return m_option_name;
}
validation_error::
validation_error(kind_t kind,
const std::string& option_value,
const std::string& option_name)
: error("")
, m_kind(kind)
, m_option_name(option_name)
, m_option_value(option_value)
, m_message(error_message(kind))
{
if (!option_value.empty())
{
m_message.append(std::string("'") + option_value + std::string("'"));
}
}
void
validation_error::set_option_name(const std::string& option_name)
{
m_option_name = option_name;
}
const std::string&
validation_error::get_option_name() const throw()
{
return m_option_name;
}
std::string
validation_error::error_message(kind_t kind)
string
validation_error::get_template(kind_t kind)
{
// Initially, store the message in 'const char*' variable,
// to avoid conversion to std::string in all cases.
const char* msg;
switch(kind)
{
case multiple_values_not_allowed:
msg = "multiple values not allowed";
break;
case at_least_one_value_required:
msg = "at least one value required";
break;
case invalid_bool_value:
msg = "invalid bool value";
msg = "the argument ('%value%') for option '%canonical_option%' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'";
break;
case invalid_option_value:
msg = "invalid option value";
msg = "the argument ('%value%') for option '%canonical_option%' is invalid";
break;
case multiple_values_not_allowed:
msg = "option '%canonical_option%' only takes a single argument";
break;
case at_least_one_value_required:
msg = "option '%canonical_option%' requires at least one argument";
break;
// currently unused
case invalid_option:
msg = "invalid option";
msg = "option '%canonical_option%' is not valid";
break;
default:
msg = "unknown error";
@@ -315,21 +425,4 @@ namespace boost { namespace program_options {
return msg;
}
const char*
validation_error::what() const throw()
{
if (!m_option_name.empty())
{
m_message = "in option '" + m_option_name + "': "
+ error_message(m_kind);
}
return m_message.c_str();
}
const std::string&
required_option::get_option_name() const throw()
{
return m_option_name;
}
}}

View File

@@ -38,65 +38,68 @@ namespace boost { namespace program_options {
// Declared once, to please Intel in VC++ mode;
unsigned i;
// First, convert/store all given options
for (i = 0; i < options.options.size(); ++i) {
// Declared here so can be used to provide context for exceptions
string option_name;
string original_token;
const string& name = options.options[i].string_key;
// Skip positional options without name
if (name.empty())
continue;
try
{
// Ignore unregistered option. The 'unregistered'
// field can be true only if user has explicitly asked
// to allow unregistered options. We can't store them
// to variables map (lacking any information about paring),
// so just ignore them.
if (options.options[i].unregistered)
continue;
// First, convert/store all given options
for (i = 0; i < options.options.size(); ++i) {
// If option has final value, skip this assignment
if (xm.m_final.count(name))
continue;
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;
const option_description& d = desc.find(name, false,
false, false);
// Ignore unregistered option. The 'unregistered'
// field can be true only if user has explicitly asked
// to allow unregistered options. We can't store them
// to variables map (lacking any information about paring),
// so just ignore them.
if (options.options[i].unregistered)
continue;
variable_value& v = m[name];
if (v.defaulted()) {
// Explicit assignment here erases defaulted value
v = variable_value();
}
try {
// If option has final value, skip this assignment
if (xm.m_final.count(option_name))
continue;
string 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);
variable_value& v = m[option_name];
if (v.defaulted()) {
// Explicit assignment here erases defaulted value
v = variable_value();
}
d.semantic()->parse(v.value(), options.options[i].value, utf8);
v.m_value_semantic = d.semantic();
// The option is not composing, and the value is explicitly
// provided. Ignore values of this option for subsequent
// calls to 'store'. We store this to a temporary set,
// so that several assignment inside *this* 'store' call
// are allowed.
if (!d.semantic()->is_composing())
new_final.insert(option_name);
}
}
#ifndef BOOST_NO_EXCEPTIONS
catch(validation_error& e)
{
e.set_option_name(name);
throw;
}
catch(multiple_occurrences& e)
{
e.set_option_name(name);
throw;
}
catch(multiple_values& e)
{
e.set_option_name(name);
throw;
}
#endif
v.m_value_semantic = d.semantic();
// The option is not composing, and the value is explicitly
// provided. Ignore values of this option for subsequent
// calls to 'store'. We store this to a temporary set,
// so that several assignment inside *this* 'store' call
// are allowed.
if (!d.semantic()->is_composing())
new_final.insert(name);
catch(error_with_option_name& e)
{
// add context and rethrow
e.add_context(option_name, original_token, options.m_options_prefix);
throw;
}
#endif
xm.m_final.insert(new_final.begin(), new_final.end());
@@ -127,7 +130,14 @@ namespace boost { namespace program_options {
// add empty value if this is an required option
if (d.semantic()->is_required()) {
xm.m_required.insert(key);
// For option names specified in multiple ways, e.g. on the command line,
// config file etc, the following precedence rules apply:
// "--" > ("-" or "/") > ""
// Precedence is set conveniently by a single call to length()
string canonical_name = d.canonical_display_name(options.m_options_prefix);
if (canonical_name.length() > xm.m_required[key].length())
xm.m_required[key] = canonical_name;
}
}
}
@@ -204,15 +214,16 @@ namespace boost { namespace program_options {
variables_map::notify()
{
// This checks if all required options occur
for (set<string>::const_iterator r = m_required.begin();
for (map<string, string>::const_iterator r = m_required.begin();
r != m_required.end();
++r)
{
const string& opt = *r;
const string& opt = r->first;
const string& display_opt = r->second;
map<string, variable_value>::const_iterator iter = find(opt);
if (iter == end() || iter->second.empty())
{
boost::throw_exception(required_option(opt));
boost::throw_exception(required_option(display_opt));
}
}

View File

@@ -7,6 +7,8 @@
#include <boost/program_options/parsers.hpp>
#include <cctype>
using std::size_t;
#ifdef _WIN32
namespace boost { namespace program_options {
@@ -89,7 +91,7 @@ namespace boost { namespace program_options {
{
std::vector<std::wstring> result;
std::vector<std::string> aux = split_winmain(to_internal(cmdline));
for (unsigned i = 0, e = aux.size(); i < e; ++i)
for (size_t i = 0, e = aux.size(); i < e; ++i)
result.push_back(from_utf8(aux[i]));
return result;
}

View File

@@ -32,6 +32,7 @@ test-suite program_options :
[ po-test split_test.cpp ]
[ po-test unrecognized_test.cpp ]
[ po-test required_test.cpp : required_test.cfg ]
[ po-test exception_txt_test.cpp ]
;
exe test_convert : test_convert.cpp ;

View File

@@ -63,7 +63,7 @@ void test_unknown_option()
catch (unknown_option& e)
{
BOOST_CHECK_EQUAL(e.get_option_name(), "-f");
BOOST_CHECK_EQUAL(string(e.what()), "unknown option -f");
BOOST_CHECK_EQUAL(string(e.what()), "unrecognised option '-f'");
}
}
@@ -94,8 +94,8 @@ void test_multiple_values()
// because: untyped_value always has one value and this is filtered before reach specific
// validation and parsing
//
BOOST_CHECK_EQUAL(e.get_option_name(), "cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "in option 'cfgfile': multiple values not allowed");
BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' only takes a single argument");
}
}
@@ -118,8 +118,8 @@ void test_multiple_occurrences()
}
catch (multiple_occurrences& e)
{
BOOST_CHECK_EQUAL(e.get_option_name(), "cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "multiple occurrences");
BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' cannot be specified more than once");
}
}
@@ -145,7 +145,7 @@ void test_missing_value()
catch (invalid_command_line_syntax& e)
{
BOOST_CHECK_EQUAL(e.kind(), invalid_syntax::missing_parameter);
BOOST_CHECK_EQUAL(e.tokens(), "cfgfile");
BOOST_CHECK_EQUAL(e.tokens(), "--cfgfile");
}
}

672
test/exception_txt_test.cpp Normal file
View File

@@ -0,0 +1,672 @@
// Copyright Leo Goodstadt 2012
// 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/parsers.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/cmdline.hpp>
using namespace boost::program_options;
#include <iostream>
#include <sstream>
#include <vector>
#include <cassert>
using namespace std;
#include "minitest.hpp"
//
// like BOOST_CHECK_EQUAL but with more descriptive error message
//
#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError:\n<<" << \
description << ">>\n Expected text=\"" << b << "\"\n Actual text =\"" << a << "\"\n\n"; assert(a == b);}
// Uncomment for Debugging, removes asserts so we can see more failures!
//#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n";
//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
//
// Uncomment to print out the complete set of diagnostic messages for the different test cases
/*
#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError: " << \
description << "\n Expecting\n" << b << "\n Found\n" << a << "\n\n"; } \
else {std::cout << description<< "\t" << b << "\n";}
*/
//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
//
// test exception for each specified command line style, e.g. short dash or config file
//
template<typename EXCEPTION>
void test_each_exception_message(const string& test_description, const vector<const char*>& argv, options_description& desc, int style, string exception_msg, istream& is = cin)
{
if (exception_msg.length() == 0)
return;
variables_map vm;
unsigned argc = argv.size();
try {
if (style == -1)
store(parse_config_file(is, desc), vm);
else
store(parse_command_line(argv.size(), argv.data(), desc, style), vm);
notify(vm);
}
catch (EXCEPTION& e)
{
//cerr << "Correct:\n\t" << e.what() << "\n";
CHECK_EQUAL(test_description, e.what(), exception_msg);
return;
}
catch (std::exception& e)
{
// concatenate argv without boost::algorithm::join
string argv_txt;
for (unsigned ii = 0; ii < argc - 1; ++ii)
argv_txt += argv[ii] + string(" ");
if (argc)
argv_txt += argv[argc - 1];
BOOST_ERROR("\n<<" + test_description +
string(">>\n Unexpected exception type!\n Actual text =\"") + e.what() +
"\"\n argv =\"" + argv_txt +
"\"\n Expected text=\"" + exception_msg + "\"\n");
return;
}
BOOST_ERROR(test_description + ": No exception thrown. ");
}
//
// test exception messages for all command line styles (unix/long/short/slash/config file)
//
// try each command line style in turn
const int unix_style = command_line_style::unix_style;
const int short_dash = command_line_style::allow_dash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent | command_line_style::allow_sticky;
const int short_slash = command_line_style::allow_slash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent;
const int long_dash = command_line_style::allow_long | command_line_style::long_allow_adjacent | command_line_style::allow_guessing;
template<typename EXCEPTION>
void test_exception_message(const vector<vector<const char*> >& argv,
options_description& desc,
const string& error_description,
const char* expected_message_template[5])
{
string expected_message;
// unix
expected_message = expected_message_template[0];
test_each_exception_message<EXCEPTION>(error_description + " -- unix",
argv[0], desc, unix_style, expected_message);
// long dash only
expected_message = expected_message_template[1];
test_each_exception_message<EXCEPTION>(error_description + " -- long_dash",
argv[1], desc, long_dash, expected_message);
// short dash only
expected_message = expected_message_template[2];
test_each_exception_message<EXCEPTION>(error_description + " -- short_dash",
argv[2], desc, short_dash, expected_message);
// short slash only
expected_message = expected_message_template[3];
test_each_exception_message<EXCEPTION>(error_description + " -- short_slash",
argv[3], desc, short_slash, expected_message);
// config file only
expected_message = expected_message_template[4];
if (expected_message.length())
{
istringstream istrm(argv[4][0]);
test_each_exception_message<EXCEPTION>(error_description + " -- config_file",
argv[4], desc, -1, expected_message, istrm);
}
}
#define VEC_STR_PUSH_BACK(vec, c_array) \
vec.push_back(vector<const char*>(c_array, c_array + sizeof(c_array) / sizeof(char*)));
//________________________________________________________________________________________
//
// invalid_option_value
//
//________________________________________________________________________________________
void test_invalid_option_value_exception_msg()
{
options_description desc;
desc.add_options()
("int-option,d", value< int >(), "An option taking an integer")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--int", "A_STRING"}; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "int-option=A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"the argument ('A_STRING') for option '--int-option' is invalid",
"the argument ('A_STRING') for option '--int-option' is invalid",
"the argument ('A_STRING') for option '-d' is invalid",
"the argument ('A_STRING') for option '/d' is invalid",
"the argument ('A_STRING') for option 'int-option' is invalid",
};
test_exception_message<invalid_option_value>(argv, desc, "invalid_option_value",
expected_msg);
}
//________________________________________________________________________________________
//
// missing_value
//
//________________________________________________________________________________________
void test_missing_value_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,e", value<string>(), "the config file")
("output,o", value<string>(), "the output file")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--cfgfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/e", "/e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { ""} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"the required argument for option '--cfgfile' is missing",
"the required argument for option '--cfgfile' is missing",
"the required argument for option '-e' is missing",
"", // Ignore probable bug in cmdline::finish_option
//"the required argument for option '/e' is missing",
"",
};
test_exception_message<invalid_command_line_syntax>(argv, desc,
"invalid_syntax::missing_parameter",
expected_msg);
}
//________________________________________________________________________________________
//
// ambiguous_option
//
//________________________________________________________________________________________
void test_ambiguous_option_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile1,c", value<string>(), "the config file")
("cfgfile2,o", value<string>(), "the config file")
("good,g", "good option")
("output,c", value<string>(), "the output file")
("output", value<string>(), "the output file")
;
vector<vector<const char*> > argv;
const char* argv0[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = {"program", "--cfgfile", "file", "--cfgfile", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = {"program", "/c", "file", "/o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "output=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"option '-c' is ambiguous and matches '--cfgfile1', and '--output'",
"option '--cfgfile' is ambiguous and matches '--cfgfile1', and '--cfgfile2'",
"option '-c' is ambiguous",
"option '/c' is ambiguous",
"option 'output' is ambiguous and matches different versions of 'output'",
};
test_exception_message<ambiguous_option>(argv, desc, "ambiguous_option",
expected_msg);
}
//________________________________________________________________________________________
//
// multiple_occurrences
//
//________________________________________________________________________________________
void test_multiple_occurrences_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>(), "the configfile")
;
vector<vector<const char*> > argv;
const char* argv0[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = {"program", "--cfgfi", "file", "--cfgfi", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = {"program", "/c", "file", "/c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "cfgfile=output.txt\ncfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"option '--cfgfile' cannot be specified more than once",
"option '--cfgfile' cannot be specified more than once",
"option '-c' cannot be specified more than once",
"option '/c' cannot be specified more than once",
"option 'cfgfile' cannot be specified more than once",
};
test_exception_message<multiple_occurrences>(argv, desc, "multiple_occurrences",
expected_msg);
}
//________________________________________________________________________________________
//
// unknown_option
//
//________________________________________________________________________________________
void test_unknown_option_exception_msg()
{
options_description desc;
desc.add_options()
("good,g", "good option")
;
vector<vector<const char*> > argv;
const char* argv0[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = {"program", "--cfgfile", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = {"program", "/c", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "cfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"unrecognised option '-ggc'",
"unrecognised option '--cfgfile'",
"unrecognised option '-ggc'",
"unrecognised option '/c'",
"unrecognised option 'cfgfile'",
};
test_exception_message<unknown_option>(argv, desc, "unknown_option", expected_msg);
}
//________________________________________________________________________________________
//
// validation_error::invalid_bool_value
//
//________________________________________________________________________________________
void test_invalid_bool_value_exception_msg()
{
options_description desc;
desc.add_options()
("bool_option,b", value< bool>(), "bool_option")
;
vector<vector<const char*> > argv;
const char* argv0[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = {"program", "--bool_optio", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = {"program", "/b", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "bool_option=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
"the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
"the argument ('file') for option '-b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
"the argument ('file') for option '/b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
"the argument ('output.txt') for option 'bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
};
test_exception_message<validation_error>(argv,
desc,
"validation_error::invalid_bool_value",
expected_msg);
}
//________________________________________________________________________________________
//
// validation_error::multiple_values_not_allowed
//
//________________________________________________________________________________________
//
// Strange exception: sole purpose seems to be catching multitoken() associated with a scalar
// validation_error::multiple_values_not_allowed seems thus to be a programmer error
//
//
void test_multiple_values_not_allowed_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->multitoken(), "the config file")
("good,g", "good option")
("output,o", value<string>(), "the output file")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--cfgfil", "file", "c", "--outpu", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/c", "file", "c", "/o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"option '--cfgfile' only takes a single argument",
"option '--cfgfile' only takes a single argument",
"option '-c' only takes a single argument",
"option '/c' only takes a single argument",
"",
};
test_exception_message<validation_error>(argv,
desc,
"validation_error::multiple_values_not_allowed",
expected_msg);
}
//________________________________________________________________________________________
//
// validation_error::at_least_one_value_required
//
//________________________________________________________________________________________
//
// Strange exception: sole purpose seems to be catching zero_tokens() associated with a scalar
// validation_error::multiple_values_not_allowed seems thus to be a programmer error
//
//
void test_at_least_one_value_required_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<int>()->zero_tokens(), "the config file")
("other,o", value<string>(), "other")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-c" } ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--cfg", "--o", "name" } ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-c" , "-o" , "name" } ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/c" } ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"option '--cfgfile' requires at least one argument",
"option '--cfgfile' requires at least one argument",
"option '-c' requires at least one argument",
"option '/c' requires at least one argument",
"",
};
test_exception_message<validation_error>(argv,
desc,
"validation_error::at_least_one_value_required",
expected_msg);
}
//________________________________________________________________________________________
//
// required_option
//
//________________________________________________________________________________________
void test_required_option_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->required(), "the config file")
("good,g", "good option")
("output,o", value<string>()->required(), "the output file")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-g" } ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--g" } ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-g"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/g"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"the option '--cfgfile' is required but missing",
"the option '--cfgfile' is required but missing",
"the option '-c' is required but missing",
"the option '/c' is required but missing",
"the option 'cfgfile' is required but missing",
};
test_exception_message<required_option>(argv,
desc,
"required_option",
expected_msg);
}
/**
* Check if this is the expected exception with the right message is being thrown inside
* func
*/
template <typename EXCEPTION, typename FUNC>
void test_exception(const string& test_name, const string& exception_txt, FUNC func)
{
try {
options_description desc;
variables_map vm;
func(desc, vm);
}
catch (EXCEPTION& e)
{
CHECK_EQUAL(test_name, e.what(), exception_txt);
return;
}
catch (std::exception& e)
{
BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
"\nExpected text:\n" + exception_txt + "\n\n");
return;
}
BOOST_ERROR(test_name + ": No exception thrown. ");
}
//________________________________________________________________________________________
//
// check_reading_file
//
//________________________________________________________________________________________
void check_reading_file(options_description& desc, variables_map& vm)
{
desc.add_options()
("output,o", value<string>(), "the output file");
const char* file_name = "no_such_file";
store(parse_config_file<char>(file_name, desc, true), vm);
}
//________________________________________________________________________________________
//
// config_file_wildcard
//
//________________________________________________________________________________________
void config_file_wildcard(options_description& desc, variables_map& vm)
{
desc.add_options()
("outpu*", value<string>(), "the output file1")
("outp*", value<string>(), "the output file2")
;
istringstream is("output1=whichone\noutput2=whichone\n");
store(parse_config_file(is, desc), vm);
}
//________________________________________________________________________________________
//
// invalid_syntax::unrecognized_line
//
//________________________________________________________________________________________
void unrecognized_line(options_description& desc, variables_map& vm)
{
istringstream is("funny wierd line\n");
store(parse_config_file(is, desc), vm);
}
//________________________________________________________________________________________
//
// abbreviated_options_in_config_file
//
//________________________________________________________________________________________
void abbreviated_options_in_config_file(options_description& desc, variables_map& vm)
{
desc.add_options()(",o", value<string>(), "the output file");
istringstream is("o=output.txt\n");
store(parse_config_file(is, desc), vm);
}
//________________________________________________________________________________________
//
// too_many_positional_options
//
//________________________________________________________________________________________
void too_many_positional_options(options_description& desc, variables_map& vm)
{
const char* argv[] = {"program", "1", "2", "3"};
positional_options_description positional_args;
positional_args.add("two_positional_arguments", 2);
store(command_line_parser(4, argv).options(desc).positional(positional_args).run(), vm);
}
//________________________________________________________________________________________
//
// invalid_command_line_style
//
//________________________________________________________________________________________
void test_invalid_command_line_style_exception_msg()
{
string test_name = "invalid_command_line_style";
using namespace command_line_style;
options_description desc;
desc.add_options()("output,o", value<string>(), "the output file");
vector<int> invalid_styles;
invalid_styles.push_back(allow_short | short_allow_adjacent);
invalid_styles.push_back(allow_short | allow_dash_for_short);
invalid_styles.push_back(allow_long);
vector<string> invalid_diagnostics;
invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
"or other of 'command_line_style::allow_slash_for_short' "
"(slashes) or 'command_line_style::allow_dash_for_short' "
"(dashes) for short options.");
invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
"or other of 'command_line_style::short_allow_next' "
"(whitespace separated arguments) or "
"'command_line_style::short_allow_adjacent' ('=' "
"separated arguments) for short options.");
invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
"or other of 'command_line_style::long_allow_next' "
"(whitespace separated arguments) or "
"'command_line_style::long_allow_adjacent' ('=' "
"separated arguments) for long options.");
const char* argv[] = {"program"};
variables_map vm;
for (unsigned ii = 0; ii < 3; ++ii)
{
bool exception_thrown = false;
try
{
store(parse_command_line(1, argv, desc, invalid_styles[ii]), vm);
}
catch (invalid_command_line_style& e)
{
string error_msg("arguments are not allowed for unabbreviated option names");
CHECK_EQUAL(test_name, e.what(), invalid_diagnostics[ii]);
exception_thrown = true;
}
catch (std::exception& e)
{
BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
"\nExpected text:\n" + invalid_diagnostics[ii] + "\n");
exception_thrown = true;
}
if (!exception_thrown)
{
BOOST_ERROR(test_name << ": No exception thrown. ");
}
}
}
int main(int /*ac*/, char** /*av*/)
{
test_ambiguous_option_exception_msg();
test_unknown_option_exception_msg();
test_multiple_occurrences_exception_msg();
test_missing_value_exception_msg();
test_invalid_option_value_exception_msg();
test_invalid_bool_value_exception_msg();
test_multiple_values_not_allowed_exception_msg();
test_required_option_exception_msg();
test_at_least_one_value_required_exception_msg();
string test_name;
string expected_message;
// check_reading_file
test_name = "check_reading_file";
expected_message = "can not read options configuration file 'no_such_file'";
test_exception<reading_file>(test_name, expected_message, check_reading_file);
// config_file_wildcard
test_name = "config_file_wildcard";
expected_message = "options 'outpu*' and 'outp*' will both match the same arguments from the configuration file";
test_exception<error>(test_name, expected_message, config_file_wildcard);
// unrecognized_line
test_name = "unrecognized_line";
expected_message = "the options configuration file contains an invalid line 'funny wierd line'";
test_exception<invalid_syntax>(test_name, expected_message, unrecognized_line);
// abbreviated_options_in_config_file
test_name = "abbreviated_options_in_config_file";
expected_message = "abbreviated option names are not permitted in options configuration files";
test_exception<error>(test_name, expected_message, abbreviated_options_in_config_file);
test_name = "too_many_positional_options";
expected_message = "too many positional options have been specified on the command line";
test_exception<too_many_positional_options_error>(
test_name, expected_message, too_many_positional_options);
test_invalid_command_line_style_exception_msg();
return 0;
}

View File

@@ -228,6 +228,21 @@ void test_default_values()
);
}
void test_value_name()
{
options_description desc("Supported options");
desc.add_options()
("include", value<string>()->value_name("directory"), "Search for headers in 'directory'.")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
"Supported options:\n"
" --include directory Search for headers in 'directory'.\n"
);
}
int main(int, char* [])
{
@@ -238,6 +253,7 @@ int main(int, char* [])
test_long_default_value();
test_word_wrapping();
test_default_values();
test_value_name();
return 0;
}

View File

@@ -34,6 +34,7 @@ void required_throw_test()
notify(vm);
}
catch (required_option& e) {
BOOST_CHECK_EQUAL(e.what(), string("the option '--cfgfile' is required but missing"));
throwed = true;
}
BOOST_CHECK(throwed);