mirror of
https://github.com/boostorg/program_options.git
synced 2026-01-19 04:22:15 +00:00
Import revision 295 of the program_options library.
[SVN r22823]
This commit is contained in:
96
.gitattributes
vendored
Normal file
96
.gitattributes
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
* text=auto !eol svneol=native#text/plain
|
||||
*.gitattributes text svneol=native#text/plain
|
||||
|
||||
# Scriptish formats
|
||||
*.bat text svneol=native#text/plain
|
||||
*.bsh text svneol=native#text/x-beanshell
|
||||
*.cgi text svneol=native#text/plain
|
||||
*.cmd text svneol=native#text/plain
|
||||
*.js text svneol=native#text/javascript
|
||||
*.php text svneol=native#text/x-php
|
||||
*.pl text svneol=native#text/x-perl
|
||||
*.pm text svneol=native#text/x-perl
|
||||
*.py text svneol=native#text/x-python
|
||||
*.sh eol=lf svneol=LF#text/x-sh
|
||||
configure eol=lf svneol=LF#text/x-sh
|
||||
|
||||
# Image formats
|
||||
*.bmp binary svneol=unset#image/bmp
|
||||
*.gif binary svneol=unset#image/gif
|
||||
*.ico binary svneol=unset#image/ico
|
||||
*.jpeg binary svneol=unset#image/jpeg
|
||||
*.jpg binary svneol=unset#image/jpeg
|
||||
*.png binary svneol=unset#image/png
|
||||
*.tif binary svneol=unset#image/tiff
|
||||
*.tiff binary svneol=unset#image/tiff
|
||||
*.svg text svneol=native#image/svg%2Bxml
|
||||
|
||||
# Data formats
|
||||
*.pdf binary svneol=unset#application/pdf
|
||||
*.avi binary svneol=unset#video/avi
|
||||
*.doc binary svneol=unset#application/msword
|
||||
*.dsp text svneol=crlf#text/plain
|
||||
*.dsw text svneol=crlf#text/plain
|
||||
*.eps binary svneol=unset#application/postscript
|
||||
*.gz binary svneol=unset#application/gzip
|
||||
*.mov binary svneol=unset#video/quicktime
|
||||
*.mp3 binary svneol=unset#audio/mpeg
|
||||
*.ppt binary svneol=unset#application/vnd.ms-powerpoint
|
||||
*.ps binary svneol=unset#application/postscript
|
||||
*.psd binary svneol=unset#application/photoshop
|
||||
*.rdf binary svneol=unset#text/rdf
|
||||
*.rss text svneol=unset#text/xml
|
||||
*.rtf binary svneol=unset#text/rtf
|
||||
*.sln text svneol=native#text/plain
|
||||
*.swf binary svneol=unset#application/x-shockwave-flash
|
||||
*.tgz binary svneol=unset#application/gzip
|
||||
*.vcproj text svneol=native#text/xml
|
||||
*.vcxproj text svneol=native#text/xml
|
||||
*.vsprops text svneol=native#text/xml
|
||||
*.wav binary svneol=unset#audio/wav
|
||||
*.xls binary svneol=unset#application/vnd.ms-excel
|
||||
*.zip binary svneol=unset#application/zip
|
||||
|
||||
# Text formats
|
||||
.htaccess text svneol=native#text/plain
|
||||
*.bbk text svneol=native#text/xml
|
||||
*.cmake text svneol=native#text/plain
|
||||
*.css text svneol=native#text/css
|
||||
*.dtd text svneol=native#text/xml
|
||||
*.htm text svneol=native#text/html
|
||||
*.html text svneol=native#text/html
|
||||
*.ini text svneol=native#text/plain
|
||||
*.log text svneol=native#text/plain
|
||||
*.mak text svneol=native#text/plain
|
||||
*.qbk text svneol=native#text/plain
|
||||
*.rst text svneol=native#text/plain
|
||||
*.sql text svneol=native#text/x-sql
|
||||
*.txt text svneol=native#text/plain
|
||||
*.xhtml text svneol=native#text/xhtml%2Bxml
|
||||
*.xml text svneol=native#text/xml
|
||||
*.xsd text svneol=native#text/xml
|
||||
*.xsl text svneol=native#text/xml
|
||||
*.xslt text svneol=native#text/xml
|
||||
*.xul text svneol=native#text/xul
|
||||
*.yml text svneol=native#text/plain
|
||||
boost-no-inspect text svneol=native#text/plain
|
||||
CHANGES text svneol=native#text/plain
|
||||
COPYING text svneol=native#text/plain
|
||||
INSTALL text svneol=native#text/plain
|
||||
Jamfile text svneol=native#text/plain
|
||||
Jamroot text svneol=native#text/plain
|
||||
Jamfile.v2 text svneol=native#text/plain
|
||||
Jamrules text svneol=native#text/plain
|
||||
Makefile* text svneol=native#text/plain
|
||||
README text svneol=native#text/plain
|
||||
TODO text svneol=native#text/plain
|
||||
|
||||
# Code formats
|
||||
*.c text svneol=native#text/plain
|
||||
*.cpp text svneol=native#text/plain
|
||||
*.h text svneol=native#text/plain
|
||||
*.hpp text svneol=native#text/plain
|
||||
*.ipp text svneol=native#text/plain
|
||||
*.tpp text svneol=native#text/plain
|
||||
*.jam text svneol=native#text/plain
|
||||
*.java text svneol=native#text/plain
|
||||
43
build/Jamfile
Normal file
43
build/Jamfile
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
subproject libs/program_options/build ;
|
||||
|
||||
SOURCES = cmdline config_file options_description parsers variables_map
|
||||
value_semantic positional_options utf8_codecvt_facet convert
|
||||
;
|
||||
|
||||
lib boost_program_options
|
||||
: ../src/$(SOURCES).cpp
|
||||
: # build requirements
|
||||
[ common-names ] # magic for install and auto-link features
|
||||
<include>$(BOOST_ROOT) <sysinclude>$(BOOST_ROOT)
|
||||
: debug release # build variants
|
||||
;
|
||||
|
||||
dll boost_program_options
|
||||
: ../src/$(SOURCES).cpp
|
||||
: # build requirements
|
||||
[ common-names ] # magic for install and auto-link features
|
||||
<define>BOOST_ALL_DYN_LINK=1 # tell source we're building dll's
|
||||
<runtime-link>dynamic # build only for dynamic runtimes
|
||||
<include>$(BOOST_ROOT) <sysinclude>$(BOOST_ROOT)
|
||||
: debug release # build variants
|
||||
;
|
||||
|
||||
install program_options lib
|
||||
: <lib>boost_program_options <dll>boost_program_options
|
||||
;
|
||||
|
||||
stage stage/lib : <lib>boost_program_options <dll>boost_program_options
|
||||
:
|
||||
# copy to a path rooted at BOOST_ROOT:
|
||||
<locate>$(BOOST_ROOT)
|
||||
# make sure the names of the libraries are correctly named:
|
||||
[ common-names ]
|
||||
# add this target to the "stage" and "all" psuedo-targets:
|
||||
<target>stage
|
||||
<target>all
|
||||
:
|
||||
debug release
|
||||
;
|
||||
|
||||
# end
|
||||
20
build/Jamfile.v2
Normal file
20
build/Jamfile.v2
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
project boost/program_options
|
||||
: source-location ../src
|
||||
;
|
||||
|
||||
SOURCES = cmdline config_file options_description parsers variables_map
|
||||
value_semantic positional_options utf8_codecvt_facet
|
||||
convert
|
||||
;
|
||||
|
||||
import os ;
|
||||
if [ os.name ] = NT
|
||||
{
|
||||
linkage = <link>static ;
|
||||
}
|
||||
|
||||
lib program_options
|
||||
: $(SOURCES).cpp
|
||||
: $(linkage)
|
||||
;
|
||||
8
doc/Jamfile.v2
Normal file
8
doc/Jamfile.v2
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
import toolset ;
|
||||
toolset.using doxygen ;
|
||||
|
||||
boostbook program_option : program_options.xml ;
|
||||
|
||||
doxygen autodoc
|
||||
: [ GLOB ../../../boost/program_options : *.hpp ] ;
|
||||
81
doc/acknowledgements.xml
Normal file
81
doc/acknowledgements.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
||||
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
|
||||
[
|
||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
||||
%entities;
|
||||
]>
|
||||
<section>
|
||||
<title>Acknowledgements</title>
|
||||
|
||||
<para>I'm very gratefull to all the people who helped with the development,
|
||||
by discussion, fixes, and as users. It was pleasant
|
||||
to see all that involvement, which made the library much better than it
|
||||
would be otherwise.
|
||||
</para>
|
||||
|
||||
<para>In the early stages, the library was affected by discussions with
|
||||
Gennadiy Rozental, William Kempf and Alexander Okhotin.
|
||||
</para>
|
||||
|
||||
<para>Hartmut Kaiser was the first person to try the library on his project
|
||||
and send a number of suggestions and fixes.
|
||||
</para>
|
||||
|
||||
<para>The formal review lead to numerous comments and enhancements. Pavol
|
||||
Droba helped with the option description semantic. Gennadiy Rozental has
|
||||
critisized many aspects of the library which caused various simplifications.
|
||||
Pavel Vozenilek did carefull review of the implementation. A number of
|
||||
comments were made by:
|
||||
<itemizedlist>
|
||||
<listitem><para>David Abrahams</para></listitem>
|
||||
<listitem><para>Neal D. Becker</para></listitem>
|
||||
<listitem><para>Misha Bergal</para></listitem>
|
||||
<listitem><para>James Curran</para></listitem>
|
||||
<listitem><para>Carl Daniel</para></listitem>
|
||||
<listitem><para>Beman Dawes</para></listitem>
|
||||
<listitem><para>Tanton Gibbs</para></listitem>
|
||||
<listitem><para>Holger Grund</para></listitem>
|
||||
<listitem><para>Hartmut Kaiser</para></listitem>
|
||||
<listitem><para>Petr Kocmid</para></listitem>
|
||||
<listitem><para>Baptiste Lepilleur</para></listitem>
|
||||
<listitem><para>Marcelo E. Magallon</para></listitem>
|
||||
<listitem><para>Chuck Messenger</para></listitem>
|
||||
<listitem><para>John Torjo</para></listitem>
|
||||
<listitem><para>Matthias Troyer</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>Doug Gregor and Reece Dunn helped to resolve the issues with Boostbook
|
||||
version of the documentation.
|
||||
</para>
|
||||
|
||||
<para>Even after review, a number of people have helped with further development:
|
||||
<itemizedlist>
|
||||
<listitem><para>Rob Lievaart</para></listitem>
|
||||
<listitem><para>Thorsten Ottosen</para></listitem>
|
||||
<listitem><para>Joseph Wu</para></listitem>
|
||||
<listitem><para>Ferdinand Prantl</para></listitem>
|
||||
<listitem><para>Miro Jurisic</para></listitem>
|
||||
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Local Variables:
|
||||
mode: xml
|
||||
sgml-indent-data: t
|
||||
sgml-parent-document: ("program_options.xml" "section")
|
||||
sgml-set-face: t
|
||||
End:
|
||||
-->
|
||||
43
doc/alternatives
Normal file
43
doc/alternatives
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
CLI (part of the Jarakta project)
|
||||
http://jakarta.apache.org/commons/cli/index.html
|
||||
|
||||
This is Java library.
|
||||
The interface seems to be similiar, except for data storage.
|
||||
1. Instead of variables_map, the library can store the data
|
||||
as Java system properties.
|
||||
2. The class Option, which uses to describe the data, is also
|
||||
used to keep the value. In contract, I keep them in separate
|
||||
place. This facilitate using the same options description
|
||||
for different data sources.
|
||||
|
||||
TODO: Need to check that Option.setType method does.
|
||||
|
||||
Werken.opt
|
||||
http://sourceforge.net/projects/werken-opt/
|
||||
|
||||
This is a much simpler library then CLI, which
|
||||
somewhat less features.
|
||||
|
||||
JArgs
|
||||
http://jargs.sourceforge.net/
|
||||
|
||||
Another Java library. Has a fixed set of value types it can
|
||||
handle.
|
||||
|
||||
Options (by Brad Appleton)
|
||||
http://www.enteract.com/~bradapp/ftp/src/libs/C++/Options.html
|
||||
|
||||
This is very lean library. It does not provide argument validation,
|
||||
and the only iterface is iteration over arguments. An interesting
|
||||
iterface decision is using chars to identify presense of option's parameters.
|
||||
This may be moved to my library (|, :, ?, *, +)
|
||||
|
||||
Cmdline (by Brad Appleton)
|
||||
http://www.enteract.com/~bradapp/ftp/src/libs/C++/CmdLine.html
|
||||
|
||||
This library provides options validation and storage. Unfortunately
|
||||
1. Only a fixed set of data types is supported.
|
||||
2. It's intrusive -- one has to declare variable of "class ArgChar" or
|
||||
something, and then extract data from there.
|
||||
|
||||
150
doc/changes.xml
Normal file
150
doc/changes.xml
Normal file
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
||||
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
|
||||
[
|
||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
||||
%entities;
|
||||
]>
|
||||
<section id="program_options.changes">
|
||||
<title>Changes since formal review</title>
|
||||
|
||||
<para>During formal review, a large number of changes was suggested. To make
|
||||
using the new version easier, the implemented changes are described
|
||||
below.</para>
|
||||
|
||||
<para>Let's start with an example. The following is a typical code for the
|
||||
reviewed version:<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("magic", parameter<int>("value"), "magic value for the program")
|
||||
.default_value("43")
|
||||
|
||||
variables_map vm;
|
||||
options_and_arguments oa1 = parse_command_line(ac, av, desc);
|
||||
store(oa1, vm, desc)
|
||||
|
||||
variables_map vm2;
|
||||
ifstream ifs("main.cfg");
|
||||
options_and_arguments oa2 = parse_config_file(ifs, desc);
|
||||
store(oa1, vm2, desc);
|
||||
|
||||
vm.next(&vm2);
|
||||
</programlisting>The code for the current version would look like:
|
||||
<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("magic", value<int>()->default_value(43),
|
||||
"magic value for the program")
|
||||
|
||||
variables_map vm;
|
||||
|
||||
store(parse_command_line(ac, av, desc), vm);
|
||||
|
||||
ifstream ifs("main.cfg");
|
||||
store(parse_command_line(ifs, desc), vm);
|
||||
|
||||
notify(vm);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Let's examine all the changes in detail</para>
|
||||
|
||||
<section>
|
||||
<title>Option description</title>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The <code>parameter</code> function was renamed to
|
||||
<code>value</code>. Rationale: "paramater" is yet another term with no
|
||||
clear definition, while "value" is already used everywhere in
|
||||
docs.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The default value is specified in different place, and should
|
||||
use the value of desired type, not string. Previous code was:
|
||||
<programlisting>
|
||||
("magic", parameter<int>("value")).default_value("43")
|
||||
</programlisting>
|
||||
and the new code is
|
||||
<programlisting>
|
||||
("magic", parameter<int>("value")->default_value(43));
|
||||
</programlisting>
|
||||
Rationale: the new way is less restrictive. At the same time, the
|
||||
new design allows to implement other behaviour, like validation of
|
||||
the value, which require knowledge of the value type.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The number of token value can take on command line, which was
|
||||
specified using character suffix appended to value name, is now
|
||||
specified using more explicit member calls. Moreover, it's not longer
|
||||
possible to specify the "value name".
|
||||
For example:
|
||||
<programlisting>("numbers", parameter<int>("n+"))</programlisting>
|
||||
has became
|
||||
<programlisting>("numbers", value<int>()->multitoken())</programlisting>
|
||||
Rationale: such modifiers tend to make command line usage less
|
||||
clear. There's no need to make evil things too easy to do.
|
||||
The "value name" had only two roles: specifying modifiers, and
|
||||
telling what to output in automated help. The first role has became
|
||||
obsolete, and the second was questionable too. It was very unclear how
|
||||
to decide on the best "value name", and eventually the selection was randon.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Parsers</title>
|
||||
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The <code>options_and_argument</code> class was removed.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The <code>cmdline</code> and <code>config_file</code> classes
|
||||
were removed from the public interface. Various command line styles
|
||||
are now declared in the <code>command_line_style</code> subnamespace.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New function <code>parse_environment</code> was added.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support for positional options was added</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<title>Storage</title>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The <code>notify</code> function should be called after all
|
||||
sources are stored in a <code>variales_map</code> instance. This is
|
||||
done to property support priority of configuration sources.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Local Variables:
|
||||
mode: xml
|
||||
sgml-indent-data: t
|
||||
sgml-parent-document: ("program_options.xml" "section")
|
||||
sgml-set-face: t
|
||||
End:
|
||||
-->
|
||||
211
doc/design.xml
Normal file
211
doc/design.xml
Normal file
@@ -0,0 +1,211 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
||||
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
|
||||
[
|
||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
||||
%entities;
|
||||
]>
|
||||
<section id="program_options.design">
|
||||
<title>Design discussion</title>
|
||||
|
||||
<para>This section focuses on some of the design questions.
|
||||
</para>
|
||||
|
||||
<section id="program_options.design.unicode">
|
||||
|
||||
<title>Unicode support</title>
|
||||
|
||||
<para>Unicode support was one of the features specifically requested
|
||||
during the formal review. For the remainder of this document we'll use
|
||||
"Unicode support" as synonim for "wchar_t" support, that is assuming
|
||||
that "wchar_t" always use Unicode encoding. Also, when talking about
|
||||
"ascii" we'll not mean strict 7-bit ASCII encoding, but rather "char"
|
||||
strings in local 8-bit encoding.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Generally, "Unicode support" can mean
|
||||
many things, but for the program_options library it means that:
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Each parser should be able to accept either <code>char*</code>
|
||||
or <code>wchar_t*</code>, correctly split the input into option
|
||||
names and option values and return the data.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>For each option, it should be possible to specify if
|
||||
convertion from string to value should use ascii or unicode.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The library guarantees that:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>ascii input is passed to an ascii value without change
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>unicode input is passed to an unicode value without change</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>ascii input passed to an unicode value, and
|
||||
unicode input passed to an ascii value will be converted
|
||||
using codecvt
|
||||
facet (which can be specified by the user)
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>The important point is that it's possible to have some "ascii
|
||||
options" together with "unicode options". There are two reasons for
|
||||
this. First, for a given type you might not have a code to extract the
|
||||
value from unicode string and it's not good to require to write such code.
|
||||
Second, imagine a reusable library which has some options and exposes
|
||||
options description in its interface. If <emphasis>all</emphasis>
|
||||
options are either ascii or unicode, and the library does not use any
|
||||
unicode strings, then the author will likely to use ascii options, which
|
||||
would make the library unusable inside unicode
|
||||
applications. Essentially, it would be necessary to provide two version
|
||||
of the library -- ascii and unicode.
|
||||
</para>
|
||||
|
||||
<para>Another important point is that ascii strings are passed though
|
||||
without modification. In other words, it's not possible to just convert
|
||||
ascii to unicode and process the unicode further. The problem is that the
|
||||
default conversion mechanism -- the <code>codecvt</code> facet -- might
|
||||
not work with 8-bit input without additional setup.
|
||||
</para>
|
||||
|
||||
<para>The unicode support outlined above is not complete. For example,
|
||||
it's not planned to allow unicode in option names. The reason is that
|
||||
Unicode support beyond the basic one is hard and requires a Boost-wide
|
||||
solution. For example, even comparing two arbitrary Unicode strings is
|
||||
non-trivial. Finally, using Unicode in option names is related to
|
||||
internationalization, which has it's own complexities. E.g. if option
|
||||
names depend on current locale, then all program parts and other parts
|
||||
which use the name must be internationaled too.
|
||||
</para>
|
||||
|
||||
<para>The primary question in implementing the Unicode support is whether
|
||||
to use templates and <code>std::basic_string</code> or to use some
|
||||
internal encoding and convert between internal and external encodings on
|
||||
the interface boundaries.
|
||||
</para>
|
||||
|
||||
<para>The choice, mostly, is between code size and execution
|
||||
speed. Templated solution would either link library code into every
|
||||
application that uses the library (thereby making shared library
|
||||
impossible), or provide explicit instantiations in the shared library
|
||||
(increasing its size). The solution based on internal encoding would
|
||||
necessary make conversions in a number of places and will be somewhat slower.
|
||||
Since speed is generally not an issue for this library, the second
|
||||
solution looks more attractive, but we'll take a closer look at
|
||||
individual components.
|
||||
</para>
|
||||
|
||||
<para>For the parsers component, we have three choices:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Use fully templated implementation: given a string of certain
|
||||
type, a parser will return &parsed_options; instance with strings of the
|
||||
same type (i.e. the &parsed_options; class will be templated).</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Use internal encoding: same as above, but strings will be
|
||||
converted to/from internal encoding.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Use and partly expose the internal encoding: same as above,
|
||||
but the strings in the &parsed_options; instance will be in the
|
||||
internal encoding. This might avoid a conversion if
|
||||
&parsed_options; instance is passed directly to other component,
|
||||
but can be also dangerous/confusing for a user.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>The second solution appears to be the best -- it does not increase
|
||||
the code size much and is cleaner than the third. To avoid extra
|
||||
conversions, the unicode version of &parsed_options; can also store
|
||||
strings in internal encoding.
|
||||
</para>
|
||||
|
||||
<para>For the options descriptions component, we don't have much
|
||||
choice. Since it's not desirable to have either all options use ascii or all
|
||||
of them use unicode, but rather have some ascii and some unicode options, the
|
||||
interface of the &value_semantic; should works with both. The only way is
|
||||
to pass additional flag telling if strings use ascii or internal encoding.
|
||||
The instance of &value_semantic; can then convert into some
|
||||
other encoding if needed.
|
||||
</para>
|
||||
|
||||
<para>For the storage component, the only affected function is &store;.
|
||||
For unicode input, it should convert it to the internal encoding. It
|
||||
should also inform the &value_semantic; class about the used encoding.
|
||||
</para>
|
||||
|
||||
<para>The final decision is what internal encoding to use. The
|
||||
alternatives are:
|
||||
<code>std::wstring</code> (using UCS-4 encoding) and
|
||||
<code>std::string</code> (using UTF-8 encoding). The difference between
|
||||
alternatives is:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Speed: UTF-8 is a bit slower</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Space: UTF-8 takes less space when input is ascii</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Code size: UTF-8 requires additional conversion code. However,
|
||||
it allows to use existing parsers without converting them to
|
||||
<code>std::wstring</code> and such conversion is likely to create a
|
||||
number of new instantinations.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
There's no clear leader, but the last point seems important, so UTF-8
|
||||
will be used.
|
||||
</para>
|
||||
|
||||
<para>The reason why UTF-8 allows to use existing parsers is that
|
||||
searching for 7-bit ascii characters is really simple. However, there are
|
||||
two subtle issues:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>We need to assume the character literals use ascii encoding
|
||||
and that input use Unicode encoding.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Unicode character (say '=') can be followed by 'composing
|
||||
character' and the combination is not the same as just '=', so
|
||||
simple search for '=' might find the wrong character.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
Neither of issues appear to be critical in practice, since ascii is
|
||||
almost universal encoding and since composing characters on '=' (and
|
||||
other characters with special meaning to the library) are not likely.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Local Variables:
|
||||
mode: xml
|
||||
sgml-indent-data: t
|
||||
sgml-parent-document: ("program_options.xml" "section")
|
||||
sgml-set-face: t
|
||||
End:
|
||||
-->
|
||||
10
doc/footer.html
Normal file
10
doc/footer.html
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
<hr>
|
||||
<center>
|
||||
<address><small>
|
||||
Generated on $date with<br>
|
||||
<a href="http://www.stack.nl/~dimitri/doxygen/index.html">
|
||||
<img src="doxygen.png" alt="doxygen" align="middle" border=0 width=110 height=53></a>
|
||||
</small></address>
|
||||
</body>
|
||||
</html>
|
||||
19
doc/glossary.dox
Normal file
19
doc/glossary.dox
Normal file
@@ -0,0 +1,19 @@
|
||||
/** @page glossary Glosary
|
||||
|
||||
<dl>
|
||||
<dt>Token</dt><dd>A single whitespace-separated part of
|
||||
command line. In other words, an element of <tt>argv</tt> array.</dd>
|
||||
|
||||
<dt>Option</dt><dd>No definition yet. Options typically correspond to
|
||||
(name, value) pair. Then can spawn several tokens.</dd>
|
||||
|
||||
<dt>Argument</dt><dd>No definition yet.</dd>
|
||||
|
||||
<dt>Command line element</dt><dd>A complete part of command line. May
|
||||
be either option or argument.</dd>
|
||||
|
||||
<dt>Parameter</dt><dd>The syntantic element which specify value of the
|
||||
option</dd>
|
||||
|
||||
</dl>
|
||||
*/
|
||||
48
doc/glossary.xml
Normal file
48
doc/glossary.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<glossary>
|
||||
<title>Glossary</title>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Token</glossterm>
|
||||
<glossdef>
|
||||
<para>A single whitespace-separated part of
|
||||
command line. In other words, an element of <code>argv</code> array.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Option</glossterm>
|
||||
<glossdef>
|
||||
<para>No definition yet. Options typically correspond to
|
||||
(name, value) pair. Then can spawn several tokens.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Argument</glossterm>
|
||||
<glossdef>
|
||||
<para>No definition yet.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Command line element</glossterm>
|
||||
<glossdef>
|
||||
<para>A complete part of command line. May
|
||||
be either option or argument.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Parameter</glossterm>
|
||||
<glossdef>
|
||||
<para>The syntantic element which specify value of an
|
||||
option.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
</glossary>
|
||||
16
doc/header.html
Normal file
16
doc/header.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><meta name="robots" content="noindex">
|
||||
<title>$title</title>
|
||||
</head><body bgcolor="#ffffff">
|
||||
|
||||
<center>
|
||||
<a href="index.html">Main</a>
|
||||
<a href="annotated.html">Classes</a>
|
||||
<a href="namespacemembers.html">Namespace members</a>
|
||||
<a href="examples.html">Examples</a>
|
||||
<a href="recipes.html">Recipes</a>
|
||||
<a href="rationale.html">Rationale</a>
|
||||
<a href="pages.html">Related pages</a>
|
||||
</center>
|
||||
|
||||
<hr>
|
||||
100
doc/howto.xml
Normal file
100
doc/howto.xml
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
||||
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
|
||||
[
|
||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
||||
%entities;
|
||||
]>
|
||||
<section id="program_options.howto">
|
||||
|
||||
<title>Howto</title>
|
||||
|
||||
<para>This section describes how the library can be used in specific situations.</para>
|
||||
|
||||
<section>
|
||||
<title>Unicode support</title>
|
||||
|
||||
<para>To use the library with Unicode, you'd need to:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Use Unicode-aware parsers for unicode input</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Require unicode support for options which need it</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>Most of the parsers have unicode versions. For example, the
|
||||
&parse_command_line; function has an overload which takes
|
||||
<code>wchar_t</code> strings, instead of ordinary <code>char</code>.
|
||||
</para>
|
||||
|
||||
<para>Even if some of the parsers are Unicode-aware, it does not mean you
|
||||
need to change definition of all the options. In fact, for many options,
|
||||
like integer ones, it makes no sense. But to makes use of Unicode, you'd
|
||||
need <emphasis>some</emphasis> Unicode-aware options. They are different
|
||||
from ordinary option is
|
||||
that they accept <code>wstring</code> input, and process it using wide
|
||||
character streams. Creating an Unicode-aware option is easy, just use the
|
||||
the <code>wvalue</code> function instead of the regular <code>value</code>.
|
||||
</para>
|
||||
|
||||
<para>When ascii parser passes data to ascii option, or unicode parser
|
||||
passes data to unicode option, the data is not changed at all. So, ascii
|
||||
option will see string in local 8 bit encoding, and unicode option will
|
||||
see whatever string was passes as unicode input.
|
||||
</para>
|
||||
|
||||
<para>The interesting question is what happens when unicode data is passed
|
||||
to ascii option, and vice versa. The library perform automatic
|
||||
conversion from Unicode to local 8 bit encoding. For example, if command
|
||||
line is always ascii, but you use <code>wstring</code> options, then the
|
||||
ascii input will be converted into unicode.
|
||||
</para>
|
||||
|
||||
<para>To perform the conversion, library uses the<code>codecvt<wchar_t,
|
||||
char></code> locale facet from the global locale. This means that if
|
||||
you want to work with string which use local 8 bit encoding (as opposed to
|
||||
7 bit ascii subset), your application should start with:
|
||||
<programlisting>
|
||||
locale::global(locale(""));
|
||||
</programlisting>
|
||||
which would set up the conversion facet according to user's selected
|
||||
locale.
|
||||
</para>
|
||||
|
||||
<para>It's wise to check the status of C++ locale support on your
|
||||
implementation, though. The quick test involves three steps:
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>Go the the "test" directory and built the "test_convert" binary.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Set some non-ascii locale in the environemt. On Linux, one can
|
||||
run, for example: <screen>
|
||||
$ export LC_CTYPE=ru_RU.KOI8-R
|
||||
</screen>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Run the "test_convert" binary passing it as parameter
|
||||
arbitrary non-ascii string in selected encoding. If you see a list
|
||||
of Unicode codepoints, everything's OK. Otherwise, locale support on
|
||||
your system might be broken.</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Local Variables:
|
||||
mode: xml
|
||||
sgml-indent-data: t
|
||||
sgml-parent-document: ("program_options.xml" "section")
|
||||
sgml-set-face: t
|
||||
End:
|
||||
-->
|
||||
193
doc/options_description.xml
Normal file
193
doc/options_description.xml
Normal file
@@ -0,0 +1,193 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
||||
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
|
||||
[
|
||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
||||
%entities;
|
||||
]>
|
||||
|
||||
<section>
|
||||
<title>Options description</title>
|
||||
|
||||
<para>The options description layer allows to specify what options are
|
||||
allowed -- and how they are should be processed. There are two
|
||||
sublayers: syntactic and semantic. The syntactic layer allows the parsers
|
||||
to group tokens into (name, value) pairs, where value is just vector of
|
||||
string. The semantic layer is responsible for converting value of option
|
||||
into more usable C++ types.
|
||||
</para>
|
||||
|
||||
<para>This separation is an important part of library design. The parsers
|
||||
use only syntactic layer, which takes away some of the freddom -- actually,
|
||||
freedom to use overly complex structures. For example, it's not easily
|
||||
possible to parse syntax like: <screen>calc --expression=1 + 2/3</screen>
|
||||
because it's not possible to parse "1 + 2/3" without known that it's C
|
||||
expression. With a little help from user the task becomes trivial, and the
|
||||
syntax becomes much clear: <screen>calc --expression="1 + 2/3"</screen>
|
||||
</para>
|
||||
|
||||
<section><title>Syntactic layer</title>
|
||||
|
||||
<para>
|
||||
The syntactic layer is represented by
|
||||
<classname>boost::program_options::options_description</classname>. The
|
||||
simplest usage is illustrated below:
|
||||
<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
;
|
||||
</programlisting>
|
||||
This declares one option named "help" and associates a description with
|
||||
it. No much, but it's just a start.
|
||||
</para>
|
||||
|
||||
<para>To make an option accept a parameter, you need to pass a third
|
||||
parameter to the call, the name of the value:
|
||||
<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
("verbose", "change verbosity level", "number")
|
||||
;
|
||||
</programlisting>
|
||||
Since we're talking about syntactic layer, we can't expect any
|
||||
interesting logic yet. In fact, the passed value name is only used for
|
||||
help message, and the value itself will represented as vector of string,
|
||||
leaving all interpretation to the user.
|
||||
</para>
|
||||
|
||||
<para>The only other thing that's possible to specify is number of tokens in
|
||||
the value. Consider the following example:
|
||||
<programlisting>
|
||||
("verbose", "change verbosity level", "number?")
|
||||
("users", "list of users", "email+")
|
||||
("log", "where to send log", "logger*")
|
||||
</programlisting>
|
||||
For the first option, it's possible to specify no value at all -- it's
|
||||
optional. For the second option, the option name can be followed by
|
||||
several tokens, for example:
|
||||
<screen>
|
||||
--users jim john
|
||||
</screen>
|
||||
Lastly, the third option can accept value with no tokens (i.e. no value),
|
||||
and value with several tokens.
|
||||
</para>
|
||||
|
||||
<para>Wondering why characters are using to specify properties? Find out
|
||||
here.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Semantic layer</title>
|
||||
|
||||
<para>The sematic layer is more interesting. As said before, it converts
|
||||
values, represented as vector of strings, into C++ type that the programmer
|
||||
desires. In order to do so, it's necessary to pass something smarter
|
||||
than a value name. That "smarter" thing is a pointer to the
|
||||
<classname>boost::program_options::value_semantic</classname> abstract
|
||||
class. Most of the time, you'd be using the
|
||||
<classname>boost::program_options::typed_value</classname>, created via
|
||||
call to <functionname>boost::program_options::value</functionname>
|
||||
function.
|
||||
For example:
|
||||
<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("verbose", "change verbosity level", value<int>("number"))
|
||||
;
|
||||
</programlisting>
|
||||
Specifies that the value of the "verbose" option is of type
|
||||
<code>int</code>.
|
||||
</para>
|
||||
|
||||
<para>The
|
||||
pointer returned by the <code>value</code> function can be used to
|
||||
specify additional information about the value, for example:
|
||||
<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("verbose", "change verbosity level", value<int>("number")->default_value(0))
|
||||
;
|
||||
</programlisting>
|
||||
would cause the "verbose" option to have value of 0 when nothing else if
|
||||
specified. For the complete list of methods which can be be called on the
|
||||
pointer, refer to class
|
||||
<classname>boost::program_options::typed_value</classname> documentation.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Positional options</title>
|
||||
|
||||
<para>Our definition of option as (name, value) pairs is simple and
|
||||
usefull, but in one special case of command line, there's a
|
||||
problem. Command line can include <firstterm>positional option</firstterm>,
|
||||
which does not specify any name at all, for example:
|
||||
<screen>
|
||||
archiver --compression=9 /etc/passwd
|
||||
</screen>
|
||||
Here, there's no option name to assign to "/etc/passwd" element.
|
||||
</para>
|
||||
|
||||
<para>One solution is to ask the user to extract positional options
|
||||
himself and process as he likes. However, there's a nicer approach --
|
||||
provide a method to guess names for positional options, so that the
|
||||
above command line can be interpreted the same way as:
|
||||
<screen>
|
||||
archiver --compression=9 --input-file=/etc/passwd
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>The &positional_options_desc; class is what allows command line
|
||||
parser to guess the names. The class specifies how many positional options
|
||||
are allowed, and for each allowed option, specifies the name. For example:
|
||||
<programlisting>
|
||||
positional_options_description pd;
|
||||
pd.add("input-file", 1, 1);</programlisting>
|
||||
specifies that for exactly one, first, positinal option the guessed
|
||||
name will be "input-file".
|
||||
</para>
|
||||
|
||||
<para>It's possible to specify that a number of positional options will be
|
||||
given the same name, or even all positional options.
|
||||
<programlisting>
|
||||
positional_options_description pd;
|
||||
pd.add("output-file", 2, 2).add_optional("input-file", 0, -1);
|
||||
</programlisting>
|
||||
In the above examples, first two positional options will be associated
|
||||
with name "output-file", and all others (which can be omitted), with the
|
||||
name "input-file".
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Classes list</title>
|
||||
<para>The following classes belong to this component<table>
|
||||
<title>Classes list</title>
|
||||
<tgroup cols="2">
|
||||
|
||||
<thead>
|
||||
<row>
|
||||
<entry><para>Name</para></entry>
|
||||
<entry><para>Description</para></entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><para><classname
|
||||
alt="boost::program_options::options_description">options_description
|
||||
</classname></para></entry>
|
||||
<entry><para>Description of a number of options</para></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
306
doc/overview.xml
Normal file
306
doc/overview.xml
Normal file
@@ -0,0 +1,306 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
||||
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
|
||||
[
|
||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
||||
%entities;
|
||||
]>
|
||||
<section id="program_options.overview">
|
||||
<title>Library overview</title>
|
||||
|
||||
<para>In the previous section, we've seen several examples of library usage.
|
||||
This section will describe overall library design: what are the primary
|
||||
components and what do they do.
|
||||
</para>
|
||||
|
||||
<para>The library has thee main components:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The options description component, which is used to describe which options
|
||||
are allowed and what to do with the values of the options.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The parsers component, which uses this information to find option names
|
||||
and values in input source and return them.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The storage component, which provides the
|
||||
interface to access the value of an option. It also converts string
|
||||
representation of value that parsers return into desired C++ types.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>To be a little more concrete, the <code>options_description</code>
|
||||
class is from options description component, the
|
||||
<code>parse_command_line</code> function is from parsers component, and the
|
||||
<code>variables_map</code> class is from storage component. </para>
|
||||
|
||||
<para>We've learned how those components can be used by the
|
||||
<code>main</code> function to parse command line and config file. Before
|
||||
going into details of each component, few notes about world outside of
|
||||
<code>main</code>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For that outside world, the storage component is the most important. It
|
||||
provides a class which stores all option values and that class can be
|
||||
freely passed around your program to modules which need access to the
|
||||
options. All the other components can be used in the place where actual
|
||||
parsing is done. However, it might also make sense to make individual program
|
||||
modules to describe their options and pass them to main module, which will
|
||||
merge all options together. Of course, this is only important when the
|
||||
number of options is large and declaring them in one place becomes
|
||||
troublesome.
|
||||
</para>
|
||||
|
||||
<!--
|
||||
<para>The design looks very simple and straight-forward, but it is worth
|
||||
noting some important points:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The options description is not tied to specific source. Once
|
||||
options are described, all parsers can use that description.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The parsers are intended to be fairly dump. They just
|
||||
split the input into (name, value) pairs, using strings to represent
|
||||
names and values. No meaningfull processing of values is done.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The storage component is focused on storing options values. It
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para>
|
||||
-->
|
||||
|
||||
<section>
|
||||
<title>Options description component</title>
|
||||
|
||||
<para>The options description component has three main classes:
|
||||
&option_description;, &value_semantic; and &options_description;. The
|
||||
first two together describe a single option. The &option_description;
|
||||
class contains the option's name, description and a pointer to &value_semantic;,
|
||||
which, in turn, knows the type of option's value and can parse the value,
|
||||
apply default value, and so on. The &options_description; class is a
|
||||
container for instances of &option_description;.
|
||||
</para>
|
||||
|
||||
<para>For almost every library, those classes could be created in a
|
||||
conventional way: e.g. you'd create new options using constructors and
|
||||
then call <code>insert</code> method of &options_description;. However,
|
||||
that's overly verbose for declaring 20 or 30 options. This concern lead
|
||||
to creation of the syntax that you've already seen:
|
||||
<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("help", "produce help")
|
||||
("optimization", value<int>()->default_value(10), "optimization level")
|
||||
;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>The call to the <code>value</code> function creates instance of
|
||||
suitable class, derived from <code>options_semantic</code>. Calling member
|
||||
functions of that instance allows to specify additional information (this
|
||||
essentially emulates named parameters for constructor). Calls to
|
||||
<code>operator()</code> on the object returned by <code>add_options</code>
|
||||
forward arguments to constructor of the <code>option_description</code>
|
||||
class and add the new instance.</para>
|
||||
|
||||
<!-- Note that the classes are not modified during parsing -->
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Parsers component</title>
|
||||
|
||||
<para>The parsers component splits input sources into (name, value) pairs.
|
||||
Each parser looks for possible options and consult the options
|
||||
description component to find if the option is know and how the value
|
||||
can be specified. In the simplest case, name is explicitly specified,
|
||||
which allows to decide if such option is known. If it is known, the
|
||||
&value_semantic; instance tells how the value can be specified. Common
|
||||
cases are when the value is explicitly specified by the user, and when
|
||||
the value cannot be specified by the user, but the presense of the
|
||||
option implies some value (for example, <code>true</code>). So, the
|
||||
parser checks that the value is specified when needed and not specified
|
||||
when not needed, and returns new (name, value) pair.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To invoke a parser you typically call a function, passing options
|
||||
desription and command line or config_file or something else.
|
||||
The results of parsing are returned as instance of the &parsed_options;
|
||||
class. Typically, it's passed directly to the storage
|
||||
component. However, it also can be used to add some special processing,
|
||||
for example if order in which options are specified is important.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There are three exceptions to the above model -- all related to tradional
|
||||
usage of command line. While they require some support from the options
|
||||
description component, the additional complexity is tolerable.
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The name specified on command line can be
|
||||
different from the option name -- it's common to provide "short option
|
||||
name" alias to a longer name. It's also common to allow abbreviated name
|
||||
to be specified on command line.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Sometimes it's desirable to specify value as several
|
||||
tokens. For example, an option "--email-recipient" can be followed
|
||||
by several emails, each as a separate command line token. This
|
||||
behaviour is supported, though it can lead to parsing ambiguities
|
||||
and is not enabled by default.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Command line can contain positional options -- i.e. elements
|
||||
which don't have any name. The command line parser provides a
|
||||
mechanism to guess names for such options, as we've seen in the
|
||||
tutorial.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<title>Storage component</title>
|
||||
|
||||
<para>The storage component is responsible for:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Storing the final values of option into speciall class and in
|
||||
regular variables</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Handling priorities between different sources.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Calling user-specified 'notify' functions with the final
|
||||
values of options.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>Let's consider an example:
|
||||
<programlisting>
|
||||
variables_map vm;
|
||||
store(parse_command_line(argc, argv, desc), vm);
|
||||
store(parse_config_file("example.cfg", desc), vm);
|
||||
finish(vm);
|
||||
</programlisting>
|
||||
The <code>variables_map</code> class is used to store the option
|
||||
values. The two calls to the <code>store</code> function add values
|
||||
found on command line and in config file. Finally the call to
|
||||
<code>notify</code> function runs user-specified notify functions and
|
||||
stores values into regular variables, if needed.
|
||||
</para>
|
||||
|
||||
<para>The priority is handled
|
||||
in a simple way: the <code>store</code> function will not change value
|
||||
of an option if it's already assigned. In this case, if command line
|
||||
specifies a value for an option, that value will be preferred to the
|
||||
value specified in confict file.
|
||||
</para>
|
||||
|
||||
<warning>
|
||||
<para>Don't forget to call the <code>notify</code> function when you've
|
||||
stored all parsed values.</para>
|
||||
</warning>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Annotated list of symbols</title>
|
||||
|
||||
<para>The following table describes all the important symbols in the
|
||||
library, for quick access.</para>
|
||||
|
||||
<informaltable pgwide="1">
|
||||
|
||||
<tgroup cols="2">
|
||||
<colspec colname='c1'/>
|
||||
<colspec colname='c2'/>
|
||||
<thead>
|
||||
|
||||
<row>
|
||||
<entry>Symbol</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
<row>
|
||||
<entry namest='c1' nameend='c2'>Options description component</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>&options_description;</entry>
|
||||
<entry>describes a number of options</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>&value;</entry>
|
||||
<entry>defines the option's value</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry namest='c1' nameend='c2'>Parsers component</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>&parse_command_line;</entry>
|
||||
<entry>parses command line</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>&parse_config_file;</entry>
|
||||
<entry>parses config file</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>&parse_environment;</entry>
|
||||
<entry>parses environment</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry namest='c1' nameend='c2'>Storage component</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>&variables_map;</entry>
|
||||
<entry>storage for option values</entry>
|
||||
</row>
|
||||
|
||||
</tbody>
|
||||
</tgroup>
|
||||
|
||||
</informaltable>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Local Variables:
|
||||
mode: xml
|
||||
sgml-indent-data: t
|
||||
sgml-parent-document: ("program_options.xml" "section")
|
||||
sgml-set-face: t
|
||||
End:
|
||||
-->
|
||||
182
doc/post_review_plan.txt
Normal file
182
doc/post_review_plan.txt
Normal file
@@ -0,0 +1,182 @@
|
||||
|
||||
Program options post-review development plan.
|
||||
|
||||
0. Convert all documentation to BoostBook format
|
||||
|
||||
1. (done)
|
||||
Simplify and clarify interface.
|
||||
|
||||
It turns out that most users are interested in 'variables_map' class, so
|
||||
it must be possible to use it directly, without even knowing about
|
||||
'options_and_arguments'. The proposed interface is:
|
||||
|
||||
options_description desc;
|
||||
....
|
||||
variables_map vm;
|
||||
load_from_command_line(vm, desc, argc, argv);
|
||||
|
||||
2. (done)
|
||||
Better separation of syntaxic and semantic processing, as suggested by
|
||||
Pavol Droba.
|
||||
|
||||
The problem with current 'option_description' interface is that the
|
||||
'validator' and 'notifier' callbacks are not really usable by ordinary
|
||||
users --- it's extender's interface. The current 'parameter' function uses
|
||||
those callback to provide user-friendly semantic interface, but it's not
|
||||
documented nor completely worked out.
|
||||
|
||||
In the new interface, the second parameter of 'option_description' ctor
|
||||
will have two possibilities: just a string and a pointer to a new class
|
||||
'value_descriptor'. When passed the latter, it will invoke the instance on
|
||||
itself, and then delete the object. A function 'value' will be provided,
|
||||
that will create value specific for a type.
|
||||
|
||||
Example
|
||||
("magic", value<int>("n", &n)->default_value(10), "magic value").
|
||||
|
||||
The 'value' function will create instances of 'typed_value_descriptor'
|
||||
type, with the following methods:
|
||||
- default_value
|
||||
- interpreter
|
||||
- validator
|
||||
- notifier
|
||||
|
||||
The 'option_description' class we'll have two attributes to support
|
||||
semantic operation: 'generator', which will handle conversion from string
|
||||
into value (including application of default value), and 'notifier'. Similiar
|
||||
to the the current design, those attributes will be set by
|
||||
'value_descriptor' instances.
|
||||
|
||||
Another function, "bool_switch" will create value descriptor for type bool,
|
||||
with default value of false. The function is needed to avoid special-casing
|
||||
'value' function on bool type, which was considered confusing (Neal D. Becker).
|
||||
|
||||
3. (done) Support for positional options.
|
||||
|
||||
Positional options will be treated uniformly with ordinary ones. User will
|
||||
be able to specify that, for example, third positional option is to be
|
||||
interpreted as option "output-file" with the same value.
|
||||
|
||||
The user interface will be simple: user will provide two instanes of
|
||||
'options_description' to functions which parse command line. For example.
|
||||
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("magic", "n", "magic value")
|
||||
;
|
||||
|
||||
options_description pdesc;
|
||||
pdesc.add_options()
|
||||
("output-file", "n", "output file")
|
||||
("input-files*", value< vector<string> >("n"), "files")
|
||||
;
|
||||
|
||||
variables_map vm;
|
||||
load_from_command_line(vm, desc, pdesc, argc, argv);
|
||||
|
||||
4. (done, except for registry)
|
||||
Multiple sources improvement.
|
||||
|
||||
Need to implement support for registry/environment.
|
||||
Also, must devise a way to handle different naming of option in
|
||||
sources. Lastly, the storing of values into program variables should
|
||||
become part of 'variables_map' interface.
|
||||
|
||||
5. Improve documentation.
|
||||
|
||||
Chuck Messenger:
|
||||
"When code is given for an example program, after the code, give examples of
|
||||
using the program, along with the expected output."
|
||||
|
||||
Pavol Droba:
|
||||
"I would prefer a few chapters explaining various components of the
|
||||
library, each followed by a reference."
|
||||
|
||||
Pavel Vozenilek:
|
||||
> Documentation should contain list of compilers the library works on and
|
||||
> also info whether MSVC 6 port is feasible or not.
|
||||
>
|
||||
> The non-Doxygen part of documentation can be also a bit expanded: e.g. I
|
||||
> would welcome some high level overview of the algorithms and structures and
|
||||
> info about expected CPU/memory consumption.
|
||||
>
|
||||
> Also info whether there are any internal limits (like option length) .
|
||||
>
|
||||
> Some examples may be bit more annotated, also contain what is expected
|
||||
> output.
|
||||
|
||||
Syntax highligting.
|
||||
|
||||
Document "*" in option names
|
||||
Automated tests for examples?
|
||||
(new) Comments inside code snippets?
|
||||
(new) Table of all symbols
|
||||
|
||||
|
||||
6. (deferred)
|
||||
Unicode support
|
||||
|
||||
- unicode in argv/argc
|
||||
- Unicode in config files not supported
|
||||
(
|
||||
The difference between ASCII and unicode files is:
|
||||
- big endian UTF16 starts with 2 bytes FE FF 9mandatory by Unicode
|
||||
standard) - little endian UTF16 starts with FF FE
|
||||
- UTF8 text starts with EF BB BF
|
||||
|
||||
Pavel Vozenilek
|
||||
)
|
||||
|
||||
7. Config file improvements
|
||||
|
||||
- should have "allow_unregistered" for config_file.
|
||||
- (done) bool options in config file do not work.
|
||||
- "#" inside strings, in config files (Pavel Vozenilek)
|
||||
|
||||
8.
|
||||
Cmdline improvements
|
||||
|
||||
- must be able to parse WinMain string
|
||||
- support for response files
|
||||
|
||||
9. Other changes.
|
||||
|
||||
- (outdated) get_value -> value (Beman)
|
||||
- (done) is "argv" const in std, or not? Adjust docs if not.
|
||||
- variables_map::count_if, find_if (Tanton Gibbs)
|
||||
- Works with exceptions disabled.
|
||||
- (outdated) disallow empty name for the 'parameter' function
|
||||
- check for prefixes if 'allow_guessing' is on
|
||||
- check for duplicate declaration of options.
|
||||
- test additional parser
|
||||
- Show default values in help output
|
||||
- Adaptive field width
|
||||
- Mandatory options
|
||||
- (new) return vector from parsers by auto_ptr, not by value?
|
||||
- (new) rename value_semantic into value_description
|
||||
- (new) output for positional_options_description
|
||||
- (new) variables_map should throw when value not found
|
||||
- (important) decide where we check that the number of passed option
|
||||
tokens is less than the allowed number. In parser or later?
|
||||
- (important) what if the same option has different definitions?
|
||||
- (new) We lost the ability to specify options_description instance
|
||||
in call to 'store'. So now we can't store just subset of options.
|
||||
Is it a problem?
|
||||
- (new) Improve formatting of 'arg'.
|
||||
- (new) revive real and regexp examples.
|
||||
- (new) even more simpler syntax for assignent to var?
|
||||
|
||||
|
||||
10. Uncertain
|
||||
- Function to get program name
|
||||
- Order of 'description' and 'value'.
|
||||
|
||||
|
||||
11. (new) Look at all "TODO" comments.
|
||||
(new) Check that all methods are documented.
|
||||
|
||||
|
||||
12. Deferred
|
||||
|
||||
- storing value to boost::optional
|
||||
- setting a flag when option is found
|
||||
162
doc/program_options.dox
Normal file
162
doc/program_options.dox
Normal file
@@ -0,0 +1,162 @@
|
||||
/** @mainpage Program options documentation
|
||||
|
||||
@section scope Scope
|
||||
|
||||
Briefly, the library should allow program developers to obtain
|
||||
<em>program options</em>, i.e. (name,value) pairs from the user,
|
||||
via conventional methods such as command line and config file.
|
||||
|
||||
Necessary facilities include:
|
||||
- parse command line
|
||||
- parse config files
|
||||
- perform semantic validation on input, such as checking for correct
|
||||
type of parameters, and storing values.
|
||||
- combine all inputs together, so that all program options can
|
||||
be obtained in one place.
|
||||
|
||||
@section goals Goals
|
||||
The fundamental goals for this library were:
|
||||
- it should be more convenient to use it than parse command line by hand,
|
||||
even when the number of possible options is 2,
|
||||
- all popular command line styles should be supported,
|
||||
- "you pay for what you use" principle is important: simple utilities
|
||||
need not be forced to depend on excessive amount of code.
|
||||
- it must be possible to validate option values, convert them to required
|
||||
types, and store either in program variables, or in data structures
|
||||
maintained by the library.
|
||||
- data from command line and config file should be usable together, and
|
||||
alternative program option sources (such as registry) should be
|
||||
possible.
|
||||
|
||||
@section design_overview Design overview
|
||||
|
||||
To meet the stated goals, the library uses a layered architecture.
|
||||
-# At the bottom, there are two parser classes,
|
||||
boost::program_options::cmdline and
|
||||
boost::program_options::config_file.
|
||||
They are responsible for syntax matters only and provide simple
|
||||
iterator-like interface.
|
||||
-# The boost::program_options::options_and_arguments holds the result of parsing command line or
|
||||
config file. It is still concerned with syntax only and holds precisely
|
||||
what is found on command line. There's a couple of associated parse
|
||||
functions (
|
||||
@ref parse_cmdline_func "1",
|
||||
@ref parse_config_file_func "2"),
|
||||
which relieve the user from the need to iterate over options
|
||||
and arguments manually.
|
||||
-# The class boost::program_options::options_description is a high-level
|
||||
description of allowed
|
||||
program options, which does not depend on concrete parser class. In
|
||||
addition, it can be used to provide help message. There are parse
|
||||
functions which return options_and_arguments given options_description.
|
||||
-# The options_description class also has semantic responsibilities. It's
|
||||
possible to specify validators for option, their default values, and the
|
||||
like. There's a function boost::program_options::perform_semantic_actions,
|
||||
which handles this information and returns a map of option values.
|
||||
-# Finally, at the top, there boost::program_options::variables_map class.
|
||||
It's possible to
|
||||
store options in it, and obtain them later. Another feature is that
|
||||
different variable_map instances can be linked together, so that both
|
||||
command line and config file data is used. Additional option sources can
|
||||
be added at this level.
|
||||
|
||||
@section futher_reading Futher reading
|
||||
|
||||
To get further information about the library, you might want to read
|
||||
the documentation for the classes referenced above. Another possibility
|
||||
is to look through the examples:
|
||||
|
||||
- @ref options_description "simple usage"
|
||||
- @ref variables_map "parsing with validation and assignment to program variables"
|
||||
- @ref multiple_sources "using command line and config file together"
|
||||
- @ref custom_syntax "customized options syntax"
|
||||
- @ref real_example "real example"
|
||||
- @ref custom_validator "custom validator"
|
||||
- @ref multiple_modules "possible approach for multi-module programs"
|
||||
- @ref cmdline "low level cmdline parsing"
|
||||
|
||||
Finally, you might want the check out the @ref recipes "recipes" page.
|
||||
*/
|
||||
|
||||
/** @page examples Examples
|
||||
|
||||
- @ref options_description "simple usage"
|
||||
- @ref variables_map "parsing with validation and assignment to program variables"
|
||||
- @ref multiple_sources "using command line and config file together"
|
||||
- @ref custom_syntax "customized options syntax"
|
||||
- @ref real_example "real example"
|
||||
- @ref custom_validator "custom validator"
|
||||
- @ref multiple_modules "possible approach for multi-module programs"
|
||||
- @ref cmdline "low level cmdline parsing"
|
||||
*/
|
||||
|
||||
/** @page options_description Options description
|
||||
|
||||
Example of quite a simple usage. Options are registered and the
|
||||
command line is parsed. The user is responsible to interpreting the
|
||||
option values. This also how automatic help message.
|
||||
|
||||
@include options_description.cpp
|
||||
*/
|
||||
|
||||
/** @page variables_map Variables map
|
||||
|
||||
In this example, the <tt>parameter</tt> function is used to enable
|
||||
validation of options (i.e. checking that they are of correct type).
|
||||
The option values are also stored in program variables.
|
||||
|
||||
@include variables_map.cpp
|
||||
*/
|
||||
|
||||
/** @page multiple_sources Multiple sources
|
||||
|
||||
It is possible for program options to come from different sources.
|
||||
Here, the command line and a config file are used, and the values
|
||||
specified in both are combined, with preferrence given to the
|
||||
command line.
|
||||
|
||||
@include multiple_sources.cpp
|
||||
*/
|
||||
|
||||
/** @page custom_syntax Custom syntax
|
||||
|
||||
Some applications use a custom syntax for the command line. In this
|
||||
example, the gcc style of "-fbar"/"-f" is handled.
|
||||
|
||||
@include custom_syntax.cpp
|
||||
*/
|
||||
|
||||
/** @page real_example A real example
|
||||
|
||||
Shows how to use custom option description class and custom formatter.
|
||||
Also validates some option relationship.
|
||||
|
||||
@include real.cpp
|
||||
*/
|
||||
|
||||
/** @page multiple_modules Multiple modules
|
||||
|
||||
Large programs are likely to have several modules which want to use
|
||||
some options. One possible approach is show here.
|
||||
@sa @ref recipe_multiple_modules
|
||||
|
||||
@include multiple_modules.cpp
|
||||
*/
|
||||
|
||||
/** @page custom_validator Custom validator
|
||||
|
||||
It's possible to plug in arbitrary function for converting the string
|
||||
value from the command line to the value used in your program. The
|
||||
example below illustrates this.
|
||||
|
||||
@include regex.cpp
|
||||
*/
|
||||
|
||||
/** @page cmdline The cmdline class
|
||||
|
||||
When validation or automatic help message are not needed, it's possible
|
||||
to use low-level boost::program_options::cmdline class, like shown
|
||||
in this example.
|
||||
|
||||
@include cmdline.cpp
|
||||
*/
|
||||
39
doc/program_options.ent
Normal file
39
doc/program_options.ent
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!ENTITY positional_options_desc
|
||||
"<classname alt='boost::program_options::positional_options_description'>positional_options_description</classname>">
|
||||
|
||||
<!ENTITY options_description
|
||||
"<classname alt='boost::program_options::options_description'>options_description</classname>">
|
||||
|
||||
<!ENTITY option_description
|
||||
"<classname alt='boost::program_options::option_description'>option_description</classname>">
|
||||
|
||||
<!ENTITY value_semantic
|
||||
"<classname alt='boost::program_options::value_semantic'>value_semantic</classname>">
|
||||
|
||||
<!ENTITY parsed_options
|
||||
"<classname alt='boost::program_options::parsed_options'>parsed_options</classname>">
|
||||
|
||||
<!ENTITY variables_map
|
||||
"<classname alt='boost::program_options::variables_map'>variables_map</classname>">
|
||||
|
||||
|
||||
<!ENTITY value
|
||||
"<functionname alt='boost::program_options::value'>value</functionname>">
|
||||
|
||||
<!ENTITY parse_command_line
|
||||
"<functionname
|
||||
alt='boost::program_options::parse_command_line'>parse_command_line</functionname>">
|
||||
|
||||
<!ENTITY parse_config_file
|
||||
"<functionname alt='boost::program_options::parse_config_file'>parse_config_file</functionname>">
|
||||
|
||||
<!ENTITY parse_environment
|
||||
"<functionname alt='boost::program_options::parse_environment'>parse_environment</functionname>">
|
||||
|
||||
<!ENTITY store
|
||||
"<functionname alt='boost::program_options::store'>store</functionname>">
|
||||
|
||||
<!ENTITY command_line_parser
|
||||
"<classname alt='boost::program_options::command_line_parser'>command_line_parser</classname>">
|
||||
|
||||
102
doc/program_options.xml
Normal file
102
doc/program_options.xml
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
||||
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd">
|
||||
<library
|
||||
name="Program_options"
|
||||
dirname="program_options" id="program_options"
|
||||
last-revision="$Date$"
|
||||
xmlns:xi="http://www.w3.org/2003/XInclude">
|
||||
<libraryinfo>
|
||||
<author>
|
||||
<firstname>Vladimir</firstname>
|
||||
<surname>Prus</surname>
|
||||
</author>
|
||||
|
||||
<librarypurpose>
|
||||
Facilities to obtain configuration data from command line, config files
|
||||
and other sources.
|
||||
</librarypurpose>
|
||||
<librarycategory name="category:data-structures"></librarycategory>
|
||||
</libraryinfo>
|
||||
|
||||
<title>Boost.program_options library</title>
|
||||
|
||||
<section>
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>The program_options library allows program developers to obtain
|
||||
<emphasis>program options</emphasis>, i.e. (name,value) pairs from the user,
|
||||
via conventional methods such as command line and config file.</para>
|
||||
|
||||
<para>Why would you use such a library, and why it's better than parsing
|
||||
your command line by trivial hand-written code? Some of the reasons are:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>It's easier. The syntax for declaring options is simple, and
|
||||
the library itself is small. Things like conversion of option values to
|
||||
desired type and storing into program variables are handled
|
||||
automatically.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Error reporting is better. All problems with command line are
|
||||
reported, while hand-written code can just misparse the input. In
|
||||
addition, the usage message can be automatically generated, to
|
||||
avoid falling out of sync with the real list of options.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Options can be read from anywhere. Sooner or later command
|
||||
line will be not enough for your users, and you'd want config files
|
||||
or maybe even environment variables. This can be added without no
|
||||
effort on your part.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Now let's see some examples of the library usage in the <xref
|
||||
linkend="program_options.tutorial"/>.
|
||||
</para>
|
||||
|
||||
<!--
|
||||
<para>Basic goals for the library include support for:</para>
|
||||
<itemizedlist>
|
||||
<listitem>parsing of command line and config files</listitem>
|
||||
<listitem>performing semantic processing of input, such as checking for correct
|
||||
type of parameters, and storing values to program variables</listitem>
|
||||
<listitem>combining all inputs together, so that all program options can
|
||||
be obtained in one place</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>it should be more convenient to use it than parse command line by hand,
|
||||
even when the number of possible options is 2,</listitem>
|
||||
<listitem>all popular command line styles should be supported,</listitem>
|
||||
<listitem>
|
||||
- "you pay for what you use" principle is important: simple utilities
|
||||
need not be forced to depend on excessive amount of code.</listitem>
|
||||
- it must be possible to validate option values, convert them to required
|
||||
types, and store either in program variables, or in data structures
|
||||
maintained by the library.
|
||||
- data from command line and config file should be usable together, and
|
||||
alternative program option sources (such as registry) should be
|
||||
possible.
|
||||
</itemizedlist>
|
||||
</para>
|
||||
-->
|
||||
|
||||
</section>
|
||||
|
||||
<xi:include href="tutorial.xml"/>
|
||||
<xi:include href="overview.xml"/>
|
||||
|
||||
<xi:include href="howto.xml"/>
|
||||
<xi:include href="design.xml"/>
|
||||
<xi:include href="acknowledgements.xml"/>
|
||||
<xi:include href="changes.xml"/>
|
||||
|
||||
|
||||
<xi:include href="autodoc.boostbook"/>
|
||||
</library>
|
||||
16
doc/questions
Normal file
16
doc/questions
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
Rename 'parameter' in option_description with something
|
||||
better, e.g. 'value_specification'?
|
||||
|
||||
Approximate matching for variable_map access?
|
||||
|
||||
Should be able to stack validators?
|
||||
|
||||
Case with variables like this
|
||||
'foo' = 10
|
||||
'foo.bar' = 12
|
||||
should become an error
|
||||
|
||||
|
||||
|
||||
|
||||
12
doc/questions.dox
Normal file
12
doc/questions.dox
Normal file
@@ -0,0 +1,12 @@
|
||||
/** @page open_questions Open questions.
|
||||
|
||||
<ol>
|
||||
<li> Shouldn't validators always use "C" locale?
|
||||
|
||||
<li> Shouldn't validator for intr check for different bases?
|
||||
|
||||
<li> Does anyone need "getop_option_description"?.
|
||||
|
||||
</ol>
|
||||
|
||||
*/
|
||||
15
doc/rationale
Normal file
15
doc/rationale
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
We could either implement simple chaining for variable maps, or
|
||||
implement generic composition classes. The former was choosen,
|
||||
mostly because of simplicity.
|
||||
|
||||
There were two implementation approaches for multiple option
|
||||
occurences in options_and_arguments. First is store them
|
||||
separately. The advantage is that it's easy to obtain all
|
||||
occurences before certain position on command line. The
|
||||
disadvantage is that we cannot return a reference to
|
||||
vector<vector<string> > in get_all_values. It was considered
|
||||
that if support for position-dependent options is to be
|
||||
added, then we're be mostly interested in occurences of
|
||||
a single option that were before some point. That's possible
|
||||
with vector<vector<string> > storage.
|
||||
95
doc/rationale.dox
Normal file
95
doc/rationale.dox
Normal file
@@ -0,0 +1,95 @@
|
||||
/** @page rationale Rationale
|
||||
|
||||
@section code_size Focus on code size
|
||||
|
||||
The program options library has two important properties:
|
||||
- runtime performance is not important. After all, command line processing
|
||||
is done only once, and the amount of data is small.
|
||||
- code size matters. Since parsing command line is utility task, users
|
||||
won't be glad to have lots of code linked to every binary which has
|
||||
options.
|
||||
|
||||
For the above reasons, the the library is designed so that it can be easily
|
||||
used as shared library, with minimum code on the side of main application.
|
||||
In particular, templates are used only where necessary, for example for
|
||||
validation of user-defined types. In other places, boost::function is
|
||||
used to allow customization, but keep templates out of the public
|
||||
interface.
|
||||
|
||||
@section string_vs_enums Strings vs. enums
|
||||
|
||||
In some places, the library uses strings to convey information that
|
||||
could be represented by enumerations or values. For example,
|
||||
the program_options::option_description class allows to add "?" to the
|
||||
parameter name to specify that the parameter is optional. For another
|
||||
example, while program_options::cmdline class allows to obtain the
|
||||
index of option, it does not require to specify an index for each option,
|
||||
and it's possible to tell options by their names.
|
||||
|
||||
Such interface appears to be much more usable. If we were using
|
||||
enumeration for different properties of parameter, there would be
|
||||
another argument to many functions, the need to type long, possible
|
||||
qualified names, and little advantage.
|
||||
|
||||
That little advantage is that if you type a wrong enumeration name,
|
||||
you'd get a compile error. If you type '!' instead of '?' after parameter
|
||||
name, you'd get incorrect behaviour. However, such errors are deemed
|
||||
rare.
|
||||
|
||||
@section char_vs_string const char* vs. std::string
|
||||
|
||||
Most of the interface uses const char* where std::string seems a natural
|
||||
choice. The reason is that those functions are called many times: for
|
||||
example to declare all options. They are typically called with string
|
||||
literals, and implicit conversion to string appears to take a lot of
|
||||
code space. Providing both std::string and const char* version would
|
||||
considerably bloat the interface. Since passing std::string is considered
|
||||
rare, only const char* versions are provided.
|
||||
|
||||
@section init_syntax Initialization syntax
|
||||
|
||||
The syntax used for creating options_description instance was designed to
|
||||
be as easy as possible in the most common case. Consider:
|
||||
@code
|
||||
desc.add_options()
|
||||
("verbose", "", "verbosity level")
|
||||
("magic", "int", "magic value").notify(some_func)
|
||||
;
|
||||
@endcode
|
||||
Here, most common properties of options: name, presense of parameter and
|
||||
description, are specified very concisely, and additional properties can
|
||||
be given quite naturally, too.
|
||||
|
||||
Another possibility would be:
|
||||
@code
|
||||
option_description d1(...), d2(...);
|
||||
desc.add(d1 & d2);
|
||||
@endcode
|
||||
or
|
||||
@code
|
||||
option_description d1(...), d2(...);
|
||||
desc = d1, d2;
|
||||
@endcode
|
||||
|
||||
The drawback is the need to explicitly create new objects and give names
|
||||
to them. The latter problem can be helped if objects are created inside
|
||||
expressions:
|
||||
@code
|
||||
desc = option_description(...), option_description(...)
|
||||
@endcode
|
||||
but there's still extra typing.
|
||||
|
||||
@section help_handling Handling of --help
|
||||
|
||||
It was suggested by Gennadiy Rozental that occurence of <tt>--help</tt>
|
||||
on command line results in throwing an exception. Actually, the
|
||||
"special" option must have been configurable. This was not
|
||||
implemeneted, because applications might reasonable want to process
|
||||
the rest of command line even of <tt>--help</tt> was seen. For example,
|
||||
<tt>--verbose</tt> option can control how much help should be output,
|
||||
or there may be several subcommand with different help screens.
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
91
doc/recipes.dox
Normal file
91
doc/recipes.dox
Normal file
@@ -0,0 +1,91 @@
|
||||
/** @page recipes Recipes
|
||||
|
||||
Here, we'll give solution for some desires which seem common.
|
||||
|
||||
@section recipe_parameter_validation How to check for correct option value types and assign them?
|
||||
|
||||
There's the boost::program_options::parameter function. It
|
||||
returns a object, which, if passed as the second parameter
|
||||
to boost::program_options::option_description constructor,
|
||||
establishes correct validation routine. A simple example
|
||||
is
|
||||
@code
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("foo", parameter<int>("arg"), "obscure option")
|
||||
;
|
||||
@endcode
|
||||
|
||||
If you pass an address of <tt>int</tt> variable as the second
|
||||
parameter of the <tt>parameter</tt> function, that variable will
|
||||
be assigned the options's value.
|
||||
|
||||
@sa @ref variables_map
|
||||
|
||||
@section recipe_lazy What if I don't want to declare any options?
|
||||
|
||||
I'm not sure this is good idea. In particular, mistyped options
|
||||
will be silently ignored, leading to possible user surprises.
|
||||
Futher, the boost::program_options::cmdline class was specially
|
||||
designed to be very lightweight.
|
||||
|
||||
Anyway, there's a version of the parse_command_line function
|
||||
which does not take an options_description instance. Also, the
|
||||
cmdline class ctor accepts an 'allow_unregistered' parameter.
|
||||
In both cases, all options will be allowed, and treated as if
|
||||
they have optional parameter.
|
||||
|
||||
Note that with the default style,
|
||||
@verbatim
|
||||
--foo bar
|
||||
@endverbatim
|
||||
will be taken as option "foo" with value "bar", which is
|
||||
probably not correct. You should disable option parameter in
|
||||
the next token to avoid problems.
|
||||
|
||||
@sa boost::program_options::cmdline
|
||||
|
||||
@section recipe_multiple_modules I have several separate modules which must controlled by options. What am I to do?
|
||||
|
||||
There are several solutions.
|
||||
|
||||
@subsection sb1 Everything's global
|
||||
|
||||
You can create a single instance of the <tt>options_description</tt> class
|
||||
somewhere near <tt>main</tt>. All the modules will export their own
|
||||
options using other <tt>options_description</tt> instances which can
|
||||
be added to the main one. After that, you'd parse command line and
|
||||
config files. The parsing results will be stored in one variables_map,
|
||||
which will be passed to all modules, which can work with their own
|
||||
options.
|
||||
|
||||
@subsection sb2 Private option data
|
||||
|
||||
Assume one of the modules does not like to see irrelevant options.
|
||||
For example, it outputs a configuration file for other program, and
|
||||
irrelevant options will confuse that program.
|
||||
|
||||
It's possible to give the module only the options that it has
|
||||
registered. First, the module provides an options_description instance
|
||||
which is added to the global one. Second the command line is parsed
|
||||
to produce an options_and_arguments instance. Lastly, the <tt>store</tt>
|
||||
function is called. If passed the options_description instance previously
|
||||
returned by the module, it will store only options specified in that
|
||||
instance.
|
||||
@sa @ref multiple_modules
|
||||
|
||||
|
||||
@subsection sb3 Unique option names
|
||||
|
||||
The most general solution would be to give unique names to options
|
||||
for different modules. One module will declare option "module1.server",
|
||||
and another would declare "module2.internal_checks". Of course, there
|
||||
can be global options like "verbosity", declared by <tt>main</tt> and
|
||||
used by all modules.
|
||||
|
||||
This solution avoids all possible name clashes between modules. On
|
||||
the other hand, longer option names can be less user-friendly. This
|
||||
problem can be alleviated if module prefix is used only for less
|
||||
common option, needed for fine-tuning.
|
||||
|
||||
*/
|
||||
209
doc/requirements-Rozental
Normal file
209
doc/requirements-Rozental
Normal file
@@ -0,0 +1,209 @@
|
||||
From rogeeff@mail.com Fri Nov 16 19:57:49 2001
|
||||
Received: from imap.cs.msu.su (imap.cs.msu.su [158.250.10.15])
|
||||
by redsun.cs.msu.su (8.9.3/8.9.3) with ESMTP id TAA06515
|
||||
for <ghost@redsun.cs.msu.su>; Fri, 16 Nov 2001 19:59:43 +0300 (MSK)
|
||||
Received: from n15.groups.yahoo.com (n15.groups.yahoo.com [216.115.96.65])
|
||||
by imap.cs.msu.su (8.11.6/8.11.6) with SMTP id fAGGtrd57869
|
||||
for <ghost@cs.msu.su>; Fri, 16 Nov 2001 19:55:54 +0300 (MSK)
|
||||
(envelope-from sentto-1234907-17382-1005929874-ghost=cs.msu.su@returns.groups.yahoo.com)
|
||||
X-eGroups-Return: sentto-1234907-17382-1005929874-ghost=cs.msu.su@returns.groups.yahoo.com
|
||||
Received: from [10.1.1.222] by n15.groups.yahoo.com with NNFMP; 16 Nov 2001 16:57:42 -0000
|
||||
X-Sender: rogeeff@mail.com
|
||||
X-Apparently-To: boost@yahoogroups.com
|
||||
Received: (EGP: mail-8_0_0_1); 16 Nov 2001 16:57:53 -0000
|
||||
Received: (qmail 2553 invoked from network); 16 Nov 2001 16:57:53 -0000
|
||||
Received: from unknown (216.115.97.172)
|
||||
by m4.grp.snv.yahoo.com with QMQP; 16 Nov 2001 16:57:53 -0000
|
||||
Received: from unknown (HELO n6.groups.yahoo.com) (216.115.96.56)
|
||||
by mta2.grp.snv.yahoo.com with SMTP; 16 Nov 2001 16:57:53 -0000
|
||||
X-eGroups-Return: rogeeff@mail.com
|
||||
Received: from [10.1.10.109] by n6.groups.yahoo.com with NNFMP; 16 Nov 2001 16:57:52 -0000
|
||||
To: boost@yahoogroups.com
|
||||
Message-ID: <9t3gid+hdf3@eGroups.com>
|
||||
In-Reply-To: <E164iu4-00052e-00@zigzag.cs.msu.su>
|
||||
User-Agent: eGroups-EW/0.82
|
||||
X-Mailer: eGroups Message Poster
|
||||
X-Originating-IP: 199.119.33.162
|
||||
From: "Gennadiy E. Rozental" <rogeeff@mail.com>
|
||||
X-Yahoo-Profile: rogeeff
|
||||
MIME-Version: 1.0
|
||||
Mailing-List: list boost@yahoogroups.com; contact boost-owner@yahoogroups.com
|
||||
Delivered-To: mailing list boost@yahoogroups.com
|
||||
Precedence: bulk
|
||||
List-Unsubscribe: <mailto:boost-unsubscribe@yahoogroups.com>
|
||||
Date: Fri, 16 Nov 2001 16:57:49 -0000
|
||||
Reply-To: boost@yahoogroups.com
|
||||
Subject: [boost] Re: arguments parsing, wildcard matcher
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Type: text/plain;
|
||||
charset=US-ASCII
|
||||
Content-Length: 5662
|
||||
Status: R
|
||||
X-Status: N
|
||||
|
||||
--- In boost@y..., Vladimir Prus <ghost@c...> wrote:
|
||||
>
|
||||
> > Just a couple of classes I wrote that I wondered if anyone thought
|
||||
> > any place in boost:
|
||||
> >
|
||||
> > arguments : simple command-line arguments and options parser:
|
||||
> >
|
||||
> > class arguments
|
||||
> > {
|
||||
> > public:
|
||||
> > arguments(int argc, char* argv[]);
|
||||
> >
|
||||
> > bool has_option(const char* name) const;
|
||||
> > bool get_option(const char* name, bool& value) const;
|
||||
>
|
||||
> > Any interest? Already proposed? Wasting my time?
|
||||
>
|
||||
> Actually, I'm already working on library with the same goals but
|
||||
more
|
||||
> elaborated. Moreover, it's almost finished. I planned to announce
|
||||
it later,
|
||||
> but have to do it now. My design goals were:
|
||||
> - It should be resonable to use the library to parse as little as
|
||||
2 command
|
||||
> line options.
|
||||
> - It must be extandable to privide any resonable handling
|
||||
> - since command line is just a way to affect the program behaviour,
|
||||
other
|
||||
> ways to accomplish that must be provided, most notable is
|
||||
configuration file
|
||||
> - library should provide a way to store information from command
|
||||
line and
|
||||
> config file in a way allowing easy retrieval and using to change
|
||||
configurable
|
||||
> parameters of the program.
|
||||
>
|
||||
> The docs are available at:
|
||||
> http://chronos.cs.msu.su/~ghost/projects/config_db/doc/index.html
|
||||
>
|
||||
> Let me know what you think.
|
||||
|
||||
Privet, Volodya.
|
||||
|
||||
Here what I am looking for to be supported by Command Line Argument
|
||||
Framework directly or by means of easy extension:
|
||||
|
||||
1. command line argument formats
|
||||
a. -<one letter key> <value>
|
||||
b. -<one letter key><value>
|
||||
c. -<key> <value>
|
||||
d. -<option> - any length
|
||||
e. /<key> <value> - and all other cases like a,b,c but with /
|
||||
instead
|
||||
g. --<key> <value>
|
||||
h. -<key substring> <value>
|
||||
An example: let say you expecting argument -osagent_port
|
||||
then following argument lists should be valid:
|
||||
-o 15000
|
||||
-osa 15000
|
||||
-osagent_port 15000
|
||||
On the other hand it should perform validity checks. For example
|
||||
if you also expect another argument -osagent_host. then first 2
|
||||
argument list above should generate runtime error and 3d should
|
||||
pass. Arguments integrity check should also be performed, i.e.
|
||||
you should not allow for user to define 2 argument like this:
|
||||
"-port"
|
||||
"-port_type"
|
||||
|
||||
2. argument types
|
||||
I should be able to explicitle specify expected argument type. For
|
||||
example: std::string, int, double, option(bool). The framework should
|
||||
perform value validation. For example 1.23 is not valid for int
|
||||
argument. The framework should allow to use user-defined classes as
|
||||
expected types for command-line argument. In other word. If I provide
|
||||
you a class with predefined psecification framework should try to
|
||||
generate object of argument class. Some simple extention you can
|
||||
provide youself. For example, using following command line you should
|
||||
be able to generate std::list<int>
|
||||
-values 2 5 7 8
|
||||
and using following command line you should be able to generate
|
||||
std::list<std::string>
|
||||
-files_to_test test1.td test2.td test3.td test4.td
|
||||
and using following command line user should be able to provide
|
||||
argument class A to generate std::list<A>
|
||||
struct A{
|
||||
std::string key;
|
||||
int value;
|
||||
};
|
||||
|
||||
-parameters_mapping name1 4 name5 7 name6 8 name9 1123
|
||||
|
||||
3. argument storage.
|
||||
a. Framework should be able to generate and store arguments
|
||||
internally. In this case framework in responsable for memory.
|
||||
b. Framework should be able to generate and store argument into the
|
||||
user-bound location. In this case user in responsable for memory.
|
||||
|
||||
4. arguments can have default values
|
||||
|
||||
5. arguments can be optional and required. The framework should
|
||||
automatically check presence of of all required arguments.
|
||||
|
||||
6. argument value access
|
||||
a. if user passed storage location - he will be able to get value
|
||||
from there
|
||||
b. by name and type. If any of them is incorrect - error. The same
|
||||
rules aplied here as in 1.h if argument matching algorithm allows
|
||||
substrings.
|
||||
c. is_present check - to be able to check presence of optional
|
||||
arguments.
|
||||
|
||||
7. usage line.
|
||||
The framework should be able to generate a using line given a
|
||||
difinition of all feilds. To support this you will probably need
|
||||
argument description as one of the command line argument
|
||||
constructor's argument. Thr framework should be able to configured to
|
||||
use different usage line. If command line contain predefined keyword
|
||||
(-help or /? for example) framework should print usage message.
|
||||
|
||||
8. Framework Error
|
||||
If any of the following condition occures during command line
|
||||
processing the framework should generate an error and print a usage
|
||||
line:
|
||||
a. invalid aargument
|
||||
b. ambiguous argument
|
||||
c. invalid value for the argument
|
||||
d. absence of required argument
|
||||
e. framework meet -help (of any other predefined keyword)
|
||||
|
||||
Invalid name or type should generate exception during value access.
|
||||
|
||||
Here my view on the problem.
|
||||
|
||||
Regards,
|
||||
|
||||
Gennadiy.
|
||||
|
||||
|
||||
P.S. Did you look into Brat Appleton's work? It seems to be close to
|
||||
what you are doing.
|
||||
|
||||
>
|
||||
> Concerning your proposal, I can mark two points, apart from it's
|
||||
been a
|
||||
> subset of mine:
|
||||
> 1. It uses get_options(const char* name, type& value) methods,
|
||||
which are not
|
||||
> extendable (and the similar thing is used in KConfig class in
|
||||
KDE....) What I
|
||||
> propose is
|
||||
> variables_map vm .....
|
||||
> int i = vm["magic"].as<int>()
|
||||
> FontName fn = vm["font"].as<FontName>()
|
||||
> 2. You propose wildcard expansions. This is good. But it is easy to
|
||||
add it to
|
||||
> any existing command line parsing library.
|
||||
>
|
||||
> - Volodya
|
||||
|
||||
|
||||
Info: http://www.boost.org Unsubscribe: <mailto:boost-unsubscribe@yahoogroups.com>
|
||||
|
||||
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
|
||||
|
||||
|
||||
|
||||
344
doc/tutorial.xml
Normal file
344
doc/tutorial.xml
Normal file
@@ -0,0 +1,344 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
||||
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
|
||||
[
|
||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
||||
%entities;
|
||||
]>
|
||||
|
||||
<section id="program_options.tutorial">
|
||||
<title>Tutorial</title>
|
||||
|
||||
<para>In this section, we'll take a look at the most common usage scenarious
|
||||
of the program_options library, starting with the simplest one. The examples
|
||||
show only the interesting code parts, but the complete programs can be found
|
||||
in the "example" directory. Through all the examples, we'll assume that the
|
||||
following namespace alias is in effect:
|
||||
<programlisting>namespace po = boost::program_options;</programlisting>
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Getting started</title>
|
||||
|
||||
<para>The first example is the simplest possible: it only handles two
|
||||
options. Here's the source code (the full program is in
|
||||
"example/first.cpp"):
|
||||
|
||||
<programlisting>
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
("compression", po::value<int>(), "set compression level")
|
||||
;
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||
po::notify(vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << desc << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vm.count("compression")) {
|
||||
cout << "Compression level was set to "
|
||||
<< vm["compression"].as<int>() << ".\n";
|
||||
} else {
|
||||
cout << "Compression level was not set.\n";
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>We start by declaring all allowed options using the
|
||||
&options_description; class. The <code>add_options</code> method of that
|
||||
class returns a special proxy object that defines
|
||||
<code>operator()</code>. Calls to that operator actually declare
|
||||
options. The parameters are option name, information about value, and option
|
||||
description. In this example, the first option has no value, and the second
|
||||
one has a value of type <code>int</code>.
|
||||
</para>
|
||||
|
||||
<para>After that, an object of class <code>variables_map</code> is
|
||||
declared. That class is indented to store values of options, and can store
|
||||
values of arbitrary types. The following calls to
|
||||
<code>parse_command_line</code>, <code>store</code> and <code>notify</code>
|
||||
functions cause <code>vm</code> to contain all option found on the command
|
||||
line.</para>
|
||||
|
||||
<para>And now, finally, we can use the options as we like. The
|
||||
<code>variables_map</code> class can be used just like
|
||||
<code>std::map</code>, except that values stored there must be converted to
|
||||
the desired type with the <code>as</code> method, as shown above.
|
||||
</para>
|
||||
|
||||
<para>It's now a good time to try compiling the code yourself, but if
|
||||
you're not yet ready, here's an example session:
|
||||
<screen>
|
||||
$bin/gcc/debug/first
|
||||
Compression level was not set.
|
||||
$bin/gcc/debug/first --help
|
||||
Allowed options:
|
||||
--help : produce help message
|
||||
--compression arg : set compression level
|
||||
|
||||
$bin/gcc/debug/first --compression 10
|
||||
Compression level was set to 10.
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Option details</title>
|
||||
|
||||
<para>Option value, surely, can have other types than <code>int</code>, and
|
||||
can have other interesting properties, which we'll discuss right now. The
|
||||
complete version of code snipped below can be found in
|
||||
"example/options_description.cpp".</para>
|
||||
|
||||
<para>Imagine we're writing a compiler. It should take the optimizaiton
|
||||
level, a number of include paths, and a number of input files, and perform some
|
||||
interesting work. Let's describe the options:
|
||||
<programlisting>
|
||||
int opt;
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
("optimization", po::value<int>(&opt)->default_value(10),
|
||||
"optimization level")
|
||||
("include-path,I", po::value< vector<string> >(),
|
||||
"include path")
|
||||
("input-file", po::value< vector<string> >(), "input file")
|
||||
;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>The "--help" option should be familiar from the previous example.
|
||||
It's a good idea to have this option in all cases.</para>
|
||||
|
||||
<para>The "optimization" option shows
|
||||
two new features. First, we specify an address of variable. After
|
||||
storing values, that variable will have the value of the option. Second,
|
||||
we specify default value of 10, which will be used if not value is
|
||||
specified by the user.
|
||||
</para>
|
||||
|
||||
<para>The "include-path" option is an example of the only case, where
|
||||
interface of the <code>options_description</code> class serves specific
|
||||
source -- the command line. Users typically like to use short option names
|
||||
for common options, and the "include-path,I" name specifies that short
|
||||
option name is "I". So, both "--include-path" and "-I" can be used.
|
||||
</para>
|
||||
|
||||
<para>The "input-file" option is used to specify the list of files to
|
||||
process. That's okay for a start, but, of course, writing something like:
|
||||
<screen>
|
||||
compiler --input-file=a.cpp
|
||||
</screen>
|
||||
is a little non-standard, compared with
|
||||
<screen>
|
||||
compiler a.cpp
|
||||
</screen>
|
||||
We'll address this in a moment.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The command line tokens which don't have any option name, like above, are
|
||||
called "positional options" by this library. They can be handled,
|
||||
too. With a little help from the user, the library can decide that "a.cpp"
|
||||
really means the same as "--input-file=a.cpp". Here's the additional code
|
||||
we need:
|
||||
<programlisting>
|
||||
po::positional_options_description p;
|
||||
p.add("input-file", -1);
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::command_line_parser(ac, av).
|
||||
options(desc).positional(p).run(), vm);
|
||||
po::notify(vm);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The first two lines says that all positional options should be translated
|
||||
into "input-file" option. Also note that we use the
|
||||
&command_line_parser; class to parse the command
|
||||
line, not the &parse_command_line;
|
||||
function. The latter is a convenient wrapper for simple cases, but now we
|
||||
need to pass additional information.
|
||||
</para>
|
||||
|
||||
<para>By now, all options are described and parsed. We'll save ourself the
|
||||
trouble of implementing the rest of compiler logic, and only print the
|
||||
options:
|
||||
<programlisting>
|
||||
if (vm.count("include-path"))
|
||||
{
|
||||
cout << "Include paths are: "
|
||||
<< vm["include-path"].as< vector<string> >() << "\n";
|
||||
}
|
||||
|
||||
if (vm.count("input-file"))
|
||||
{
|
||||
cout << "Input files are: "
|
||||
<< vm["input-file"].as< vector<string> >() << "\n";
|
||||
}
|
||||
|
||||
cout << "Optimization level is " << opt << "\n";
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Here's an example session:
|
||||
<screen>
|
||||
$bin/gcc/debug/options_description --help
|
||||
Usage: options_description [options]
|
||||
Allowed options:
|
||||
--help : produce help message
|
||||
--optimization arg : optimization level
|
||||
-I [ --include-path ] arg : include path
|
||||
--input-file arg : input file
|
||||
$bin/gcc/debug/options_description
|
||||
Optimization level is 10
|
||||
$bin/gcc/debug/options_description --optimization 4 -I foo a.cpp
|
||||
Include paths are: foo
|
||||
Input files are: a.cpp
|
||||
Optimization level is 4
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Oops, there's a slight problem. It's still possible to specify the
|
||||
"--input-file" option, and usage message says so, which can be confusing
|
||||
for the user. It would be nice to hide this information, but let's wait
|
||||
for the next example.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Multiple sources</title>
|
||||
|
||||
<para>It's quite likely that specifying all options to our compiler on the
|
||||
command line will annoy users. What if user installs a new library and
|
||||
wants to always pass an additional command line element? What if he has
|
||||
made some choices which should be applied on every run? It's desirable to
|
||||
create a config file with common setting, which will used together with
|
||||
command line.
|
||||
</para>
|
||||
|
||||
<para>Of course, there should be a need to combine the values from command
|
||||
line and config file. For example, optimization level specified on command
|
||||
line should override value from config file. On the other hand, include
|
||||
paths are better merged together.
|
||||
</para>
|
||||
|
||||
<para>Let's see the code now. The complete program is in
|
||||
"examples/multiple_sources.cpp". The option definition has two interesting
|
||||
details. First, we declare several instances of the
|
||||
<code>options_description</code> class. The reason is that, in general,
|
||||
not all options are alike. Some options, like "input-file" above, should
|
||||
not be presented in automatic help message. Some options make sense only
|
||||
in config file. Finally, it nice to have some structure in help message,
|
||||
not jump plain long list of options. Let's declare several option groups:
|
||||
<programlisting>
|
||||
// Declare a group of options that will be
|
||||
// allowed only on command line
|
||||
po::options_description generic("Generic options");
|
||||
generic.add_options()
|
||||
("version,v", "print version string")
|
||||
("help", "produce help message")
|
||||
;
|
||||
|
||||
// Declare a group of options that will be
|
||||
// allowed both on command line and in
|
||||
// config file
|
||||
po::options_description config("Configuration");
|
||||
config.add_options()
|
||||
("optimization", po::value<int>(&opt)->default_value(10),
|
||||
"optimization level")
|
||||
("include-path,I",
|
||||
po::value< vector<string> >()->composing(),
|
||||
"include path")
|
||||
;
|
||||
|
||||
// Hidden options, will be alled both on command line and
|
||||
// in config file, but will not be show to the user.
|
||||
po::options_description hidden("Hidden options");
|
||||
hidden.add_options()
|
||||
("input-file", po::value< vector<string> >(), "input file")
|
||||
;
|
||||
</programlisting>
|
||||
Note the call to the <code>composing</code> method in declaration of the
|
||||
"include-path" option. It tells that values from different sources
|
||||
should be composed together, as we'll see shortly.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <code>add</code> method of the <code>options_description</code>
|
||||
class can be used to further group the options:
|
||||
<programlisting>
|
||||
po::options_description cmdline_options;
|
||||
cmdline_options.add(generic).add(config).add(hidden);
|
||||
|
||||
po::options_description config_file_options;
|
||||
config_file_options.add(config).add(hidden);
|
||||
|
||||
po::options_description visible("Allowed options");
|
||||
visible.add(generic).add(config);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Parsing and storing of values follows the usual pattern, except that
|
||||
we additionally call <functionname>parse_config_file</functionname>, and
|
||||
call the &store; function twice. But what
|
||||
happens if the same value is specified both on the command line and in
|
||||
config file. Usually, the value stored first is preferred. This is what
|
||||
happens for the "--optimization" option. For "composing" options, like
|
||||
"include-file", the values are merged together.
|
||||
</para>
|
||||
|
||||
<para>Here's an example session:
|
||||
<screen>
|
||||
$bin/gcc/debug/multiple_sources
|
||||
Include paths are: /opt
|
||||
Optimization level is 1
|
||||
$bin/gcc/debug/multiple_sources --help
|
||||
Allows options:
|
||||
|
||||
Generic options:
|
||||
-v [ --version ] : print version string
|
||||
--help : produce help message
|
||||
|
||||
Configuration:
|
||||
--optimization n : optimization level
|
||||
-I [ --include-path ] path : include path
|
||||
|
||||
$bin/gcc/debug/multiple_sources --optimization=4 -I foo a.cpp b.cpp
|
||||
Include paths are: foo /opt
|
||||
Input files are: a.cpp b.cpp
|
||||
Optimization level is 4
|
||||
</screen>
|
||||
The first invocation uses values from configuration file. The last
|
||||
invocation also uses values from command line. As we see, the include
|
||||
paths on command line and config file are merged, while optimization is
|
||||
taked from command line.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Local Variables:
|
||||
mode: xml
|
||||
sgml-indent-data: t
|
||||
sgml-parent-document: ("program_options.xml" "section")
|
||||
sgml-set-face: t
|
||||
End:
|
||||
-->
|
||||
16
example/Jamfile
Normal file
16
example/Jamfile
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
subproject libs/program_options/example ;
|
||||
|
||||
rule program-options-example ( name extra-sources * )
|
||||
{
|
||||
exe $(name) : $(name).cpp <lib>../build/boost_program_options $(extra-sources)
|
||||
: <include>$(BOOST_ROOT) ;
|
||||
}
|
||||
|
||||
program-options-example first ;
|
||||
program-options-example options_description ;
|
||||
program-options-example multiple_sources ;
|
||||
program-options-example custom_syntax ;
|
||||
|
||||
#program-options-example real ;
|
||||
#program-options-example regex <lib>../../regex/build/boost_regex ;
|
||||
15
example/Jamfile.v2
Normal file
15
example/Jamfile.v2
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
project
|
||||
: requirements <library>../build//program_options
|
||||
<hardcode-dll-paths>true
|
||||
;
|
||||
|
||||
exe first : first.cpp ;
|
||||
exe options_description : options_description.cpp ;
|
||||
exe multiple_sources : multiple_sources.cpp ;
|
||||
exe custom_syntax : custom_syntax.cpp ;
|
||||
|
||||
exe a : a.cpp ;
|
||||
|
||||
#exe real : real.cpp ;
|
||||
#exe regex : regex.cpp /boost/regex//boost_regex ;
|
||||
63
example/custom_syntax.cpp
Normal file
63
example/custom_syntax.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
/** This example shows how to support custom options syntax.
|
||||
|
||||
It's possible to install 'custom_parser'. It will be invoked on all command
|
||||
line tokens and can return name/value pair, or nothing. If it returns
|
||||
nothing, usual processing will be done.
|
||||
*/
|
||||
|
||||
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
/* This custom option parse function recognize gcc-style
|
||||
option "-fbar" / "-fno-bar".
|
||||
*/
|
||||
pair<string, string> reg_foo(const string& s)
|
||||
{
|
||||
if (s.find("-f") == 0) {
|
||||
if (s.substr(2, 3) == "no-")
|
||||
return make_pair(s.substr(5), string("false"));
|
||||
else
|
||||
return make_pair(s.substr(2), string("true"));
|
||||
} else {
|
||||
return make_pair(string(), string());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int ac, char* av[])
|
||||
{
|
||||
try {
|
||||
options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "produce a help message")
|
||||
("foo", value<string>(), "just an option")
|
||||
;
|
||||
|
||||
variables_map vm;
|
||||
store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
|
||||
.run(), vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << desc;
|
||||
cout << "\nIn addition -ffoo and -fno-foo syntax are recognized.\n";
|
||||
}
|
||||
if (vm.count("foo")) {
|
||||
cout << "foo value with the value of "
|
||||
<< vm["foo"].as<string>() << "\n";
|
||||
}
|
||||
}
|
||||
catch(exception& e) {
|
||||
cout << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
51
example/first.cpp
Normal file
51
example/first.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
/* The simplest usage of the library.
|
||||
*/
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
namespace po = boost::program_options;
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
using namespace std;
|
||||
|
||||
int main(int ac, char* av[])
|
||||
{
|
||||
try {
|
||||
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
("compression", po::value<int>(), "set compression level")
|
||||
;
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||
po::notify(vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << desc << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vm.count("compression")) {
|
||||
cout << "Compression level was set to "
|
||||
<< vm["compression"].as<int>() << ".\n";
|
||||
} else {
|
||||
cout << "Compression level was not set.\n";
|
||||
}
|
||||
}
|
||||
catch(exception& e) {
|
||||
cerr << "error: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
catch(...) {
|
||||
cerr << "Exception of unknown type!\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
5
example/multiple_sources.cfg
Normal file
5
example/multiple_sources.cfg
Normal file
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Comment out this line to use hard-coded default value of 10
|
||||
#
|
||||
optimization = 1
|
||||
include-path = /opt
|
||||
109
example/multiple_sources.cpp
Normal file
109
example/multiple_sources.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
/* Shows how to use both command line and config file. */
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
namespace po = boost::program_options;
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
using namespace std;
|
||||
|
||||
// A helper function to simplify the main part.
|
||||
template<class T>
|
||||
ostream& operator<<(ostream& os, const vector<T>& v)
|
||||
{
|
||||
copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
int main(int ac, char* av[])
|
||||
{
|
||||
try {
|
||||
int opt;
|
||||
|
||||
// Declare a group of options that will be
|
||||
// allowed only on command line
|
||||
po::options_description generic("Generic options");
|
||||
generic.add_options()
|
||||
("version,v", "print version string")
|
||||
("help", "produce help message")
|
||||
;
|
||||
|
||||
// Declare a group of options that will be
|
||||
// allowed both on command line and in
|
||||
// config file
|
||||
po::options_description config("Configuration");
|
||||
config.add_options()
|
||||
("optimization", po::value<int>(&opt)->default_value(10),
|
||||
"optimization level")
|
||||
("include-path,I",
|
||||
po::value< vector<string> >()->composing(),
|
||||
"include path")
|
||||
;
|
||||
|
||||
// Hidden options, will be alled both on command line and
|
||||
// in config file, but will not be show to the user.
|
||||
po::options_description hidden("Hidden options");
|
||||
hidden.add_options()
|
||||
("input-file", po::value< vector<string> >(), "input file")
|
||||
;
|
||||
|
||||
|
||||
po::options_description cmdline_options;
|
||||
cmdline_options.add(generic).add(config).add(hidden);
|
||||
|
||||
po::options_description config_file_options;
|
||||
config_file_options.add(config).add(hidden);
|
||||
|
||||
po::options_description visible("Allowed options");
|
||||
visible.add(generic).add(config);
|
||||
|
||||
po::positional_options_description p;
|
||||
p.add("input-file", -1);
|
||||
|
||||
po::variables_map vm;
|
||||
store(po::command_line_parser(ac, av).
|
||||
options(cmdline_options).positional(p).run(), vm);
|
||||
|
||||
ifstream ifs("multiple_sources.cfg");
|
||||
store(parse_config_file(ifs, config_file_options), vm);
|
||||
notify(vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << visible << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vm.count("version")) {
|
||||
cout << "Multiple sources example, version 1.0\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vm.count("include-path"))
|
||||
{
|
||||
cout << "Include paths are: "
|
||||
<< vm["include-path"].as< vector<string> >() << "\n";
|
||||
}
|
||||
|
||||
if (vm.count("input-file"))
|
||||
{
|
||||
cout << "Input files are: "
|
||||
<< vm["input-file"].as< vector<string> >() << "\n";
|
||||
}
|
||||
|
||||
cout << "Optimization level is " << opt << "\n";
|
||||
}
|
||||
catch(exception& e)
|
||||
{
|
||||
cout << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
73
example/options_description.cpp
Normal file
73
example/options_description.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
using namespace boost;
|
||||
namespace po = boost::program_options;
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
using namespace std;
|
||||
|
||||
|
||||
// A helper function to simplify the main part.
|
||||
template<class T>
|
||||
ostream& operator<<(ostream& os, const vector<T>& v)
|
||||
{
|
||||
copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
|
||||
return os;
|
||||
}
|
||||
|
||||
int main(int ac, char* av[])
|
||||
{
|
||||
try {
|
||||
int opt;
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
("optimization", po::value<int>(&opt)->default_value(10),
|
||||
"optimization level")
|
||||
("include-path,I", po::value< vector<string> >(),
|
||||
"include path")
|
||||
("input-file", po::value< vector<string> >(), "input file")
|
||||
;
|
||||
|
||||
po::positional_options_description p;
|
||||
p.add("input-file", -1);
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::command_line_parser(ac, av).
|
||||
options(desc).positional(p).run(), vm);
|
||||
po::notify(vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << "Usage: options_description [options]\n";
|
||||
cout << desc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vm.count("include-path"))
|
||||
{
|
||||
cout << "Include paths are: "
|
||||
<< vm["include-path"].as< vector<string> >() << "\n";
|
||||
}
|
||||
|
||||
if (vm.count("input-file"))
|
||||
{
|
||||
cout << "Input files are: "
|
||||
<< vm["input-file"].as< vector<string> >() << "\n";
|
||||
}
|
||||
|
||||
cout << "Optimization level is " << opt << "\n";
|
||||
}
|
||||
catch(exception& e)
|
||||
{
|
||||
cout << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
122
example/real.cpp
Normal file
122
example/real.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
/* Auxilliary functions for checking input for validity. */
|
||||
|
||||
/* Function used to check that 'opt1' and 'opt2' are not specified
|
||||
at the same time. */
|
||||
void conflicting_options(const variables_map& vm,
|
||||
const char* opt1, const char* opt2)
|
||||
{
|
||||
if (vm.count(opt1) && !vm[opt1].defaulted()
|
||||
&& vm.count(opt2) && !vm[opt2].defaulted())
|
||||
throw logic_error(string("Conflicting options '")
|
||||
+ opt1 + "' and '" + opt2 + "'");
|
||||
}
|
||||
|
||||
/* Function used to check that of 'for_what' is specified, then
|
||||
'required_option' is specified too. */
|
||||
void option_dependency(const variables_map& vm,
|
||||
const char* for_what, const char* required_option)
|
||||
{
|
||||
if (vm.count(for_what) && !vm[for_what].defaulted())
|
||||
if (vm.count(required_option) == 0 || vm[required_option].defaulted())
|
||||
throw logic_error(string("Option '") + for_what
|
||||
+ "' requires option '" + required_option + "'");
|
||||
}
|
||||
|
||||
/* Custom class for describing option. Allows to specify that the option is
|
||||
'internal'.
|
||||
*/
|
||||
class custom_description : public option_description_easy_init<custom_description> {
|
||||
public:
|
||||
custom_description() : m_internal(false) {}
|
||||
void internal() { m_internal = true ; }
|
||||
bool is_internal() const { return m_internal; }
|
||||
|
||||
private:
|
||||
bool m_internal;
|
||||
};
|
||||
|
||||
|
||||
/* Custom function for formatting one option. Does not print description for
|
||||
internal options, and prints description on a separate line for all others. */
|
||||
void format_option(ostream& os, const option_description& desc)
|
||||
{
|
||||
os << " " << desc.format_name() << " " << desc.format_parameter();
|
||||
const custom_description* d;
|
||||
if ((d = dynamic_cast<const custom_description*>(&desc)) && d->is_internal())
|
||||
os << " (internal)\n";
|
||||
else
|
||||
os << "\n " << desc.description() << "\n";
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try {
|
||||
string ofile;
|
||||
string macrofile, libmakfile;
|
||||
bool t_given = false;
|
||||
bool b_given = false;
|
||||
string mainpackage;
|
||||
string depends = "deps_file";
|
||||
string sources = "src_file";
|
||||
string root = ".";
|
||||
|
||||
options_description desc("Allowed options");
|
||||
desc.add_options<custom_description>()
|
||||
// First parameter describes option name/short name
|
||||
// The second is parameter to option
|
||||
// The third is description
|
||||
("help,h", "", "print usage message")
|
||||
("output,o", value("<pathname>", &ofile), "pathname for output")
|
||||
("macrofile,m", value("<macrofile>", ¯ofile), "full pathname of macro.h")
|
||||
("two,t", value("", &t_given), "preprocess both header and body")
|
||||
("body,b", value("", &b_given), "preprocess body in the header context")
|
||||
("libmakfile,l", value("<pathname>", &libmakfile), "write include makefile for library")
|
||||
("mainpackage,p", value("<name>", &mainpackage), "output dependency information")
|
||||
("depends,d", value("<pathname>", &depends), "write dependendies to <pathname>")
|
||||
("sources,s", value("<pathname>", &sources), "write source package list to <pathname>")
|
||||
("root,r", value("<pathname>", &root), "treat <dirname> as project root directory")
|
||||
("foo", "", "").default_value("10").internal()
|
||||
;
|
||||
|
||||
options_and_arguments oa = parse_command_line(argc, argv, desc);
|
||||
variables_map vm;
|
||||
store(oa, vm, desc);
|
||||
|
||||
if (vm.count("help")) {
|
||||
desc.output(cout, format_option);
|
||||
cout << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
conflicting_options(vm, "output", "two");
|
||||
conflicting_options(vm, "output", "body");
|
||||
conflicting_options(vm, "output", "mainpackage");
|
||||
conflicting_options(vm, "two", "mainpackage");
|
||||
conflicting_options(vm, "body", "mainpackage");
|
||||
|
||||
conflicting_options(vm, "two", "body");
|
||||
conflicting_options(vm, "libmakfile", "mainpackage");
|
||||
conflicting_options(vm, "libmakfile", "mainpackage");
|
||||
|
||||
option_dependency(vm, "depends", "mainpackage");
|
||||
option_dependency(vm, "sources", "mainpackage");
|
||||
option_dependency(vm, "root", "mainpackage");
|
||||
|
||||
cout << "two = " << vm["two"].as<bool>() << "\n";
|
||||
}
|
||||
catch(exception& e) {
|
||||
cerr << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
75
example/regex.cpp
Normal file
75
example/regex.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
using namespace boost;
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
/* This validator makes sure that value is of form
|
||||
XXX-XXX
|
||||
where X are digits. It then converts the second group to a integer
|
||||
value. This has no practical meaning, meant only to show how
|
||||
regex can be used to validate values.
|
||||
*/
|
||||
void fancy_parameter_validator(boost::any& a,
|
||||
const std::vector<std::string>& values)
|
||||
{
|
||||
static regex r("\\d\\d\\d-(\\d\\d\\d)");
|
||||
|
||||
// Make sure no previous assignment to 'a' was made.
|
||||
validators::check_first_occurence(a);
|
||||
// Extract the first string from 'values'. If there is more than
|
||||
// one string, it's an error, and exception will be thrown.
|
||||
const string& s = validators::get_single_string(values);
|
||||
|
||||
// Do regex match and convert the interesting part to
|
||||
// int.
|
||||
smatch match;
|
||||
if (regex_match(s, match, r)) {
|
||||
a = any(lexical_cast<int>(match[1]));
|
||||
} else {
|
||||
throw validation_error("invalid value");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int ac, const char **av)
|
||||
{
|
||||
try {
|
||||
options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "", "produce a help screen")
|
||||
("version,v", "", "print the version number")
|
||||
("magic,m", "arg", "magic value (in NNN-NNN format)").
|
||||
validator(fancy_parameter_validator)
|
||||
;
|
||||
|
||||
options_and_arguments oa = parse_command_line(ac, av, desc);
|
||||
variables_map vm;
|
||||
store(oa, vm, desc);
|
||||
|
||||
if (oa.count("help")) {
|
||||
cout << "Usage: regex [options]\n";
|
||||
cout << desc;
|
||||
return 0;
|
||||
}
|
||||
if (oa.count("version")) {
|
||||
cout << "Version 1.\n";
|
||||
return 0;
|
||||
}
|
||||
if (oa.count("magic")) {
|
||||
cout << "The magic is \"" << vm["magic"].as<int>() << "\"\n";
|
||||
}
|
||||
}
|
||||
catch(exception& e)
|
||||
{
|
||||
cout << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
24
include/boost/program_options.hpp
Normal file
24
include/boost/program_options.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright Vladimir Prus 2002. Permission to copy, use,
|
||||
// modify, sell and distribute this software is granted provided this
|
||||
// copyright notice appears in all copies. This software is provided
|
||||
// "as is" without express or implied warranty, and with no claim as
|
||||
// to its suitability for any purpose.
|
||||
|
||||
#ifndef PROGRAM_OPTIONS_VP_2003_05_19
|
||||
#define PROGRAM_OPTIONS_VP_2003_05_19
|
||||
|
||||
#if _MSC_VER >= 1020
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/positional_options.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include <boost/program_options/cmdline.hpp>
|
||||
#include <boost/program_options/errors.hpp>
|
||||
#include <boost/program_options/option.hpp>
|
||||
#include <boost/program_options/value_semantic.hpp>
|
||||
#include <boost/program_options/version.hpp>
|
||||
|
||||
#endif
|
||||
85
include/boost/program_options/cmdline.hpp
Normal file
85
include/boost/program_options/cmdline.hpp
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#ifndef CMDLINE_HPP_VP_2004_03_13
|
||||
#define CMDLINE_HPP_VP_2004_03_13
|
||||
|
||||
namespace boost { namespace program_options { namespace command_line_style {
|
||||
/** Various possible styles of options.
|
||||
|
||||
There are "long" options, which start with "--" and "short",
|
||||
which start with either "-" or "/". Both kinds can be allowed or
|
||||
disallowed, see allow_long and allow_short. The allowed character
|
||||
for short option is also configurable.
|
||||
|
||||
Option's value can be specified in the same token as value
|
||||
("--foo=bar"), or in the next token.
|
||||
|
||||
It's possible to introduce long option by the same character as
|
||||
long option, see allow_long_disguise.
|
||||
|
||||
Finally, guessing (specifying only prefix of option) and case
|
||||
insensitive processing are supported.
|
||||
*/
|
||||
enum style_t {
|
||||
/// Allow "--long_name" style
|
||||
allow_long = 1,
|
||||
/// Alow "-<single character" style
|
||||
allow_short = allow_long << 1,
|
||||
/// Allow "-" in short options
|
||||
allow_dash_for_short = allow_short << 1,
|
||||
/// Allow "/" in short options
|
||||
allow_slash_for_short = allow_dash_for_short << 1,
|
||||
/** Allow option parameter in the same token
|
||||
for long option, like in
|
||||
@verbatim
|
||||
--foo=10
|
||||
@endverbatim
|
||||
*/
|
||||
long_allow_adjacent = allow_slash_for_short << 1,
|
||||
/** Allow option parameter in the same token for
|
||||
long options. */
|
||||
long_allow_next = long_allow_adjacent << 1,
|
||||
/** Allow option parameter in the same token for
|
||||
short options. */
|
||||
short_allow_adjacent = long_allow_next << 1,
|
||||
/** Allow option parameter in the next token for
|
||||
short options. */
|
||||
short_allow_next = short_allow_adjacent << 1,
|
||||
/** Allow to merge several short options together,
|
||||
so that "-s -k" become "-sk". All of the options
|
||||
but last should accept no parameter. For example, if
|
||||
"-s" accept a parameter, then "k" will be taken as
|
||||
parameter, not another short option.
|
||||
Dos-style short options cannot be sticky.
|
||||
*/
|
||||
allow_sticky = short_allow_next << 1,
|
||||
/** Allow abbreviated spellings for long options,
|
||||
if they unambiguously identify long option.
|
||||
No long option name should be prefix of other
|
||||
long option name is guessing is in effect.
|
||||
*/
|
||||
allow_guessing = allow_sticky << 1,
|
||||
/** Ignore the difference in case for options.
|
||||
@todo Should this apply to long options only?
|
||||
*/
|
||||
case_insentitive = allow_guessing << 1,
|
||||
/** Allow long options with single option starting character,
|
||||
e.g <tt>-foo=10</tt>
|
||||
*/
|
||||
allow_long_disguise = case_insentitive << 1,
|
||||
/** The more-or-less traditional unix style. */
|
||||
unix_style = (allow_short | short_allow_adjacent | short_allow_next
|
||||
| allow_long | long_allow_adjacent | long_allow_next
|
||||
| allow_sticky | allow_guessing
|
||||
| allow_dash_for_short),
|
||||
/** The default style. */
|
||||
default_style = unix_style
|
||||
};
|
||||
}}}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
50
include/boost/program_options/config.hpp
Normal file
50
include/boost/program_options/config.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2004 Hartmut Kaiser
|
||||
//
|
||||
// Use, modification and distribution is subject to 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)
|
||||
|
||||
#ifndef PROGRAM_OPTIONS_CONFIG_HK_2004_01_11
|
||||
#define PROGRAM_OPTIONS_CONFIG_HK_2004_01_11
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
// Support for autolinking.
|
||||
#if BOOST_VERSION >= 103100 // works beginning from Boost V1.31.0
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// enable automatic library variant selection
|
||||
#if !defined(BOOST_PROGRAM_OPTIONS_SOURCE) && !defined(BOOST_ALL_NO_LIB) && \
|
||||
!defined(BOOST_PROGRAM_OPTIONS_NO_LIB)
|
||||
|
||||
// Set the name of our library, this will get undef'ed by auto_link.hpp
|
||||
// once it's done with it:
|
||||
#define BOOST_LIB_NAME boost_program_options
|
||||
|
||||
// And include the header that does the work:
|
||||
#include <boost/config/auto_link.hpp>
|
||||
|
||||
#endif // auto-linking disabled
|
||||
|
||||
#endif // BOOST_VERSION
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Windows DLL suport
|
||||
#ifdef BOOST_HAS_DECLSPEC
|
||||
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_PROGRAM_OPTIONS_DYN_LINK)
|
||||
// export if this is our own source, otherwise import:
|
||||
#ifdef BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
# define BOOST_PROGRAM_OPTIONS_DECL __declspec(dllexport)
|
||||
#else
|
||||
# define BOOST_PROGRAM_OPTIONS_DECL __declspec(dllimport)
|
||||
#endif // BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#endif // DYN_LINK
|
||||
#endif // BOOST_HAS_DECLSPEC
|
||||
|
||||
#ifndef BOOST_PROGRAM_OPTIONS_DECL
|
||||
#define BOOST_PROGRAM_OPTIONS_DECL
|
||||
#endif
|
||||
|
||||
|
||||
#endif // PROGRAM_OPTIONS_CONFIG_HK_2004_01_11
|
||||
|
||||
272
include/boost/program_options/detail/cmdline.hpp
Normal file
272
include/boost/program_options/detail/cmdline.hpp
Normal file
@@ -0,0 +1,272 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
|
||||
#ifndef CMDLINE_VP_2003_05_19
|
||||
#define CMDLINE_VP_2003_05_19
|
||||
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/errors.hpp>
|
||||
#include <boost/program_options/cmdline.hpp>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
|
||||
/** Command line parser class. Main requirements were:
|
||||
- Powerfull enough to support all common uses.
|
||||
- Simple and easy to learn/use.
|
||||
- Minimal code size and extrernal dependencies.
|
||||
- Extensible for custom syntaxes.
|
||||
|
||||
First all options are registered. After that, elements of command line
|
||||
are extracted using operator++.
|
||||
|
||||
For each element, user can find
|
||||
- if it's an option or an argument
|
||||
- name of the option
|
||||
- index of the option
|
||||
- option value(s), if any
|
||||
|
||||
Sometimes the registered option name is not equal to the encountered
|
||||
one, for example, because name abbreviation is supported. Therefore
|
||||
two option names can be obtained:
|
||||
- the registered one
|
||||
- the one found at the command line
|
||||
|
||||
There are lot of style options, which can be used to tune the command
|
||||
line parsing. In addition, it's possible to install additional parser
|
||||
which will process custom option styles.
|
||||
|
||||
@todo mininal match length for guessing?
|
||||
*/
|
||||
class cmdline {
|
||||
public:
|
||||
|
||||
typedef ::boost::program_options::command_line_style::style_t style_t;
|
||||
|
||||
typedef function1<std::pair<std::string, std::string>,
|
||||
const std::string&>
|
||||
additional_parser;
|
||||
|
||||
/** Constructs a command line parser for (argc, argv) pair. Uses
|
||||
style options passed in 'style', which should be binary or'ed values
|
||||
of style_t enum. It can also be zero, in which case a "default"
|
||||
style will be used. If 'allow_unregistered' is true, then allows
|
||||
unregistered options. They will be assigned index 1 and are
|
||||
assumed to have optional parameter.
|
||||
*/
|
||||
cmdline(const std::vector<std::string>& args, int style,
|
||||
bool allow_unregistered = false);
|
||||
|
||||
/** @overload */
|
||||
cmdline(int argc, const char*const * argv, int style,
|
||||
bool allow_unregistered = false);
|
||||
|
||||
/** Set additional parser. This will be called for each token
|
||||
of command line. If first string in pair is not empty,
|
||||
then the token is considered matched by this parser,
|
||||
and the first string will be considered an option name
|
||||
(which can be long or short), while the second will be
|
||||
option's parameter (if not empty).
|
||||
Note that additional parser can match only one token.
|
||||
*/
|
||||
void set_additional_parser(additional_parser p);
|
||||
|
||||
/** Registers a new option.
|
||||
@param long_name Long name to use. When ending '*', symbols up to
|
||||
it give an allowed prefix -- all options starting with it will be
|
||||
allowed. The first character may not be '-'. Empty string means no
|
||||
long name.
|
||||
@param short_name Short name to use. Value of '\0' means no short
|
||||
name.
|
||||
@param properties Tell about possible parameters
|
||||
'|' -- no parameter
|
||||
'?' -- optional parameter
|
||||
':' -- required parameter
|
||||
'*' -- 0 or more parameters
|
||||
'+' -- 1 or more parameters
|
||||
@param index A distinguishing value for the option.
|
||||
The index will be returned by 'option_index' member function. Indices
|
||||
need not be unqiue -- e.g. client can set all indices to 0, and
|
||||
use string value to recognize options.
|
||||
*/
|
||||
void add_option(const std::string& long_name, char short_name,
|
||||
char properties = '|', int index = 0);
|
||||
|
||||
/** @overload */
|
||||
void add_option(const char* long_name, char short_name,
|
||||
char properties = '|', int index = 0);
|
||||
|
||||
|
||||
/** Returns false when nothing more can be extracted */
|
||||
operator bool() const;
|
||||
|
||||
/** Advances to the next element on command line.
|
||||
When called for the first time, positions on the first element. */
|
||||
cmdline& operator++();
|
||||
|
||||
/// Tells if the current element is option.
|
||||
bool at_option() const;
|
||||
|
||||
/// Tells if the current element is argument.
|
||||
bool at_argument() const;
|
||||
|
||||
/** Returns the option name. If there's long option name associated with
|
||||
this option, it is returned, even if short name was used in command line.
|
||||
Otherwise, the short name given to 'add_option' is returned with '-' prepended.
|
||||
For purposes of simplicity, '-' is used even when dos-style short option
|
||||
was found.
|
||||
*/
|
||||
const std::string& option_name() const;
|
||||
/** Returns the index for the current option. */
|
||||
int option_index() const;
|
||||
/** Returns the option name as found on the command line. Any symbols
|
||||
that introduce the option, or delimit its parameter will be
|
||||
stripped. This function allows to work with allowed prefixes, in
|
||||
which case 'option_name' will return the prefix specification, and
|
||||
full option name should be queried explicitly.
|
||||
*/
|
||||
const std::string& raw_option_name() const;
|
||||
/** Returns the first of option values. If there's more than one option
|
||||
throws multiple_values. If there are no options, returns empty
|
||||
string. */
|
||||
const std::string& option_value() const;
|
||||
/** Returns all option values. */
|
||||
const std::vector<std::string>& option_values() const;
|
||||
/** Returns the argument. */
|
||||
const std::string& argument() const;
|
||||
|
||||
/** Returns all arguments read by this command line parser. */
|
||||
const std::vector<std::string>& arguments() const;
|
||||
|
||||
/** Returns the token that was current by the time 'operator++' was
|
||||
invoked the last time. */
|
||||
const std::string& last() const;
|
||||
|
||||
private:
|
||||
|
||||
// Properties of an option.
|
||||
enum properties_t {
|
||||
no_parameter = 1,
|
||||
/// 0 or 1 parameter
|
||||
allow_parameter,
|
||||
/// exactly 1 parameter
|
||||
require_parameter,
|
||||
/// 0 or more parameters
|
||||
allow_parameters,
|
||||
/// 1 or more parameters
|
||||
require_parameters,
|
||||
};
|
||||
|
||||
enum element_kind_t {
|
||||
ek_option = 0,
|
||||
ek_argument
|
||||
};
|
||||
|
||||
// General error status.
|
||||
enum error_type_t {
|
||||
no_error = 0,
|
||||
unknown_option,
|
||||
ambiguous_option,
|
||||
invalid_syntax,
|
||||
};
|
||||
|
||||
// Detailed error status.
|
||||
enum error_description_t {
|
||||
ed_success = 0,
|
||||
ed_long_not_allowed,
|
||||
ed_long_adjacent_not_allowed,
|
||||
ed_short_adjacent_not_allowed,
|
||||
ed_empty_adjacent_parameter,
|
||||
ed_missing_parameter,
|
||||
ed_extra_parameter,
|
||||
ed_unknown_option,
|
||||
ed_ambiguous_option,
|
||||
};
|
||||
|
||||
struct option {
|
||||
option(const std::string& long_name, char short_name,
|
||||
properties_t properties, int index)
|
||||
: long_name(long_name), short_name(short_name), index(index),
|
||||
properties(properties)
|
||||
{}
|
||||
|
||||
std::string long_name;
|
||||
char short_name;
|
||||
int index;
|
||||
properties_t properties;
|
||||
};
|
||||
std::vector<option> options;
|
||||
|
||||
void init(const std::vector<std::string>& args, int style,
|
||||
bool allow_unregistered);
|
||||
void check_style(int style) const;
|
||||
|
||||
void next();
|
||||
|
||||
const option* find_long_option(const char* name);
|
||||
const option* find_short_option(char name);
|
||||
|
||||
enum option_kind { error_option, no_option, long_option, short_option,
|
||||
dos_option };
|
||||
option_kind is_option(const char* s);
|
||||
// All handle_* member functions take string without any "--" or "-" or "/"
|
||||
// They return true and success and set m_num_tokens to the number of
|
||||
// tokens that were consumed.
|
||||
bool handle_long_option(const char* s);
|
||||
bool handle_short_option(const char* s, bool ignore_sticky = false);
|
||||
bool handle_dos_option(const char* s);
|
||||
// Attempts to apply additional parser to 's'.
|
||||
bool handle_additional_parser(const std::pair<std::string, std::string>& p);
|
||||
|
||||
bool process_parameter(const option* opt, bool adjacent_parameter,
|
||||
bool next_parameter);
|
||||
void advance(int count);
|
||||
|
||||
/// Converts parameter property character into enum value.
|
||||
properties_t translate_property(char p);
|
||||
|
||||
/** Clears all error state. If there were an error, throws appripriate
|
||||
exception. */
|
||||
void clear_error();
|
||||
|
||||
// Copies of input.
|
||||
std::vector<std::string> args;
|
||||
style_t style;
|
||||
bool allow_unregistered;
|
||||
|
||||
// Current state of parsing.
|
||||
unsigned index;
|
||||
const char* m_current;
|
||||
const char* m_next;
|
||||
const char* pending_short_option;
|
||||
bool m_no_more_options;
|
||||
error_description_t m_error_description;
|
||||
element_kind_t m_element_kind;
|
||||
int m_option_index;
|
||||
|
||||
// Results of parsing the last option
|
||||
std::string m_last;
|
||||
const option* m_opt;
|
||||
std::string m_option_name, m_raw_option_name, m_argument;
|
||||
std::vector<std::string> m_option_values;
|
||||
int m_num_tokens;
|
||||
bool m_disguised_long;
|
||||
|
||||
std::vector<std::string> m_arguments;
|
||||
|
||||
additional_parser m_additional_parser;
|
||||
};
|
||||
|
||||
void test_cmdline_detail();
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
|
||||
149
include/boost/program_options/detail/config_file.hpp
Normal file
149
include/boost/program_options/detail/config_file.hpp
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
|
||||
#ifndef CONFIG_FILE_VP_2003_01_02
|
||||
#define CONFIG_FILE_VP_2003_01_02
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/option.hpp>
|
||||
#include <boost/program_options/eof_iterator.hpp>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
|
||||
/** Standalone parser for config files in ini-line format.
|
||||
The parser is a model of single-pass lvalue iterator, and
|
||||
default constructor creates past-the-end-iterator. The typical usage is:
|
||||
config_file_iterator i(is, ... set of options ...), e;
|
||||
for(; i !=e; ++i) {
|
||||
*i;
|
||||
}
|
||||
|
||||
Syntax conventions:
|
||||
|
||||
- config file can not contain positional options
|
||||
- '#' is comment character: it is ignored together with
|
||||
the rest of the line.
|
||||
- variable assignments are in the form
|
||||
name '=' value.
|
||||
spaces around '=' are trimmed.
|
||||
- Section names are given in brackets.
|
||||
|
||||
The actuall option name is constructed by combining current section
|
||||
name and specified option name, with dot between. If section_name
|
||||
already contains dot at the end, new dot is not inserted. For example:
|
||||
@verbatim
|
||||
[gui.accessibility]
|
||||
visual_bell=yes
|
||||
@endverbatim
|
||||
will result in option "gui.accessibility.visual_bell" with value
|
||||
"yes" been returned.
|
||||
|
||||
TODO: maybe, we should just accept a pointer to options_description
|
||||
class.
|
||||
*/
|
||||
class common_config_file_iterator
|
||||
: public eof_iterator<common_config_file_iterator, option>
|
||||
{
|
||||
public:
|
||||
common_config_file_iterator() { found_eof(); }
|
||||
common_config_file_iterator(
|
||||
const std::set<std::string>& allowed_options);
|
||||
|
||||
virtual ~common_config_file_iterator() {}
|
||||
|
||||
public: // Method required by eof_iterator
|
||||
|
||||
void get();
|
||||
|
||||
protected: // Stubs for derived classes
|
||||
|
||||
// Obtains next line from the config file
|
||||
// Note: really, this design is a bit ugly
|
||||
// The most clean thing would be to pass 'line_iterator' to
|
||||
// constructor of this class, but to avoid templating this class
|
||||
// we'd need polymorphic iterator, which does not exist yet.
|
||||
virtual bool getline(std::string&) { return false; }
|
||||
|
||||
private:
|
||||
/** Adds another allowed option. If the 'name' ends with
|
||||
'*', then all options with the same prefix are
|
||||
allowed. For example, if 'name' is 'foo*', then 'foo1' and
|
||||
'foo_bar' are allowed. */
|
||||
void add_option(const char* name);
|
||||
|
||||
// Returns true if 's' is a registered option name.
|
||||
bool allowed_option(const std::string& s) const;
|
||||
|
||||
// That's probably too much data for iterator, since
|
||||
// it will be copied, but let's not bother for now.
|
||||
std::set<std::string> allowed_options;
|
||||
// Invariant: no element is prefix of other element.
|
||||
std::set<std::string> allowed_prefixes;
|
||||
std::string m_prefix;
|
||||
};
|
||||
|
||||
template<class charT>
|
||||
class basic_config_file_iterator : public common_config_file_iterator {
|
||||
public:
|
||||
basic_config_file_iterator()
|
||||
{
|
||||
found_eof();
|
||||
}
|
||||
|
||||
/** Creates a config file parser for the specified stream.
|
||||
*/
|
||||
basic_config_file_iterator(std::basic_istream<charT>& is,
|
||||
const std::set<std::string>& allowed_options);
|
||||
|
||||
private: // base overrides
|
||||
|
||||
bool getline(std::string&);
|
||||
|
||||
private: // internal data
|
||||
shared_ptr<std::basic_istream<charT> > is;
|
||||
};
|
||||
|
||||
typedef basic_config_file_iterator<char> config_file_iterator;
|
||||
typedef basic_config_file_iterator<wchar_t> wconfig_file_iterator;
|
||||
|
||||
|
||||
struct null_deleter
|
||||
{
|
||||
void operator()(void const *) const {}
|
||||
};
|
||||
|
||||
|
||||
template<class charT>
|
||||
basic_config_file_iterator<charT>::
|
||||
basic_config_file_iterator(std::basic_istream<charT>& is,
|
||||
const std::set<std::string>& allowed_options)
|
||||
: common_config_file_iterator(allowed_options)
|
||||
{
|
||||
this->is.reset(&is, null_deleter());
|
||||
get();
|
||||
}
|
||||
|
||||
template<class charT>
|
||||
bool
|
||||
basic_config_file_iterator<charT>::getline(std::string& s)
|
||||
{
|
||||
return std::getline(*is, s);
|
||||
}
|
||||
|
||||
template<>
|
||||
bool
|
||||
basic_config_file_iterator<wchar_t>::getline(std::string& s);
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
52
include/boost/program_options/detail/convert.hpp
Normal file
52
include/boost/program_options/detail/convert.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#ifndef BOOST_CONVERT_HPP_VP_2004_04_28
|
||||
#define BOOST_CONVERT_HPP_VP_2004_04_28
|
||||
|
||||
#include <string>
|
||||
#include <locale>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
|
||||
/** Converts from local 8 bit encoding into wchar_t string using
|
||||
the specified locale facet. */
|
||||
std::wstring
|
||||
from_8_bit(const std::string& s,
|
||||
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt);
|
||||
|
||||
/** Converts from wchar_t string into local 8 bit encoding into using
|
||||
the specified locale facet. */
|
||||
std::string
|
||||
to_8_bit(const std::wstring& s,
|
||||
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt);
|
||||
|
||||
|
||||
/** Converts 's', which is assumed to be in UTF8 encoding, into wide
|
||||
string. */
|
||||
std::wstring
|
||||
from_utf8(const std::string& s);
|
||||
|
||||
/** Converts wide string 's' into string in UTF8 encoding. */
|
||||
std::string
|
||||
to_utf8(const std::wstring& s);
|
||||
|
||||
/** Converts wide string 's' into local 8 bit encoding determined by
|
||||
the current locale. */
|
||||
std::string
|
||||
to_local_8_bit(const std::wstring& s);
|
||||
|
||||
/** Converts 's', which is assumed to be in local 8 bit encoding, into wide
|
||||
string. */
|
||||
std::wstring
|
||||
from_local_8_bit(const std::string& s);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
58
include/boost/program_options/detail/parsers.hpp
Normal file
58
include/boost/program_options/detail/parsers.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#ifndef BOOST_PARSERS_HPP_VP_2004_05_06
|
||||
#define BOOST_PARSERS_HPP_VP_2004_05_06
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
template<class charT>
|
||||
basic_command_line_parser<charT>&
|
||||
basic_command_line_parser<charT>::options(const options_description& desc)
|
||||
{
|
||||
m_desc = &desc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class charT>
|
||||
basic_command_line_parser<charT>&
|
||||
basic_command_line_parser<charT>::positional(
|
||||
const positional_options_description& desc)
|
||||
{
|
||||
m_positional = &desc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class charT>
|
||||
basic_command_line_parser<charT>&
|
||||
basic_command_line_parser<charT>::style(int style)
|
||||
{
|
||||
m_style = style;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class charT>
|
||||
basic_command_line_parser<charT>&
|
||||
basic_command_line_parser<charT>::extra_parser(ext_parser ext)
|
||||
{
|
||||
m_ext = ext;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class charT>
|
||||
basic_parsed_options<charT>
|
||||
parse_command_line(int argc, charT* argv[],
|
||||
const options_description& desc,
|
||||
int style = 0,
|
||||
function1<std::pair<std::string, std::string>,
|
||||
const std::string&> ext)
|
||||
{
|
||||
return basic_command_line_parser<charT>(argc, argv).options(desc).
|
||||
style(style).extra_parser(ext).run();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
202
include/boost/program_options/detail/utf8_codecvt_facet.hpp
Normal file
202
include/boost/program_options/detail/utf8_codecvt_facet.hpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#ifndef BOOST_UTF8_CODECVT_FACET_HPP
|
||||
#define BOOST_UTF8_CODECVT_FACET_HPP
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// utf8_codecvt_facet.hpp
|
||||
|
||||
// Copyright © 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
|
||||
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu). Permission to copy,
|
||||
// use, modify, sell and distribute this software is granted provided this
|
||||
// copyright notice appears in all copies. This software is provided "as is"
|
||||
// without express or implied warranty, and with no claim as to its suitability
|
||||
// for any purpose.
|
||||
|
||||
// Note:(Robert Ramey). I have made the following alterations in the original
|
||||
// code.
|
||||
// a) Rendered utf8_codecvt<wchar_t, char> with using templates
|
||||
// b) Move longer functions outside class definition to prevent inlining
|
||||
// and make code smaller
|
||||
// c) added on a derived class to permit translation to/from current
|
||||
// locale to utf8
|
||||
|
||||
// See http://www.boost.org for updates, documentation, and revision history.
|
||||
|
||||
// archives stored as text - note these ar templated on the basic
|
||||
// stream templates to accommodate wide (and other?) kind of characters
|
||||
//
|
||||
// note the fact that on libraries without wide characters, ostream is
|
||||
// is not a specialization of basic_ostream which in fact is not defined
|
||||
// in such cases. So we can't use basic_ostream<OStream::char_type> but rather
|
||||
// use two template parameters
|
||||
//
|
||||
// utf8_codecvt_facet
|
||||
// This is an implementation of a std::codecvt facet for translating
|
||||
// from UTF-8 externally to UCS-4. Note that this is not tied to
|
||||
// any specific types in order to allow customization on platforms
|
||||
// where wchar_t is not big enough.
|
||||
//
|
||||
// NOTES: The current implementation jumps through some unpleasant hoops in
|
||||
// order to deal with signed character types. As a std::codecvt_base::result,
|
||||
// it is necessary for the ExternType to be convertible to unsigned char.
|
||||
// I chose not to tie the extern_type explicitly to char. But if any combination
|
||||
// of types other than <wchar_t,char_t> is used, then std::codecvt must be
|
||||
// specialized on those types for this to work.
|
||||
|
||||
#include <locale>
|
||||
|
||||
#include <boost/detail/workaround.hpp>
|
||||
|
||||
// mbstate_t not available in global namespace
|
||||
#if BOOST_WORKAROUND(__BORLANDC__,BOOST_TESTED_AT(0x564))
|
||||
using std::mbstate_t;
|
||||
#endif
|
||||
|
||||
// maximum lenght of a multibyte string
|
||||
#define MB_LENGTH_MAX 8
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
|
||||
struct utf8_codecvt_facet_wchar_t :
|
||||
public std::codecvt<wchar_t, char, mbstate_t>
|
||||
{
|
||||
public:
|
||||
explicit utf8_codecvt_facet_wchar_t(size_t no_locale_manage=0)
|
||||
: std::codecvt<wchar_t, char, mbstate_t>(no_locale_manage)
|
||||
{}
|
||||
protected:
|
||||
virtual std::codecvt_base::result do_in(
|
||||
mbstate_t& state,
|
||||
const char * from,
|
||||
const char * from_end,
|
||||
const char * & from_next,
|
||||
wchar_t * to,
|
||||
wchar_t * to_end,
|
||||
wchar_t*& to_next
|
||||
) const;
|
||||
|
||||
virtual std::codecvt_base::result do_out(
|
||||
mbstate_t & state, const wchar_t * from,
|
||||
const wchar_t * from_end, const wchar_t* & from_next,
|
||||
char * to, char * to_end, char * & to_next
|
||||
) const;
|
||||
|
||||
bool invalid_continuing_octet(unsigned char octet_1) const {
|
||||
return (octet_1 < 0x80|| 0xbf< octet_1);
|
||||
}
|
||||
|
||||
bool invalid_leading_octet(unsigned char octet_1) const {
|
||||
return (0x7f < octet_1 && octet_1 < 0xc0) ||
|
||||
(octet_1 > 0xfd);
|
||||
}
|
||||
|
||||
// continuing octets = octets except for the leading octet
|
||||
static unsigned int get_cont_octet_count(unsigned char lead_octet) {
|
||||
return get_octet_count(lead_octet) - 1;
|
||||
}
|
||||
|
||||
static unsigned int get_octet_count(unsigned char lead_octet);
|
||||
|
||||
// How many "continuing octets" will be needed for this word
|
||||
// == total octets - 1.
|
||||
int get_cont_octet_out_count(wchar_t word) const ;
|
||||
|
||||
virtual bool do_always_noconv() const throw() { return false; }
|
||||
|
||||
// UTF-8 isn't really stateful since we rewind on partial conversions
|
||||
virtual std::codecvt_base::result do_unshift(
|
||||
mbstate_t&,
|
||||
char * from,
|
||||
char * to,
|
||||
char * & next
|
||||
) const
|
||||
{
|
||||
next = from;
|
||||
return ok;
|
||||
}
|
||||
|
||||
virtual int do_encoding() const throw() {
|
||||
const int variable_byte_external_encoding=0;
|
||||
return variable_byte_external_encoding;
|
||||
}
|
||||
|
||||
// How many char objects can I process to get <= max_limit
|
||||
// wchar_t objects?
|
||||
virtual int do_length(
|
||||
const mbstate_t &,
|
||||
const char * from,
|
||||
const char * from_end,
|
||||
size_t max_limit
|
||||
) const throw();;
|
||||
|
||||
// Largest possible value do_length(state,from,from_end,1) could return.
|
||||
virtual int do_max_length() const throw () {
|
||||
return 6; // largest UTF-8 encoding of a UCS-4 character
|
||||
}
|
||||
};
|
||||
|
||||
#if 0 // not used - incorrect in any case
|
||||
// Robert Ramey - use the above to make a code converter from multi-byte
|
||||
// char strings to utf8 encoding
|
||||
struct utf8_codecvt_facet_char : public utf8_codecvt_facet_wchar_t
|
||||
{
|
||||
typedef utf8_codecvt_facet_wchar_t base_class;
|
||||
public:
|
||||
explicit utf8_codecvt_facet_char(size_t no_locale_manage=0)
|
||||
: base_class(no_locale_manage)
|
||||
{}
|
||||
protected:
|
||||
virtual std::codecvt_base::result do_in(
|
||||
mbstate_t & state,
|
||||
const char * from,
|
||||
const char * from_end,
|
||||
const char * & from_next,
|
||||
char * to,
|
||||
char * to_end,
|
||||
char * & to_next
|
||||
) const;
|
||||
|
||||
virtual std::codecvt_base::result do_out(
|
||||
mbstate_t & state,
|
||||
const char * from,
|
||||
const char * from_end,
|
||||
const char* & from_next,
|
||||
char * to,
|
||||
char * to_end,
|
||||
char * & to_next
|
||||
) const;
|
||||
|
||||
// How many char objects can I process to get <= max_limit
|
||||
// char objects?
|
||||
virtual int do_length(
|
||||
const mbstate_t&,
|
||||
const char * from,
|
||||
const char * from_end,
|
||||
size_t max_limit
|
||||
) const;
|
||||
};
|
||||
#endif
|
||||
|
||||
template<class Internal, class External>
|
||||
struct utf8_codecvt_facet
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct utf8_codecvt_facet<wchar_t, char>
|
||||
: public utf8_codecvt_facet_wchar_t
|
||||
{};
|
||||
|
||||
#if 0
|
||||
template<>
|
||||
struct utf8_codecvt_facet<char, char>
|
||||
: public utf8_codecvt_facet_char
|
||||
{};
|
||||
#endif
|
||||
|
||||
}}}
|
||||
|
||||
#endif // BOOST_UTF8_CODECVT_FACET_HPP
|
||||
195
include/boost/program_options/detail/value_semantic.hpp
Normal file
195
include/boost/program_options/detail/value_semantic.hpp
Normal file
@@ -0,0 +1,195 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
// This file defines template functions that are declared in
|
||||
// ../value_semantic.hpp.
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
extern std::string arg;
|
||||
|
||||
template<class T, class charT>
|
||||
std::string
|
||||
typed_value<T, charT>::name() const
|
||||
{
|
||||
if (!m_default_value.empty() && !m_default_value_as_text.empty()) {
|
||||
return arg + " (=" + m_default_value_as_text + ")";
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class charT>
|
||||
void
|
||||
typed_value<T, charT>::notify(const boost::any& value_store) const
|
||||
{
|
||||
const T* value = boost::any_cast<const T>(&value_store);
|
||||
if (m_store_to) {
|
||||
*m_store_to = *value;
|
||||
}
|
||||
if (m_notifier) {
|
||||
m_notifier(*value);
|
||||
}
|
||||
}
|
||||
|
||||
namespace validators {
|
||||
/* If v.size() > 1, throw validation_error.
|
||||
If v.size() == 1, return v.front()
|
||||
Otherwise, returns a reference to a statically allocated
|
||||
empty string if 'allow_empty' and throws validation_error
|
||||
otherwise. */
|
||||
template<class charT>
|
||||
const std::basic_string<charT>& get_single_string(
|
||||
const std::vector<std::basic_string<charT> >& v,
|
||||
bool allow_empty = false)
|
||||
{
|
||||
static std::basic_string<charT> empty;
|
||||
if (v.size() > 1)
|
||||
throw validation_error("multiple values not allowed");
|
||||
if (v.size() == 1)
|
||||
return v.front();
|
||||
else if (allow_empty)
|
||||
return empty;
|
||||
else
|
||||
throw validation_error("at least one value required");
|
||||
}
|
||||
|
||||
/* Throws multiple_occurences if 'value' is not empty. */
|
||||
void check_first_occurence(const boost::any& value);
|
||||
}
|
||||
|
||||
using namespace validators;
|
||||
|
||||
/** Validates 's' and updates 'v'.
|
||||
@pre 'v' is either empty or in the state assigned by the previous
|
||||
invocation of 'validate'.
|
||||
Specializations are provided for bool, float, int, and string.
|
||||
*/
|
||||
template<class T, class charT>
|
||||
class validator {
|
||||
public:
|
||||
/// Method that does the job.
|
||||
void operator()(boost::any& v,
|
||||
const std::vector< std::basic_string<charT> >& xs)
|
||||
{
|
||||
validators::check_first_occurence(v);
|
||||
std::basic_string<charT> s(validators::get_single_string(xs));
|
||||
try {
|
||||
v = any(lexical_cast<T>(s));
|
||||
}
|
||||
catch(const bad_lexical_cast&) {
|
||||
throw validation_error("invalid option value");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Validates sequences. Allows multiple values per option occurence
|
||||
and multiple occurences. */
|
||||
template<class T, class charT>
|
||||
class validator< std::vector<T>, charT > {
|
||||
public:
|
||||
void operator()(boost::any& v,
|
||||
const std::vector<std::basic_string<charT> >& s);
|
||||
};
|
||||
|
||||
template<>
|
||||
void validator<bool, char>::operator()(boost::any& v,
|
||||
const std::vector<std::string>& xs);
|
||||
|
||||
template<>
|
||||
void validator<bool, wchar_t>::operator()(
|
||||
boost::any& v, const std::vector<std::wstring>& xs);
|
||||
|
||||
|
||||
// For some reason, this declaration, which is require by the standard,
|
||||
// cause gcc 3.2 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) ) \
|
||||
)
|
||||
template<>
|
||||
void validator<std::string, char>::operator()(
|
||||
boost::any& v,
|
||||
const std::vector<std::string>& xs);
|
||||
|
||||
template<>
|
||||
void validator<std::string, wchar_t>::operator()(
|
||||
boost::any& v,
|
||||
const std::vector<std::wstring>& xs);
|
||||
#endif
|
||||
|
||||
template<class T, class charT>
|
||||
void
|
||||
validator<std::vector<T>, charT>
|
||||
::operator()(boost::any& v,
|
||||
const std::vector<std::basic_string<charT> >& s)
|
||||
{
|
||||
if (v.empty()) {
|
||||
v = boost::any(std::vector<T>());
|
||||
}
|
||||
std::vector<T>* tv = boost::any_cast< std::vector<T> >(&v);
|
||||
assert(tv);
|
||||
for (size_t i = 0; i < s.size(); ++i)
|
||||
{
|
||||
try {
|
||||
tv->push_back(boost::lexical_cast<T>(s[i]));
|
||||
}
|
||||
catch(const bad_lexical_cast& /*e*/) {
|
||||
throw validation_error(std::string("value ").append(s[i]).
|
||||
append(" is invalid"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class T, class charT>
|
||||
void
|
||||
typed_value<T, charT>::
|
||||
parse(boost::any& value_store,
|
||||
const std::vector<std::basic_string<charT> >& new_tokens) const
|
||||
{
|
||||
// The only reason for using 'validator' class and
|
||||
// therefore adding a new level of indirection is that
|
||||
// we can't partically specialize 'parse' on vector<T>.
|
||||
boost::program_options::validator<T, charT> validator;
|
||||
validator(value_store, new_tokens);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typed_value<T>*
|
||||
value()
|
||||
{
|
||||
return value<T>(0);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typed_value<T>*
|
||||
value(T* v)
|
||||
{
|
||||
typed_value<T>* r = new typed_value<T>(v);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typed_value<T, wchar_t>*
|
||||
wvalue()
|
||||
{
|
||||
return wvalue<T>(0);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typed_value<T, wchar_t>*
|
||||
wvalue(T* v)
|
||||
{
|
||||
typed_value<T, wchar_t>* r = new typed_value<T, wchar_t>(v);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}}
|
||||
51
include/boost/program_options/environment_iterator.hpp
Normal file
51
include/boost/program_options/environment_iterator.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#ifndef BOOST_ENVIRONMENT_ITERATOR_VP_2004_05_14
|
||||
#define BOOST_ENVIRONMENT_ITERATOR_VP_2004_05_14
|
||||
|
||||
#include "eof_iterator.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
namespace boost {
|
||||
|
||||
class environment_iterator
|
||||
: public eof_iterator<environment_iterator,
|
||||
std::pair<std::string, std::string> >
|
||||
{
|
||||
public:
|
||||
environment_iterator(char** environment)
|
||||
: m_environment(environment)
|
||||
{
|
||||
get();
|
||||
}
|
||||
|
||||
environment_iterator()
|
||||
{
|
||||
found_eof();
|
||||
}
|
||||
|
||||
void get()
|
||||
{
|
||||
if (*m_environment == 0)
|
||||
found_eof();
|
||||
else {
|
||||
std::string s(*m_environment);
|
||||
std::string::size_type n = s.find('=');
|
||||
assert(n != s.npos);
|
||||
value().first = s.substr(0, n);
|
||||
value().second = s.substr(n+1);
|
||||
}
|
||||
++m_environment;
|
||||
}
|
||||
|
||||
private:
|
||||
char** m_environment;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
97
include/boost/program_options/eof_iterator.hpp
Normal file
97
include/boost/program_options/eof_iterator.hpp
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#ifndef EOF_ITERATOR_VP_2004_03_12
|
||||
#define EOF_ITERATOR_VP_2004_03_12
|
||||
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
|
||||
namespace boost {
|
||||
|
||||
/** The 'eof_iterator' class is usefull for constructing forward iterators
|
||||
in cases where iterator extract data from some source and it's easy
|
||||
to detect 'eof' -- i.e. the situation where there's no data. One
|
||||
apparent example is reading lines from a file.
|
||||
|
||||
Implementing such iterators using 'iterator_facade' directly would
|
||||
require to create class with three core operation, a couple of
|
||||
constructors. When using 'eof_iterator', the derived class should define
|
||||
only one method to get new value, plus a couple of constructors.
|
||||
|
||||
The basic idea is that iterator has 'eof' bit. Two iterators are equal
|
||||
only if both have their 'eof' bits set. The 'get' method either obtains
|
||||
the new value or sets the 'eof' bit.
|
||||
|
||||
Specifically, derived class should define:
|
||||
|
||||
1. A default constructor, which creates iterator with 'eof' bit set. The
|
||||
constructor body should call 'found_eof' method defined here.
|
||||
2. Some other constructor. It should initialize some 'data pointer' used
|
||||
in iterator operation and then call 'get'.
|
||||
3. The 'get' method. It should operate this way:
|
||||
- look at some 'data pointer' to see if new element is available;
|
||||
if not, it should call 'found_eof'.
|
||||
- extract new element and store it at location returned by the 'value'
|
||||
method.
|
||||
- advance the data pointer.
|
||||
|
||||
Essentially, the 'get' method has partly functionality of 'increment'
|
||||
and 'dereference'. It's very good for the cases where data extraction
|
||||
implicitly moves data pointer, like for stream operation.
|
||||
*/
|
||||
template<class Derived, class ValueType>
|
||||
class eof_iterator : public iterator_facade<Derived, const ValueType,
|
||||
forward_traversal_tag>
|
||||
{
|
||||
public:
|
||||
eof_iterator()
|
||||
: m_at_eof(false)
|
||||
{}
|
||||
|
||||
protected: // iterface for derived
|
||||
|
||||
/** Returns the reference which should be used by derived
|
||||
class to store the next value. */
|
||||
ValueType& value()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
/** Should be called by derived class to indicate that it can't
|
||||
produce next element. */
|
||||
void found_eof()
|
||||
{
|
||||
m_at_eof = true;
|
||||
}
|
||||
|
||||
|
||||
private: // iterator core operations
|
||||
friend class iterator_core_access;
|
||||
|
||||
void increment()
|
||||
{
|
||||
static_cast<Derived&>(*this).get();
|
||||
}
|
||||
|
||||
bool equal(const eof_iterator& other) const
|
||||
{
|
||||
if (m_at_eof && other.m_at_eof)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const ValueType& dereference() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
bool m_at_eof;
|
||||
ValueType m_value;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
130
include/boost/program_options/errors.hpp
Normal file
130
include/boost/program_options/errors.hpp
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
|
||||
#ifndef ERRORS_VP_2003_01_02
|
||||
#define ERRORS_VP_2003_01_02
|
||||
|
||||
#include <boost/program_options/config.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
/** Base class for all errors in the library. */
|
||||
class DECL error : public std::logic_error {
|
||||
public:
|
||||
error(const std::string& what) : std::logic_error(what) {}
|
||||
};
|
||||
|
||||
class DECL invalid_syntax : public error {
|
||||
public:
|
||||
invalid_syntax(const std::string& tokens, const std::string& msg)
|
||||
: error(std::string(msg).append(" in '").append(tokens).append("'")),
|
||||
tokens(tokens), msg(msg)
|
||||
{}
|
||||
|
||||
// gcc says that throw specification on dtor is loosened
|
||||
// without this line
|
||||
~invalid_syntax() throw() {}
|
||||
|
||||
// TODO: copy ctor might throw
|
||||
std::string tokens, msg;
|
||||
};
|
||||
|
||||
/** Class thrown when option name is not recognized. */
|
||||
class DECL unknown_option : public error {
|
||||
public:
|
||||
unknown_option(const std::string& name)
|
||||
: error(std::string("unknown option ").append(name))
|
||||
{}
|
||||
};
|
||||
|
||||
/** Class thrown when there's ambiguity between several possible options. */
|
||||
class DECL ambiguous_option : public error {
|
||||
public:
|
||||
ambiguous_option(const std::string& name,
|
||||
const std::vector<std::string>& alternatives)
|
||||
: error(std::string("ambiguous option ").append(name)),
|
||||
alternatives(alternatives)
|
||||
{}
|
||||
|
||||
~ambiguous_option() throw() {}
|
||||
|
||||
// TODO: copy ctor might throw
|
||||
std::vector<std::string> alternatives;
|
||||
};
|
||||
|
||||
/** Class thrown when there are several option values, but
|
||||
user called a method which cannot return them all. */
|
||||
class DECL multiple_values : public error {
|
||||
public:
|
||||
multiple_values(const std::string& what) : error(what) {}
|
||||
};
|
||||
|
||||
/** Class thrown when there are several occurences of an
|
||||
option, but user called a method which cannot return
|
||||
them all. */
|
||||
class DECL multiple_occurences : public error {
|
||||
public:
|
||||
multiple_occurences(const std::string& what) : error(what) {}
|
||||
};
|
||||
|
||||
/** Class thrown when value of option is incorrect. */
|
||||
class DECL validation_error : public error {
|
||||
public:
|
||||
validation_error(const std::string& what) : error(what) {}
|
||||
};
|
||||
|
||||
/** Class thrown then there are too many positional options. */
|
||||
class DECL too_many_positional_options_error : public error {
|
||||
public:
|
||||
too_many_positional_options_error(const std::string& what)
|
||||
: error(what) {}
|
||||
};
|
||||
|
||||
/** Class thrown then there are too few positional options. */
|
||||
class DECL too_few_positional_options_error : public error {
|
||||
public:
|
||||
too_few_positional_options_error(const std::string& what)
|
||||
: error(what) {}
|
||||
};
|
||||
|
||||
class DECL invalid_command_line_syntax : public invalid_syntax {
|
||||
public:
|
||||
enum kind_t {
|
||||
long_not_allowed,
|
||||
long_adjacent_not_allowed,
|
||||
short_adjacent_not_allowed,
|
||||
empty_adjacent_parameter,
|
||||
missing_parameter,
|
||||
extra_parameter,
|
||||
};
|
||||
|
||||
invalid_command_line_syntax(const std::string& tokens, kind_t kind);
|
||||
kind_t kind() const;
|
||||
protected:
|
||||
static std::string error_message(kind_t kind);
|
||||
private:
|
||||
kind_t m_kind;
|
||||
};
|
||||
|
||||
class DECL invalid_command_line_style : public error {
|
||||
public:
|
||||
invalid_command_line_style(const std::string& msg)
|
||||
: error(msg)
|
||||
{}
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#undef DECL
|
||||
|
||||
#endif
|
||||
54
include/boost/program_options/option.hpp
Normal file
54
include/boost/program_options/option.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#ifndef OPTION_HPP_VP_2004_02_25
|
||||
#define OPTION_HPP_VP_2004_02_25
|
||||
|
||||
#include <boost/program_options/config.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
/** Option found in input source.
|
||||
Contains a key and a value. The key, in turn, can be string (name of
|
||||
an option), or a integer (position in input source) -- in case no name
|
||||
is specified. The latter is only possible for command line.
|
||||
The template parameter specifies the type of char used for storing the
|
||||
option's value.
|
||||
*/
|
||||
template<class charT>
|
||||
class basic_option {
|
||||
public:
|
||||
basic_option() : position_key(-1) {}
|
||||
basic_option(const std::string& string_key,
|
||||
const std::vector< std::string> &value)
|
||||
: string_key(string_key), value(value)
|
||||
{}
|
||||
|
||||
/** String key of this option. Intentionally independent of the template
|
||||
parameter. */
|
||||
std::string string_key;
|
||||
/** Position key of this option. All options without explicit name are
|
||||
sequentally numbered starting from 0. If an option has explicit name,
|
||||
'position_key' is equal to -1. It is possible that both
|
||||
position_key and string_key is specified, in case name is implicitly
|
||||
added.
|
||||
*/
|
||||
int position_key;
|
||||
/** Option's value */
|
||||
std::vector< std::basic_string<charT> > value;
|
||||
};
|
||||
typedef basic_option<char> option;
|
||||
typedef basic_option<wchar_t> woption;
|
||||
|
||||
}}
|
||||
|
||||
#undef DECL
|
||||
|
||||
|
||||
#endif
|
||||
244
include/boost/program_options/options_description.hpp
Normal file
244
include/boost/program_options/options_description.hpp
Normal file
@@ -0,0 +1,244 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
|
||||
#ifndef OPTION_DESCRIPTION_VP_2003_05_19
|
||||
#define OPTION_DESCRIPTION_VP_2003_05_19
|
||||
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/errors.hpp>
|
||||
#include <boost/program_options/value_semantic.hpp>
|
||||
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/detail/workaround.hpp>
|
||||
#include <boost/any.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
/** Boost namespace */
|
||||
namespace boost {
|
||||
/** Namespace for the library. */
|
||||
namespace program_options {
|
||||
|
||||
/** Describes one possible command line/config file option. There are two
|
||||
kinds of properties of an option. First describe it syntactically and
|
||||
are used only to validate input. Second affect interpretation of the
|
||||
option, for example default value for it or function that should be
|
||||
called when the value is finally know. Routines which perform parsing
|
||||
never use second kind of properties -- they are side effect free.
|
||||
@sa options_description
|
||||
*/
|
||||
class DECL option_description {
|
||||
public:
|
||||
|
||||
option_description();
|
||||
|
||||
/** Initializes the object with the passed data.
|
||||
|
||||
Note: it would be nice to make the second parameter auto_ptr,
|
||||
to explicitly pass ownership. Unfortunately, it's often needed to
|
||||
create objects of types derived from 'value_semantic':
|
||||
options_description d;
|
||||
d.add_options()("a", parameter<int>("n")->default_value(1));
|
||||
Here, the static type returned by 'parameter' should be derived
|
||||
from value_semantic.
|
||||
|
||||
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
|
||||
|
||||
So, we have to use plain old pointers. Besides, users are not
|
||||
expected to use the constructor directly.
|
||||
|
||||
|
||||
The 'name' parameter is interpreted by the following rules:
|
||||
- if there's no "," character in 'name', it specifies long name
|
||||
- otherwise, the part before "," specifies long name and the part
|
||||
after -- long name.
|
||||
*/
|
||||
option_description(const char* name,
|
||||
const value_semantic* s);
|
||||
|
||||
/** Initializes the class with the passed data.
|
||||
*/
|
||||
option_description(const char* name,
|
||||
const value_semantic* s,
|
||||
const char* description);
|
||||
|
||||
virtual ~option_description();
|
||||
|
||||
/// Name to be used with short-style option ("-w").
|
||||
const std::string& short_name() const;
|
||||
|
||||
/// Name to be used with long-style option ("--whatever").
|
||||
const std::string& long_name() const;
|
||||
/// Explanation of this option
|
||||
const std::string& description() const;
|
||||
|
||||
/// Semantic of option's value
|
||||
shared_ptr<const value_semantic> semantic() const;
|
||||
|
||||
/// Returns the option name, formatted suitably for usage message.
|
||||
std::string format_name() const;
|
||||
|
||||
/** Return the parameter name and properties, formatted suitably for
|
||||
usage message. */
|
||||
std::string format_parameter() const;
|
||||
|
||||
private:
|
||||
|
||||
option_description& name(const char* name);
|
||||
|
||||
std::string m_short_name, m_long_name, m_description;
|
||||
std::string m_default_value, m_default_parameter;
|
||||
// shared_ptr is needed to simplify memory management in
|
||||
// copy ctor and destructor.
|
||||
shared_ptr<const value_semantic> m_value_semantic;
|
||||
};
|
||||
|
||||
class options_description;
|
||||
|
||||
/** Class which provides convenient creation syntax to option_description.
|
||||
*/
|
||||
class DECL options_description_easy_init {
|
||||
public:
|
||||
options_description_easy_init(options_description* owner);
|
||||
|
||||
options_description_easy_init&
|
||||
operator()(const char* name,
|
||||
const char* description);
|
||||
|
||||
options_description_easy_init&
|
||||
operator()(const char* name,
|
||||
const value_semantic* s);
|
||||
|
||||
options_description_easy_init&
|
||||
operator()(const char* name,
|
||||
const value_semantic* s,
|
||||
const char* description);
|
||||
|
||||
private:
|
||||
options_description* owner;
|
||||
};
|
||||
|
||||
|
||||
/** A set of option descriptions. This provides convenient interface for
|
||||
adding new option (the add_options) method, and facilities to search
|
||||
for options by name.
|
||||
|
||||
See @ref a_adding_options "here" for option adding interface discussion.
|
||||
@sa option_description
|
||||
*/
|
||||
class DECL options_description {
|
||||
public:
|
||||
|
||||
/** Creates the instance. */
|
||||
options_description();
|
||||
/** Creates the instance. The 'caption' parameter gives the name of
|
||||
this 'options_description' instance. Primarily usefull for output.
|
||||
*/
|
||||
options_description(const std::string& caption);
|
||||
/** Adds new variable description. Throws duplicate_variable_error if
|
||||
either short or long name matches that of already present one.
|
||||
*/
|
||||
void add(shared_ptr<option_description> desc);
|
||||
/** Adds a group of option description. This have the same
|
||||
effect as adding all option_descriptions in 'desc'
|
||||
individually, except that output operator will show
|
||||
a separate group.
|
||||
Returns *this.
|
||||
*/
|
||||
options_description& add(const options_description& desc);
|
||||
|
||||
public:
|
||||
/** Returns an object of implementation-defined type suitable for adding
|
||||
options to options_description. The returned object will
|
||||
have overloaded operator() with parameter type matching
|
||||
'option_description' constructors. Calling the operator will create
|
||||
new option_description instance and add it.
|
||||
*/
|
||||
options_description_easy_init add_options();
|
||||
|
||||
/** Count the number of option descriptions with given name.
|
||||
Returns 0 or 1.
|
||||
The 'name' parameter can be either name of long option, and short
|
||||
option prepended by '-'.
|
||||
*/
|
||||
unsigned count(const std::string& name) const;
|
||||
/** Count the number of descriptions having the given string as
|
||||
prefix. This makes sense only for long options.
|
||||
*/
|
||||
unsigned count_approx(const std::string& prefix) const;
|
||||
/** Returns description given a name.
|
||||
@pre count(name) == 1
|
||||
*/
|
||||
const option_description& find(const std::string& name) const;
|
||||
/** Returns description given a prefix. Throws
|
||||
@pre count_approx(name) == 1
|
||||
*/
|
||||
const option_description& find_approx(const std::string& prefix) const;
|
||||
/// Returns all such strings x for which count(x) == 1
|
||||
std::set<std::string> keys() const;
|
||||
/** For each option description stored, contains long name if not empty,
|
||||
if it is empty, short name is returned.
|
||||
*/
|
||||
std::set<std::string> primary_keys() const;
|
||||
/// Returns all such long options for which 'prefix' is prefix
|
||||
std::set<std::string> approximations(const std::string& prefix) const;
|
||||
|
||||
/** Produces a human readable output of 'desc', listing options,
|
||||
their descriptions and allowed parameters. Other options_description
|
||||
instances previously passed to add will be output separately. */
|
||||
friend DECL std::ostream& operator<<(std::ostream& os,
|
||||
const options_description& desc);
|
||||
|
||||
/** Output 'desc' to the specified streeam, calling 'f' to output each
|
||||
option_description element. */
|
||||
void print(std::ostream& os) const;
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, int>::const_iterator name2index_iterator;
|
||||
typedef std::pair<name2index_iterator, name2index_iterator>
|
||||
approximation_range;
|
||||
|
||||
|
||||
approximation_range find_approximation(const std::string& prefix) const;
|
||||
|
||||
template<typename Derived>
|
||||
friend class option_description_easy_init;
|
||||
|
||||
std::string m_caption;
|
||||
// Data organization is chosen since:
|
||||
// - there could be two names for one option
|
||||
// - option_add_proxy needs to know the last added option
|
||||
std::vector< shared_ptr<option_description> > options;
|
||||
std::map<std::string, int> name2index;
|
||||
// Whether the option comes from one of declared groups.
|
||||
std::vector<bool> belong_to_group;
|
||||
|
||||
std::vector<options_description> groups;
|
||||
|
||||
};
|
||||
|
||||
/** Class thrown when duplicate option description is found. */
|
||||
class DECL duplicate_option_error : public error {
|
||||
public:
|
||||
duplicate_option_error(const std::string& what) : error(what) {}
|
||||
};
|
||||
}}
|
||||
|
||||
#undef DECL
|
||||
|
||||
#endif
|
||||
199
include/boost/program_options/parsers.hpp
Normal file
199
include/boost/program_options/parsers.hpp
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
|
||||
#ifndef PARSERS_VP_2003_05_19
|
||||
#define PARSERS_VP_2003_05_19
|
||||
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/option.hpp>
|
||||
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <boost/function/function1.hpp>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
class options_description;
|
||||
class positional_options_description;
|
||||
|
||||
|
||||
/** Results of parsing an input source.
|
||||
The primary use of this class is passing information from parsers
|
||||
component to value storage component. This class does not makes
|
||||
much sense itself.
|
||||
*/
|
||||
template<class charT>
|
||||
class basic_parsed_options {
|
||||
public:
|
||||
explicit basic_parsed_options(const options_description* description)
|
||||
: description(description) {}
|
||||
/** Options found in the source. */
|
||||
std::vector< basic_option<charT> > options;
|
||||
/** Options description that was used for parsing.
|
||||
Parsers should return pointer to the instance of
|
||||
option_description passed to them, and issues of lifetime are
|
||||
up to the caller. Can be NULL.
|
||||
*/
|
||||
const options_description* description;
|
||||
};
|
||||
|
||||
/** Specialization of basic_parsed_options which:
|
||||
- provides convenient conversion from basic_parsed_options<char>
|
||||
- stores the passed char-based options for later use.
|
||||
*/
|
||||
template<>
|
||||
class basic_parsed_options<wchar_t> {
|
||||
public:
|
||||
/** Constructs wraped_options from options in UTF8 encoding. */
|
||||
explicit basic_parsed_options(const basic_parsed_options<char>& po);
|
||||
|
||||
std::vector< basic_option<wchar_t> > options;
|
||||
const options_description* description;
|
||||
|
||||
/** Stores UTF8 encoded options that were passed to constructor,
|
||||
to avoid reverse conversion in some cases. */
|
||||
basic_parsed_options<char> utf8_encoded_options;
|
||||
};
|
||||
|
||||
typedef basic_parsed_options<char> parsed_options;
|
||||
typedef basic_parsed_options<wchar_t> wparsed_options;
|
||||
|
||||
/** Augments basic_parsed_options<wchar_t> with conversion from
|
||||
'parsed_options' */
|
||||
|
||||
|
||||
typedef function1<std::pair<std::string, std::string>, const std::string&> ext_parser;
|
||||
|
||||
/** Comamnd line parser.
|
||||
|
||||
The class allows to specify all the information needed for parsing and
|
||||
the parse the command line. It is primarily needed to emulate named
|
||||
function parameters -- a regular function with 5 parameters will be hard
|
||||
to use and creating overloads with smaller nuber of parameters will be
|
||||
confusing.
|
||||
|
||||
For the most common case, the function parse_command_line is a better
|
||||
alternative.
|
||||
*/
|
||||
template<class charT>
|
||||
class basic_command_line_parser {
|
||||
public:
|
||||
/** Creates a command line parser for the specified arguments
|
||||
list. The 'args' parameter should not include program name.
|
||||
*/
|
||||
basic_command_line_parser(const std::vector<
|
||||
std::basic_string<charT> >& args);
|
||||
/** Creates a command line parser for the specified arguments
|
||||
list. The parameter should be the same as passes to 'main'.
|
||||
*/
|
||||
basic_command_line_parser(int argc, charT* argv[]);
|
||||
|
||||
basic_command_line_parser& options(const options_description& desc);
|
||||
basic_command_line_parser& positional(
|
||||
const positional_options_description& desc);
|
||||
|
||||
basic_command_line_parser& style(int);
|
||||
basic_command_line_parser& extra_parser(ext_parser);
|
||||
|
||||
basic_parsed_options<charT> run() const;
|
||||
private:
|
||||
int m_style;
|
||||
const options_description* m_desc;
|
||||
const positional_options_description* m_positional;
|
||||
function1<std::pair<std::string, std::string>, const std::string&> m_ext;
|
||||
|
||||
// Intentionally independent from charT
|
||||
std::vector<std::string> m_args;
|
||||
};
|
||||
|
||||
template<>
|
||||
basic_command_line_parser<char>
|
||||
::basic_command_line_parser(const std::vector<
|
||||
std::basic_string<char> >& args);
|
||||
|
||||
template<>
|
||||
basic_command_line_parser<char>
|
||||
::basic_command_line_parser(int argc, char* argv[]);
|
||||
|
||||
template<>
|
||||
basic_command_line_parser<wchar_t>
|
||||
::basic_command_line_parser(const std::vector<
|
||||
std::basic_string<wchar_t> >& args);
|
||||
|
||||
template<>
|
||||
basic_command_line_parser<wchar_t>
|
||||
::basic_command_line_parser(int argc, wchar_t* argv[]);
|
||||
|
||||
template<>
|
||||
basic_parsed_options<char>
|
||||
basic_command_line_parser<char>::run() const;
|
||||
|
||||
template<>
|
||||
basic_parsed_options<wchar_t>
|
||||
basic_command_line_parser<wchar_t>::run() const;
|
||||
|
||||
typedef basic_command_line_parser<char> command_line_parser;
|
||||
typedef basic_command_line_parser<wchar_t> wcommand_line_parser;
|
||||
|
||||
/** Creates instance of 'command_line_parser', passes parameters to it,
|
||||
and returns the result of calling the 'run' method.
|
||||
*/
|
||||
template<class charT>
|
||||
basic_parsed_options<charT>
|
||||
parse_command_line(int argc, charT* argv[],
|
||||
const options_description&,
|
||||
int style = 0,
|
||||
function1<std::pair<std::string, std::string>,
|
||||
const std::string&> ext
|
||||
= ext_parser());
|
||||
|
||||
/** Parse a config file.
|
||||
*/
|
||||
template<class charT>
|
||||
DECL basic_parsed_options<charT>
|
||||
parse_config_file(std::basic_istream<charT>&, const options_description&);
|
||||
|
||||
/** Parse environment.
|
||||
|
||||
For each environment variable, the 'name_mapper' function is called to
|
||||
obtain the option name. If it returns empty string, the variable is
|
||||
ignored.
|
||||
|
||||
This is done since naming of environment variables is typically
|
||||
different from the naming of command line options.
|
||||
*/
|
||||
DECL parsed_options
|
||||
parse_environment(const options_description&,
|
||||
const function1<std::string, std::string>& name_mapper);
|
||||
|
||||
/** Parse environment.
|
||||
|
||||
Takes all environment variables which start with 'prefix'. The option
|
||||
name is obtained from variable name by removing the prefix and
|
||||
converting the remaining string into lower case.
|
||||
*/
|
||||
DECL parsed_options
|
||||
parse_environment(const options_description&, const std::string& prefix);
|
||||
|
||||
/** @overload
|
||||
This function exists for resolve ambiguity between the two above
|
||||
functions when second argument is of 'char*' type. There's implicit
|
||||
convension to both function1 and string.
|
||||
*/
|
||||
DECL parsed_options
|
||||
parse_environment(const options_description&, const char* prefix);
|
||||
|
||||
}}
|
||||
|
||||
#undef DECL
|
||||
|
||||
#include "detail/parsers.hpp"
|
||||
|
||||
#endif
|
||||
67
include/boost/program_options/positional_options.hpp
Normal file
67
include/boost/program_options/positional_options.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#ifndef BOOST_PROGRAM_OPTIONS_POSITIONAL_OPTIONS_VP_2004_03_02
|
||||
#define BOOST_PROGRAM_OPTIONS_POSITIONAL_OPTIONS_VP_2004_03_02
|
||||
|
||||
#include <boost/program_options/config.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
/** Describes positional options.
|
||||
|
||||
The class allows to guess option names for positional options, which
|
||||
are specified on the command line and are identified by the position.
|
||||
The class uses the information provided by the user to associate a name
|
||||
with every positional option, or tell that no name is known.
|
||||
|
||||
The primary assumption is that only relative order of positional options
|
||||
themself matters, and that any interleaving ordinary options don't
|
||||
affect interpretation of positional options.
|
||||
|
||||
The user initializes the class by specifying that first N positional
|
||||
options should be given the name X1, following M options should be given
|
||||
the name X2 and so on.
|
||||
*/
|
||||
class DECL positional_options_description {
|
||||
public:
|
||||
positional_options_description();
|
||||
|
||||
/** Species that up to 'max_count' next positional options
|
||||
should be given the 'name'. The value of '-1' means 'unlimited'.
|
||||
No calls to 'add' can be made after call with 'max_value' equal to
|
||||
'-1'.
|
||||
*/
|
||||
void add(const char* name, int max_count);
|
||||
|
||||
/** Returns the maximum number of positional options that can
|
||||
be present. Can return numeric_limits<unsigned>::max() to
|
||||
indicate unlimited number. */
|
||||
unsigned max_total_count() const;
|
||||
|
||||
/** Returns the name that should be associated with positional
|
||||
options at 'position'.
|
||||
Precodting: position < max_total_count()
|
||||
*/
|
||||
const std::string& name_for_position(unsigned position) const;
|
||||
|
||||
private:
|
||||
// List of names corresponding to the positions. If the number of
|
||||
// positions is unlimited, then the last name is stored in
|
||||
// m_trailing;
|
||||
std::vector<std::string> m_names;
|
||||
std::string m_trailing;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#undef DECL
|
||||
|
||||
#endif
|
||||
|
||||
316
include/boost/program_options/value_semantic.hpp
Normal file
316
include/boost/program_options/value_semantic.hpp
Normal file
@@ -0,0 +1,316 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#ifndef VALUE_SEMANTIC_HPP_VP_2004_02_24
|
||||
#define VALUE_SEMANTIC_HPP_VP_2004_02_24
|
||||
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/errors.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <boost/any.hpp>
|
||||
#include <boost/function/function1.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
/** Class which specifies how the option's value is to be parsed
|
||||
and converted into C++ types.
|
||||
*/
|
||||
class DECL value_semantic {
|
||||
public:
|
||||
/** Returns the name of the option. The name is only meaningfull
|
||||
for automatic help message.
|
||||
*/
|
||||
virtual std::string name() const = 0;
|
||||
|
||||
/** Returns true if value cannot be specified in source at all.
|
||||
Other methods can still set the value somehow, but
|
||||
user can't affect it.
|
||||
*/
|
||||
virtual bool zero_tokens() const = 0;
|
||||
|
||||
/** Returns true if values from different sources should be composed.
|
||||
Otherwise, value from the first source is used and values from
|
||||
other sources are discarded.
|
||||
*/
|
||||
virtual bool is_composing() const = 0;
|
||||
|
||||
/** Returns true if explicit value of an option can be omitted.
|
||||
*/
|
||||
virtual bool is_implicit() const = 0;
|
||||
|
||||
/** Returns true if value can span several token in input source. */
|
||||
virtual bool is_multitoken() const = 0;
|
||||
|
||||
/** Parses a group of tokens that specify a value of option.
|
||||
Stores the result in 'value_store', using whatever representation
|
||||
is desired. Maybe be called several times if value of the same
|
||||
option is specified more than once.
|
||||
*/
|
||||
virtual void parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens,
|
||||
bool utf8) const
|
||||
= 0;
|
||||
|
||||
/** Called to assign default value to 'value_store'. Returns
|
||||
true if default value is assigned, and false if not default
|
||||
value exists. */
|
||||
virtual bool apply_default(boost::any& value_store) const = 0;
|
||||
|
||||
/** Called when final value of an option is determined.
|
||||
*/
|
||||
virtual void notify(const boost::any& value_store) const = 0;
|
||||
|
||||
virtual ~value_semantic() {}
|
||||
};
|
||||
|
||||
/** Helper class which perform necessary character conversions in the
|
||||
'parse' method and forwards the data further.
|
||||
*/
|
||||
template<class charT>
|
||||
class value_semantic_codecvt_helper {
|
||||
// Nothing here. Specializations to follow.
|
||||
};
|
||||
|
||||
template<>
|
||||
class value_semantic_codecvt_helper<char> : public value_semantic {
|
||||
private: // base overrides
|
||||
void parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens,
|
||||
bool utf8) const;
|
||||
protected: // interface for derived classes.
|
||||
virtual void parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens)
|
||||
const = 0;
|
||||
};
|
||||
|
||||
template<>
|
||||
class value_semantic_codecvt_helper<wchar_t> : public value_semantic {
|
||||
private: // base overrides
|
||||
void parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens,
|
||||
bool utf8) const;
|
||||
protected: // interface for derived classes.
|
||||
virtual void parse(boost::any& value_store,
|
||||
const std::vector<std::wstring>& new_tokens)
|
||||
const = 0;
|
||||
};
|
||||
|
||||
/** Class which specify handling of value for which user did not specified
|
||||
anything. */
|
||||
class DECL untyped_value : public value_semantic_codecvt_helper<char> {
|
||||
public:
|
||||
untyped_value(bool zero_tokens = false)
|
||||
: m_zero_tokens(zero_tokens)
|
||||
{}
|
||||
|
||||
std::string name() const;
|
||||
|
||||
bool zero_tokens() const { return m_zero_tokens; }
|
||||
bool is_composing() const { return false; }
|
||||
bool is_implicit() const { return false; }
|
||||
bool is_multitoken() const { return false; }
|
||||
|
||||
/** If 'value_store' is already initialized, or new_tokens
|
||||
has more than one elements, throws. Otherwise, assigns
|
||||
the first string from 'new_tokens' to 'value_store', without
|
||||
any modifications.
|
||||
*/
|
||||
void parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens) const;
|
||||
|
||||
/** Does nothing. */
|
||||
bool apply_default(boost::any&) const { return false; }
|
||||
|
||||
/** Does nothing. */
|
||||
void notify(const boost::any& value_store) const {}
|
||||
private:
|
||||
bool m_zero_tokens;
|
||||
};
|
||||
|
||||
/** Class which handles value of a specific type. */
|
||||
template<class T, class charT = char>
|
||||
class typed_value : public value_semantic_codecvt_helper<charT> {
|
||||
public:
|
||||
/** Ctor. The 'store_to' parameter tells where to store
|
||||
the value when it's known. The parameter can be NULL. */
|
||||
typed_value(T* store_to)
|
||||
: m_store_to(store_to), m_composing(false),
|
||||
m_implicit(false), m_multitoken(false)
|
||||
{}
|
||||
|
||||
/** Specifies default value, which will be used
|
||||
if none is explicitly specified. The type 'T' should
|
||||
provide operator<< for ostream.
|
||||
*/
|
||||
typed_value* default_value(const T& v)
|
||||
{
|
||||
m_default_value = boost::any(v);
|
||||
m_default_value_as_text = boost::lexical_cast<std::string>(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specifies default value, which will be used
|
||||
if none is explicitly specified. Unlike the above overload,
|
||||
the type 'T' need not provide operator<< for ostream,
|
||||
but textual representation of default value must be provided
|
||||
by the user.
|
||||
*/
|
||||
typed_value* default_value(const T& v, const std::string& textual)
|
||||
{
|
||||
m_default_value = boost::any(v);
|
||||
m_default_value_as_text = textual;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specifies a function to be called when the final value
|
||||
is determined. */
|
||||
typed_value* notifier(function1<void, const T&> f)
|
||||
{
|
||||
m_notifier = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specifies that the value is composing. See the 'is_composing'
|
||||
method for explanation.
|
||||
*/
|
||||
typed_value* composing()
|
||||
{
|
||||
m_composing = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specifies that the value is implicit. */
|
||||
typed_value* implicit()
|
||||
{
|
||||
m_implicit = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specifies that the value can span multiple tokens. */
|
||||
typed_value* multitoken()
|
||||
{
|
||||
m_multitoken = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public: // value semantic overrides
|
||||
|
||||
std::string name() const;
|
||||
|
||||
bool zero_tokens() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_composing() const
|
||||
{
|
||||
return m_composing;
|
||||
}
|
||||
|
||||
bool is_implicit() const
|
||||
{
|
||||
return m_implicit;
|
||||
}
|
||||
|
||||
bool is_multitoken() const
|
||||
{
|
||||
return m_multitoken;
|
||||
}
|
||||
|
||||
|
||||
/** Creates an instance of the 'validator' class and calls
|
||||
it's operator() to perform actual convertion. */
|
||||
void parse(boost::any& value_store,
|
||||
const std::vector< std::basic_string<charT> >& new_tokens) const;
|
||||
|
||||
/** If default value was specified via previous call to
|
||||
'default_value', stores that value into 'value_store'.
|
||||
Returns true if default value was stored.
|
||||
*/
|
||||
virtual bool apply_default(boost::any& value_store) const
|
||||
{
|
||||
if (!m_default_value.empty()) {
|
||||
value_store = m_default_value;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** If an address of variable to store value was specified
|
||||
when creating *this, stores the value there. Otherwise,
|
||||
does nothing. */
|
||||
void notify(const boost::any& value_store) const;
|
||||
|
||||
|
||||
private:
|
||||
T* m_store_to;
|
||||
|
||||
// Default value is stored as boost::any and not
|
||||
// as boost::optional to avoid unnecessary instantinations.
|
||||
boost::any m_default_value;
|
||||
std::string m_default_value_as_text;
|
||||
bool m_composing, m_implicit, m_multitoken;
|
||||
boost::function1<void, const T&> m_notifier;
|
||||
};
|
||||
|
||||
|
||||
/** Creates a typed_value<T> instance. This function is the primary
|
||||
method to create value_semantic instance for a specific type, which
|
||||
can later be passed to 'option_description' constructor.
|
||||
The second overload is used when it's additionally desired to store the
|
||||
value of option into program variable.
|
||||
*/
|
||||
template<class T>
|
||||
typed_value<T>*
|
||||
value();
|
||||
|
||||
/** @overload
|
||||
*/
|
||||
template<class T>
|
||||
typed_value<T>*
|
||||
value(T* v);
|
||||
|
||||
/** Creates a typed_value<T> instance. This function is the primary
|
||||
method to create value_semantic instance for a specific type, which
|
||||
can later be passed to 'option_description' constructor.
|
||||
*/
|
||||
template<class T>
|
||||
typed_value<T, wchar_t>*
|
||||
wvalue();
|
||||
|
||||
/** @overload
|
||||
*/
|
||||
template<class T>
|
||||
typed_value<T, wchar_t>*
|
||||
wvalue(T* v);
|
||||
|
||||
/** Works the same way as the 'value' function, but also makes the created
|
||||
value_semantic implicit, i.e. the value can be omitted. The omitted
|
||||
value is considered to be 'true'.
|
||||
*/
|
||||
DECL typed_value<bool>*
|
||||
bool_switch();
|
||||
|
||||
/** @overload
|
||||
*/
|
||||
DECL typed_value<bool>*
|
||||
bool_switch(bool* v);
|
||||
|
||||
}}
|
||||
|
||||
#include "detail/value_semantic.hpp"
|
||||
|
||||
#undef DECL
|
||||
|
||||
#endif
|
||||
|
||||
197
include/boost/program_options/variables_map.hpp
Normal file
197
include/boost/program_options/variables_map.hpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
|
||||
#ifndef VARIABLES_MAP_VP_2003_05_19
|
||||
#define VARIABLES_MAP_VP_2003_05_19
|
||||
|
||||
#include <boost/program_options/config.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
|
||||
#include <boost/any.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
template<class charT>
|
||||
class basic_parsed_options;
|
||||
|
||||
class value_semantic;
|
||||
class variables_map;
|
||||
|
||||
/** Class holding value of option. Contains details about how the
|
||||
value is set and allows to conveniently obtain the value.
|
||||
*/
|
||||
class DECL variable_value {
|
||||
public:
|
||||
variable_value() : m_defaulted(false) {}
|
||||
variable_value(const boost::any& v, bool defaulted)
|
||||
: v(v), m_defaulted(defaulted)
|
||||
{}
|
||||
|
||||
/** If stored value if of type T, returns that value. Otherwise,
|
||||
throws boost::bad_any_cast exception. */
|
||||
template<class T> const T& as() const;
|
||||
|
||||
/** @overload */
|
||||
template<class T> T& as();
|
||||
|
||||
/// Returns true if no value is stored.
|
||||
bool empty() const;
|
||||
/** Returns true if the value was not explcitly
|
||||
given, but has default value. */
|
||||
bool defaulted() const;
|
||||
/** Returns the contained value. */
|
||||
const boost::any& value() const;
|
||||
|
||||
/** Returns the contained value. */
|
||||
boost::any& value();
|
||||
private:
|
||||
boost::any v;
|
||||
bool m_defaulted;
|
||||
// Internal reference to value semantic. We need to run
|
||||
// notifications when *final* values of options are known, and
|
||||
// they are known only after all sources are stored. By that
|
||||
// time options_description for the first source might not
|
||||
// be easily accessible, so we need to store semantic here.
|
||||
shared_ptr<const value_semantic> m_value_semantic;
|
||||
|
||||
friend void DECL store(const basic_parsed_options<char>& options,
|
||||
variables_map& m, bool);
|
||||
friend void DECL notify(variables_map& m);
|
||||
};
|
||||
|
||||
/** Implements string->string mapping with convenient value casting
|
||||
facilities. */
|
||||
class DECL abstract_variables_map {
|
||||
public:
|
||||
abstract_variables_map();
|
||||
abstract_variables_map(const abstract_variables_map* next);
|
||||
|
||||
virtual ~abstract_variables_map() {}
|
||||
|
||||
/** Obtains the value of variable 'name', from *this and
|
||||
possibly from the chain of variable maps.
|
||||
|
||||
- if there's no value in *this.
|
||||
- if there's next variable map, returns value from it
|
||||
- otherwise, returns empty value
|
||||
|
||||
- if there's defaulted value
|
||||
- if there's next varaible map, which has non-defauled
|
||||
value, return that
|
||||
- otherwise, return value from *this
|
||||
|
||||
- if there's non-defauled value, returns it.
|
||||
*/
|
||||
const variable_value& operator[](const std::string& name) const;
|
||||
|
||||
/** Sets next variable map, which will be used to find
|
||||
variables not found in *this. */
|
||||
void next(abstract_variables_map* next);
|
||||
|
||||
private:
|
||||
/** Returns value of variable 'name' stored in *this, or
|
||||
empty value otherwise. */
|
||||
virtual const variable_value& get(const std::string& name) const = 0;
|
||||
|
||||
const abstract_variables_map* m_next;
|
||||
};
|
||||
|
||||
/** Concrete variables map which store variables in real map. */
|
||||
class DECL variables_map : public abstract_variables_map,
|
||||
public std::map<std::string, variable_value>
|
||||
{
|
||||
public:
|
||||
variables_map();
|
||||
variables_map(const abstract_variables_map* next);
|
||||
|
||||
// Resolve conflict between inherited operators.
|
||||
const variable_value& operator[](const std::string& name) const
|
||||
{ return abstract_variables_map::operator[](name); }
|
||||
|
||||
private:
|
||||
/** Implementation of abstract_variables_map::get
|
||||
which does 'find' in *this. */
|
||||
const variable_value& get(const std::string& name) const;
|
||||
};
|
||||
|
||||
/** Stores in 'm' all options that are defined in 'options'.
|
||||
If 'm' already has a non-defaulted value of an option, that value
|
||||
is not changed, even of 'options' specify some value.
|
||||
*/
|
||||
DECL void store(const basic_parsed_options<char>& options, variables_map& m,
|
||||
bool utf8 = false);
|
||||
|
||||
/** Stores in 'm' all options that are defined in 'options'.
|
||||
If 'm' already has a non-defaulted value of an option, that value
|
||||
is not changed, even of 'options' specify some value.
|
||||
This is wide character variant.
|
||||
*/
|
||||
DECL void store(const basic_parsed_options<wchar_t>& options,
|
||||
variables_map& m);
|
||||
|
||||
|
||||
/** Runs all 'notify' function for options in 'm'. */
|
||||
DECL void notify(variables_map& m);
|
||||
|
||||
|
||||
/*
|
||||
* Templates/inlines
|
||||
*/
|
||||
|
||||
inline bool
|
||||
variable_value::empty() const
|
||||
{
|
||||
return v.empty();
|
||||
}
|
||||
|
||||
inline bool
|
||||
variable_value::defaulted() const
|
||||
{
|
||||
return m_defaulted;
|
||||
}
|
||||
|
||||
inline
|
||||
const boost::any&
|
||||
variable_value::value() const
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
inline
|
||||
boost::any&
|
||||
variable_value::value()
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
const T&
|
||||
variable_value::as() const {
|
||||
const T* r = boost::any_cast<T>(&v);
|
||||
if (!r)
|
||||
throw boost::bad_any_cast();
|
||||
return *r;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T&
|
||||
variable_value::as() {
|
||||
T* r = boost::any_cast<T>(&v);
|
||||
if (!r)
|
||||
throw boost::bad_any_cast();
|
||||
return *r;
|
||||
}
|
||||
}}
|
||||
|
||||
#undef DECL
|
||||
|
||||
#endif
|
||||
15
include/boost/program_options/version.hpp
Normal file
15
include/boost/program_options/version.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#ifndef BOOST_PROGRAM_OPTIONS_VERSION_HPP_VP_2004_04_05
|
||||
#define BOOST_PROGRAM_OPTIONS_VERSION_HPP_VP_2004_04_05
|
||||
|
||||
/** The version of the source interface.
|
||||
The value will be incremented whenever a change is made which might
|
||||
cause compilation errors for existing code.
|
||||
*/
|
||||
#define BOOST_PROGRAM_OPTIONS_VERSION 2
|
||||
|
||||
#endif
|
||||
738
src/cmdline.cpp
Normal file
738
src/cmdline.cpp
Normal file
@@ -0,0 +1,738 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <boost/program_options/detail/cmdline.hpp>
|
||||
#include <boost/program_options/errors.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
using namespace std;
|
||||
using namespace boost::program_options::command_line_style;
|
||||
|
||||
invalid_command_line_syntax::
|
||||
invalid_command_line_syntax(const std::string& tokens, kind_t kind)
|
||||
: invalid_syntax(tokens, error_message(kind)), m_kind(kind)
|
||||
{}
|
||||
|
||||
std::string
|
||||
invalid_command_line_syntax::error_message(kind_t kind)
|
||||
{
|
||||
// Initially, store the message in 'const char*' variable,
|
||||
// to avoid convesion to std::string in all cases.
|
||||
const char* msg;
|
||||
if (kind == long_not_allowed)
|
||||
msg = "long options are not allowed";
|
||||
else if (kind == long_adjacent_not_allowed)
|
||||
msg = "parameters adjuacent to long options not allowed";
|
||||
else if (kind == short_adjacent_not_allowed)
|
||||
msg = "parameters adjust to short options are not allowed";
|
||||
else if (kind == empty_adjacent_parameter)
|
||||
msg = "adjacent parameter is empty";
|
||||
else if (kind == missing_parameter)
|
||||
msg = "required parameter is missing";
|
||||
else if (kind == extra_parameter)
|
||||
msg = "extra parameter";
|
||||
else
|
||||
msg = "unknown error";
|
||||
return msg;
|
||||
}
|
||||
|
||||
invalid_command_line_syntax::kind_t
|
||||
invalid_command_line_syntax::kind() const
|
||||
{
|
||||
return m_kind;
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
|
||||
cmdline::cmdline(const std::vector<std::string>& args, int style,
|
||||
bool allow_unregistered)
|
||||
{
|
||||
init(args, style, allow_unregistered);
|
||||
}
|
||||
|
||||
cmdline::cmdline(int argc, const char*const * argv, int style,
|
||||
bool allow_unregistered)
|
||||
{
|
||||
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
|
||||
vector<string> args;
|
||||
copy(argv+1, argv+argc, inserter(args, args.end()));
|
||||
init(args, style, allow_unregistered);
|
||||
#else
|
||||
init(vector<string>(argv+1, argv+argc), style, allow_unregistered);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::init(const std::vector<std::string>& args, int style,
|
||||
bool allow_unregistered)
|
||||
{
|
||||
if (style == 0)
|
||||
style = default_style;
|
||||
|
||||
check_style(style);
|
||||
|
||||
this->args = args;
|
||||
this->style = style ? style_t(style) : default_style;
|
||||
this->allow_unregistered = allow_unregistered,
|
||||
|
||||
index = 0;
|
||||
m_current = 0;
|
||||
m_next = 0;
|
||||
pending_short_option = 0;
|
||||
m_no_more_options = false;
|
||||
m_error_description = ed_success;
|
||||
m_num_tokens = 0;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::check_style(int style) const
|
||||
{
|
||||
bool allow_some_long =
|
||||
(style & allow_long) || (style & allow_long_disguise);
|
||||
|
||||
const char* error = 0;
|
||||
if (allow_some_long &&
|
||||
!(style & long_allow_adjacent) && !(style & long_allow_next))
|
||||
error = "style disallows parameters for long options";
|
||||
|
||||
if (!error && (style & allow_short) &&
|
||||
!(style & short_allow_adjacent) && !(style & short_allow_next))
|
||||
error = "style disallows parameters 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";
|
||||
|
||||
if (error)
|
||||
throw invalid_command_line_style(error);
|
||||
|
||||
// Need to check that if guessing and long disguise are enabled
|
||||
// -f will mean the same as -foo
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::set_additional_parser(additional_parser p)
|
||||
{
|
||||
m_additional_parser = p;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::add_option(const std::string& long_name, char short_name,
|
||||
char properties, int index)
|
||||
{
|
||||
options.push_back(option(long_name, short_name,
|
||||
translate_property(properties), index));
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::add_option(const char* long_name, char short_name,
|
||||
char properties, int index)
|
||||
{
|
||||
add_option(string(long_name), short_name, properties, index);
|
||||
}
|
||||
|
||||
cmdline&
|
||||
cmdline::operator++()
|
||||
{
|
||||
next();
|
||||
clear_error();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
cmdline::at_option() const
|
||||
{
|
||||
return m_element_kind == ek_option;
|
||||
}
|
||||
|
||||
bool
|
||||
cmdline::at_argument() const
|
||||
{
|
||||
return m_element_kind == ek_argument;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::next()
|
||||
{
|
||||
if (!*this)
|
||||
return;
|
||||
|
||||
// Skip over current element
|
||||
advance(m_num_tokens);
|
||||
|
||||
// We might have consumed all tokens by now.
|
||||
if (!*this)
|
||||
return;
|
||||
|
||||
m_last = m_current;
|
||||
|
||||
m_opt = 0;
|
||||
m_num_tokens = 0;
|
||||
m_disguised_long = false;
|
||||
|
||||
m_option_name = std::string();
|
||||
m_option_values.clear();
|
||||
m_argument = std::string();
|
||||
|
||||
m_element_kind = ek_option;
|
||||
if (pending_short_option) {
|
||||
if (handle_short_option(pending_short_option))
|
||||
m_option_index = m_opt->index;
|
||||
// TODO: should decide what to do in this case
|
||||
assert(!m_disguised_long);
|
||||
} else {
|
||||
|
||||
if (m_additional_parser) {
|
||||
pair<string, string> p = m_additional_parser(m_current);
|
||||
if (!p.first.empty())
|
||||
if (handle_additional_parser(p)) {
|
||||
m_option_index = m_opt ? m_opt->index : 1;
|
||||
return;
|
||||
} else {
|
||||
// handle_additional_parser should have set
|
||||
// error code accordingly.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(is_option(m_current)) {
|
||||
case error_option:
|
||||
break;
|
||||
case no_option:
|
||||
if (strcmp(m_current, "--") == 0) {
|
||||
m_no_more_options = true;
|
||||
advance(1);
|
||||
next();
|
||||
return;
|
||||
} else {
|
||||
m_element_kind = ek_argument;
|
||||
m_argument = m_current;
|
||||
m_arguments.push_back(m_argument);
|
||||
m_num_tokens = 1;
|
||||
}
|
||||
break;
|
||||
case long_option:
|
||||
if (handle_long_option(m_current + 2))
|
||||
m_option_index = m_opt ? m_opt->index : 1;
|
||||
break;
|
||||
case short_option:
|
||||
if (handle_short_option(m_current + 1))
|
||||
m_option_index = m_opt ? m_opt->index : 1;
|
||||
break;
|
||||
case dos_option:
|
||||
if (handle_dos_option(m_current + 1))
|
||||
m_option_index = m_opt ? m_opt->index : 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const string&
|
||||
cmdline::argument() const
|
||||
{
|
||||
return m_argument;
|
||||
}
|
||||
|
||||
const string&
|
||||
cmdline::option_name() const
|
||||
{
|
||||
return m_option_name;
|
||||
}
|
||||
|
||||
int
|
||||
cmdline::option_index() const
|
||||
{
|
||||
return m_option_index;
|
||||
}
|
||||
|
||||
const string&
|
||||
cmdline::raw_option_name() const
|
||||
{
|
||||
return m_raw_option_name;
|
||||
}
|
||||
|
||||
const string&
|
||||
cmdline::option_value() const
|
||||
{
|
||||
static string empty;
|
||||
if (m_option_values.size() > 1)
|
||||
throw multiple_values("multiple values");
|
||||
return m_option_values.empty() ? empty : m_option_values.front();
|
||||
}
|
||||
|
||||
const std::vector<std::string>&
|
||||
cmdline::option_values() const
|
||||
{
|
||||
return m_option_values;
|
||||
}
|
||||
|
||||
const vector<string>&
|
||||
cmdline::arguments() const
|
||||
{
|
||||
return m_arguments;
|
||||
}
|
||||
|
||||
const string&
|
||||
cmdline::last() const
|
||||
{
|
||||
return m_last;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
int strncmp_nocase(const char* s1, const char* s2, size_t n)
|
||||
{
|
||||
size_t i(0);
|
||||
for(;*s1 && *s2 && i < n; ++s1, ++s2, ++i) {
|
||||
char c1 = *s1;
|
||||
char c2 = *s2;
|
||||
if (c1 == c2)
|
||||
continue;
|
||||
c1 = tolower(*s1);
|
||||
c2 = tolower(*s2);
|
||||
if (c1 < c2)
|
||||
return -1;
|
||||
else if (c1 > c2)
|
||||
return 1;
|
||||
}
|
||||
if (i == n)
|
||||
return 0;
|
||||
else
|
||||
if (!*s1 && *s2)
|
||||
return -1;
|
||||
else if (*s1 && !*s2)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test_cmdline_detail()
|
||||
{
|
||||
using detail::strncmp_nocase;
|
||||
assert(strncmp_nocase("a", "a", 5) == 0);
|
||||
assert(strncmp_nocase("a", "d", 5) < 0);
|
||||
assert(strncmp_nocase("d", "a", 5) > 0);
|
||||
assert(strncmp_nocase("abc", "abcd", 4) < 0);
|
||||
assert(strncmp_nocase("abcd", "abc", 4) > 0);
|
||||
assert(strncmp_nocase("abcd", "abc", 3) == 0);
|
||||
}
|
||||
|
||||
const cmdline::option*
|
||||
cmdline::find_long_option(const char* name)
|
||||
{
|
||||
// some systems does not have strchr et.al. in namespace std
|
||||
using namespace std;
|
||||
|
||||
// Handle the case of '=' in name, which is not part of option name
|
||||
const char* eq = strchr(name, '=');
|
||||
std::size_t n = eq ? eq - name : strlen(name);
|
||||
|
||||
int (*cmp)(const char*, const char*, size_t);
|
||||
#if BOOST_WORKAROUND(_MSC_FULL_VER, >= 13102292) &&\
|
||||
BOOST_WORKAROUND(_MSC_FULL_VER, BOOST_TESTED_AT(13103077))
|
||||
cmp = (style & case_insentitive) ? detail::strncmp_nocase : std::strncmp;
|
||||
#else
|
||||
cmp = (style & case_insentitive) ? detail::strncmp_nocase : strncmp;
|
||||
#endif
|
||||
const option* result(0);
|
||||
for (size_t i = 0; i < options.size(); ++i) {
|
||||
const char* known_name = options[i].long_name.c_str();
|
||||
bool prefix = (*options[i].long_name.rbegin() == '*');
|
||||
std::size_t n2 = n;
|
||||
if (prefix)
|
||||
n2 = options[i].long_name.size()-1;
|
||||
if (cmp(name, known_name, n2) == 0) {
|
||||
// Is there match without guessing?
|
||||
if (options[i].long_name.size() == n2
|
||||
|| (prefix && options[i].long_name.size() > n2)) {
|
||||
result = &options[i];
|
||||
break;
|
||||
} else if (style & allow_guessing) {
|
||||
if (result) {
|
||||
result = 0;
|
||||
m_error_description = ed_ambiguous_option;
|
||||
break;
|
||||
} else {
|
||||
result = &options[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!result && m_error_description == ed_success)
|
||||
m_error_description = ed_unknown_option;
|
||||
return result;
|
||||
}
|
||||
|
||||
const cmdline::option*
|
||||
cmdline::find_short_option(char name)
|
||||
{
|
||||
for (size_t i = 0; i < options.size(); ++i) {
|
||||
if (name == options[i].short_name)
|
||||
return &options[i];
|
||||
}
|
||||
m_error_description = ed_unknown_option;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
cmdline::handle_long_option(const char* s)
|
||||
{
|
||||
// some systems does not have strchr et.al. in namespace std
|
||||
using namespace std;
|
||||
|
||||
const option* opt = find_long_option(s);
|
||||
m_opt = opt;
|
||||
|
||||
if (opt || allow_unregistered) {
|
||||
// We always use the long name as specified by the
|
||||
// user, not abbreviation or otherwise-cased one we
|
||||
// get on the command line.
|
||||
if (opt)
|
||||
m_option_name = opt->long_name;
|
||||
|
||||
bool adjacent_parameter(false), next_parameter(false);
|
||||
const char* eq = strchr(s, '=');
|
||||
if (eq) {
|
||||
// But store option spelling from command line as well.
|
||||
m_raw_option_name = string(s, eq);
|
||||
if (eq[1]) {
|
||||
if (!(style & long_allow_adjacent)) {
|
||||
m_error_description = ed_long_adjacent_not_allowed;
|
||||
return false;
|
||||
} else {
|
||||
adjacent_parameter = true;
|
||||
m_option_values.push_back(string(eq+1));
|
||||
}
|
||||
} else {
|
||||
m_error_description = ed_empty_adjacent_parameter;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
m_raw_option_name = s;
|
||||
if (m_next && is_option(m_next) == no_option
|
||||
&& (style & long_allow_next)) {
|
||||
next_parameter = true;
|
||||
}
|
||||
m_error_description = ed_success;
|
||||
}
|
||||
|
||||
if (!opt)
|
||||
m_option_name = m_raw_option_name;
|
||||
|
||||
return process_parameter(opt, adjacent_parameter, next_parameter);
|
||||
|
||||
} else {
|
||||
// Option not found, 'find_long_option' has set error code already.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
cmdline::handle_short_option(const char* s, bool ignore_sticky)
|
||||
{
|
||||
pending_short_option = 0;
|
||||
|
||||
if (style & allow_long_disguise) {
|
||||
const option* opt = find_long_option(s);
|
||||
m_opt = opt;
|
||||
if (opt) {
|
||||
m_disguised_long = true;
|
||||
return handle_long_option(s);
|
||||
}
|
||||
else
|
||||
m_error_description = ed_success;
|
||||
}
|
||||
|
||||
m_disguised_long = false;
|
||||
const option* opt = find_short_option(*s);
|
||||
m_opt = opt;
|
||||
|
||||
if (opt || allow_unregistered) {
|
||||
|
||||
if (opt && !opt->long_name.empty())
|
||||
m_option_name = opt->long_name;
|
||||
else
|
||||
m_option_name = '-' + string(s, s+1);
|
||||
|
||||
m_raw_option_name = string(s, 1);
|
||||
|
||||
bool adjacent_parameter(false), next_parameter(false);
|
||||
if (s[1] != '\0') {
|
||||
if (!(style & short_allow_adjacent)) {
|
||||
m_error_description = ed_short_adjacent_not_allowed;
|
||||
return false;
|
||||
} else {
|
||||
adjacent_parameter = true;
|
||||
m_option_values.push_back(string(s+1));
|
||||
}
|
||||
} else {
|
||||
|
||||
if ((style & short_allow_next) && m_next) {
|
||||
option_kind kind = is_option(m_next);
|
||||
|
||||
if (kind == no_option) {
|
||||
next_parameter = true;
|
||||
} else if (kind == short_option && opt
|
||||
&& opt->properties == require_parameter)
|
||||
{
|
||||
// This handles a special case:
|
||||
// -a -1
|
||||
// where "-1" is a parameter to "-a". It's pretty
|
||||
// hard to quote "-1" in any way on the comment line, so
|
||||
// we decide that if "-a" has required parameter then -1
|
||||
// is the parameter.
|
||||
// We do so even if there's registered "-1" option,
|
||||
// since:
|
||||
// - that how getopt works
|
||||
// - it allows command line to be parsed, we'll
|
||||
// get error otherwise.
|
||||
next_parameter = true;
|
||||
}
|
||||
}
|
||||
// Reset error state that 'is_option' might have changed
|
||||
m_error_description = ed_success;
|
||||
}
|
||||
|
||||
bool ok = process_parameter(opt, adjacent_parameter, next_parameter);
|
||||
if (!ok && m_error_description == ed_extra_parameter
|
||||
&& (style & allow_sticky) && !ignore_sticky)
|
||||
if (find_short_option(s[1]) != 0) {
|
||||
m_error_description = ed_success;
|
||||
m_option_values.clear();
|
||||
pending_short_option = s+1;
|
||||
m_num_tokens = 0;
|
||||
ok = true;
|
||||
} else {
|
||||
m_error_description = ed_extra_parameter;
|
||||
}
|
||||
return ok;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
cmdline::handle_dos_option(const char* s)
|
||||
{
|
||||
return handle_short_option(s, true);
|
||||
}
|
||||
|
||||
bool
|
||||
cmdline::handle_additional_parser(const std::pair<string, string>& p)
|
||||
{
|
||||
m_option_name = p.first;
|
||||
m_raw_option_name = p.first;
|
||||
m_option_values.push_back(p.second);
|
||||
|
||||
if (p.first[0] == '-')
|
||||
m_opt = find_short_option(p.first[1]);
|
||||
else
|
||||
m_opt = find_long_option(p.first.c_str());
|
||||
|
||||
if (m_opt && !m_opt->long_name.empty())
|
||||
m_option_name = m_opt->long_name;
|
||||
else
|
||||
m_option_name = "-" + p.first.substr(1,1);
|
||||
|
||||
return process_parameter(m_opt, !p.second.empty(), false);
|
||||
}
|
||||
|
||||
/* Handles parameter assingments, setting m_option_value and
|
||||
m_num_tokens.
|
||||
|
||||
'opt' describes the detected option. If it's 0, it means the option
|
||||
is not registered, but the parser allowes unregisted options. Assumes
|
||||
that this option allows but not requires a parameter.
|
||||
|
||||
'adjacent_parameter' says if there's a parameter in the same token as
|
||||
the option. In which case it must be already assigned to m_option_value.
|
||||
|
||||
'next_parameter' says if there's next token, which can be interpreted as
|
||||
argument.
|
||||
*/
|
||||
bool
|
||||
cmdline::process_parameter(const option* opt, bool adjacent_parameter,
|
||||
bool next_parameter)
|
||||
{
|
||||
properties_t properties;
|
||||
if (opt)
|
||||
properties = opt->properties;
|
||||
else
|
||||
properties = allow_parameter;
|
||||
bool accept_parameter((properties == allow_parameter)
|
||||
|| (properties == require_parameter)
|
||||
|| (properties == allow_parameters)
|
||||
|| (properties == require_parameters));
|
||||
|
||||
bool ok(true);
|
||||
if (accept_parameter) {
|
||||
if (adjacent_parameter) {
|
||||
// Everything assigned already
|
||||
m_num_tokens = 1;
|
||||
} else {
|
||||
if (next_parameter) {
|
||||
m_option_values.push_back(m_next);
|
||||
m_num_tokens = 2;
|
||||
} else {
|
||||
// No, there's no parameter at all!
|
||||
if (properties == require_parameter) {
|
||||
m_error_description = ed_missing_parameter;
|
||||
ok = false;
|
||||
} else {
|
||||
m_num_tokens = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (adjacent_parameter) {
|
||||
m_error_description = ed_extra_parameter;
|
||||
ok = false;
|
||||
} else {
|
||||
m_num_tokens = 1;
|
||||
}
|
||||
}
|
||||
// If multiple parameters are allowed, consume every non-option
|
||||
// token
|
||||
if (properties == allow_parameters || properties == require_parameters)
|
||||
{
|
||||
// Don't use m_current and m_next, but directly iterate over
|
||||
// input.
|
||||
for(size_t i = index + 2;
|
||||
i < args.size() && is_option(args[i].c_str()) == no_option
|
||||
&& args[i] != "--";
|
||||
++i, ++m_num_tokens) {
|
||||
m_option_values.push_back(args[i]);
|
||||
}
|
||||
m_error_description = ed_success;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
cmdline::operator bool() const
|
||||
{
|
||||
return index < args.size() && m_error_description == ed_success;
|
||||
}
|
||||
|
||||
cmdline::option_kind
|
||||
cmdline::is_option(const char* s)
|
||||
{
|
||||
if (m_no_more_options)
|
||||
return no_option;
|
||||
|
||||
if (*s == '-' && *(s+1) == '-' && *(s+2) != '\0')
|
||||
if (style & allow_long)
|
||||
return long_option;
|
||||
else {
|
||||
m_error_description = ed_long_not_allowed;
|
||||
return error_option;
|
||||
}
|
||||
|
||||
if (style & allow_short)
|
||||
{
|
||||
if ((style & allow_dash_for_short) && *s == '-' && *(s+1) != '-'
|
||||
&& *(s+1) != '\0')
|
||||
return short_option;
|
||||
if ((style & allow_slash_for_short) && *s == '/')
|
||||
return dos_option;
|
||||
}
|
||||
|
||||
return no_option;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::advance(int count)
|
||||
{
|
||||
index += count;
|
||||
// Note that the 'args' array is not modified at all,
|
||||
// therefore storing results of c_str() is OK.
|
||||
m_current = index < args.size()? args[index].c_str() : 0;
|
||||
m_next = index+1 < args.size() ? args[index+1].c_str() : 0;
|
||||
}
|
||||
|
||||
cmdline::properties_t
|
||||
cmdline::translate_property(char p)
|
||||
{
|
||||
if (p == '|')
|
||||
return no_parameter;
|
||||
else if (p == '?')
|
||||
return allow_parameter;
|
||||
else if (p == ':')
|
||||
return require_parameter;
|
||||
else if (p == '*')
|
||||
return allow_parameters;
|
||||
else if (p == '+')
|
||||
return require_parameters;
|
||||
else
|
||||
throw logic_error("Invalid property character");
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::clear_error()
|
||||
{
|
||||
error_description_t e = m_error_description;
|
||||
m_error_description = ed_success;
|
||||
|
||||
invalid_command_line_syntax::kind_t re;
|
||||
|
||||
// FIXME: have no idea why g++ 3.2 wants it.
|
||||
typedef boost::program_options::unknown_option unknown_option;
|
||||
typedef boost::program_options::ambiguous_option ambiguous_option;
|
||||
|
||||
if (e) {
|
||||
if (e == ed_unknown_option)
|
||||
throw unknown_option(m_current);
|
||||
if (e == ed_ambiguous_option)
|
||||
throw ambiguous_option(m_current, vector<string>());
|
||||
|
||||
switch(e) {
|
||||
case ed_long_not_allowed:
|
||||
re = invalid_command_line_syntax::long_not_allowed;
|
||||
break;
|
||||
case ed_long_adjacent_not_allowed:
|
||||
re = invalid_command_line_syntax::long_adjacent_not_allowed;
|
||||
break;
|
||||
case ed_short_adjacent_not_allowed:
|
||||
re = invalid_command_line_syntax::short_adjacent_not_allowed;
|
||||
break;
|
||||
case ed_empty_adjacent_parameter:
|
||||
re = invalid_command_line_syntax::empty_adjacent_parameter;
|
||||
break;
|
||||
case ed_missing_parameter:
|
||||
re = invalid_command_line_syntax::missing_parameter;
|
||||
break;
|
||||
case ed_extra_parameter:
|
||||
re = invalid_command_line_syntax::extra_parameter;
|
||||
break;
|
||||
default:
|
||||
; // do nothing
|
||||
}
|
||||
throw invalid_command_line_syntax(m_current, re);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}}}
|
||||
185
src/config_file.cpp
Normal file
185
src/config_file.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <boost/program_options/detail/config_file.hpp>
|
||||
#include <boost/program_options/errors.hpp>
|
||||
#include <boost/program_options/detail/convert.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cassert>
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
|
||||
using namespace std;
|
||||
|
||||
common_config_file_iterator::common_config_file_iterator(
|
||||
const std::set<std::string>& allowed_options)
|
||||
: allowed_options(allowed_options)
|
||||
{
|
||||
for(std::set<std::string>::const_iterator i = allowed_options.begin();
|
||||
i != allowed_options.end();
|
||||
++i)
|
||||
{
|
||||
add_option(i->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
common_config_file_iterator::add_option(const char* name)
|
||||
{
|
||||
string s(name);
|
||||
assert(!s.empty());
|
||||
if (*s.rbegin() == '*') {
|
||||
s.resize(s.size()-1);
|
||||
bool bad_prefixes(false);
|
||||
// If 's' is a prefix of one of allowed suffix, then
|
||||
// lower_bound will return that element.
|
||||
// If some element is prefix of 's', then lower_bound will
|
||||
// return the next element.
|
||||
set<string>::iterator i = allowed_prefixes.lower_bound(s);
|
||||
if (i != allowed_prefixes.end()) {
|
||||
if (i->find(s) == 0)
|
||||
bad_prefixes = true;
|
||||
}
|
||||
if (i != allowed_prefixes.begin()) {
|
||||
--i;
|
||||
if (s.find(*i) == 0)
|
||||
bad_prefixes = true;
|
||||
}
|
||||
if (bad_prefixes)
|
||||
throw error("bad prefixes");
|
||||
allowed_prefixes.insert(s);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
string trim_ws(const string& s)
|
||||
{
|
||||
string::size_type n, n2;
|
||||
n = s.find_first_not_of(" \t\r\n");
|
||||
if (n == string::npos)
|
||||
return string();
|
||||
else {
|
||||
n2 = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(n, n2-n+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void common_config_file_iterator::get()
|
||||
{
|
||||
string s;
|
||||
string::size_type n;
|
||||
bool found = false;
|
||||
|
||||
while(this->getline(s)) {
|
||||
|
||||
// strip '#' comments and whitespace
|
||||
if ((n = s.find('#')) != string::npos)
|
||||
s = s.substr(0, n);
|
||||
s = trim_ws(s);
|
||||
|
||||
if (!s.empty()) {
|
||||
// Handle section name
|
||||
if (*s.begin() == '[' && *s.rbegin() == ']') {
|
||||
m_prefix = s.substr(1, s.size()-2);
|
||||
if (*m_prefix.rbegin() != '.')
|
||||
m_prefix += '.';
|
||||
}
|
||||
else if ((n = s.find('=')) != string::npos) {
|
||||
|
||||
string name = m_prefix + trim_ws(s.substr(0, n));
|
||||
string value = trim_ws(s.substr(n+1));
|
||||
|
||||
if (!allowed_option(name))
|
||||
throw unknown_option(name);
|
||||
|
||||
if (value.empty())
|
||||
throw invalid_syntax(s, "no value given");
|
||||
|
||||
found = true;
|
||||
this->value().string_key = name;
|
||||
this->value().value.clear();
|
||||
this->value().value.push_back(value);
|
||||
break;
|
||||
|
||||
} else {
|
||||
throw invalid_syntax(s, "unrecognized line");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
found_eof();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
common_config_file_iterator::allowed_option(const std::string& s) const
|
||||
{
|
||||
set<string>::const_iterator i = allowed_options.find(s);
|
||||
if (i != allowed_options.end())
|
||||
return true;
|
||||
// If s is "pa" where "p" is allowed prefix then
|
||||
// lower_bound should find the element after "p".
|
||||
// This depends on 'allowed_prefixes' invariant.
|
||||
i = allowed_prefixes.lower_bound(s);
|
||||
if (i != allowed_prefixes.begin() && s.find(*--i) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool
|
||||
basic_config_file_iterator<wchar_t>::getline(std::string& s)
|
||||
{
|
||||
std::wstring ws;
|
||||
if (std::getline(*is, ws)) {
|
||||
s = to_utf8(ws);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
#if 0
|
||||
using boost::program_options::config_file;
|
||||
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
stringstream s(
|
||||
"a = 1\n"
|
||||
"b = 2\n");
|
||||
|
||||
config_file cf(s);
|
||||
cf.add_option("a");
|
||||
cf.add_option("b");
|
||||
|
||||
assert(++cf);
|
||||
assert(cf.name() == "a");
|
||||
assert(cf.value() == "1");
|
||||
assert(++cf);
|
||||
assert(cf.name() == "b");
|
||||
assert(cf.value() == "2");
|
||||
assert(!++cf);
|
||||
}
|
||||
catch(exception& e)
|
||||
{
|
||||
cout << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
138
src/convert.cpp
Normal file
138
src/convert.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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 <fstream>
|
||||
#include <locale.h>
|
||||
#include <locale>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <locale>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace boost { namespace detail {
|
||||
|
||||
/* Internal function to acutally pefrom convertion.
|
||||
The logic in from_8_bit and to_8_bit function is exactly
|
||||
the same, except that one calls 'in' method of codecvt and another
|
||||
calls the 'out' method, and that syntax different makes straightforward
|
||||
template implementation impossible.
|
||||
|
||||
This functions takes a 'fun' argument, which should have the same
|
||||
parameters and return type and the in/out methods. The actual converting
|
||||
function will pass functional objects created with boost::bind.
|
||||
Experiments show that the performance loss is withing 10%.
|
||||
*/
|
||||
template<class ToChar, class FromChar, class Fun>
|
||||
std::basic_string<ToChar>
|
||||
convert(const std::basic_string<FromChar>& s, Fun fun)
|
||||
|
||||
{
|
||||
std::basic_string<ToChar> result;
|
||||
|
||||
std::mbstate_t state = {0};
|
||||
|
||||
const FromChar* from = s.data();
|
||||
const FromChar* from_end = s.data() + s.size();
|
||||
// The interace of cvt is not really iterator-like, and it's
|
||||
// not possible the tell the required output size without the conversion.
|
||||
// All we can is convert data by pieces.
|
||||
while(from != from_end) {
|
||||
|
||||
// std::basic_string does not provide non-const pointers to the data,
|
||||
// so converting directly into string is not possible.
|
||||
ToChar buffer[32];
|
||||
|
||||
ToChar* to_next = buffer;
|
||||
// Need variable bacause boost::bind doesn't work with rvalues.
|
||||
ToChar* to_end = buffer + 32;
|
||||
std::codecvt_base::result r =
|
||||
fun(state, from, from_end, from, buffer, to_end, to_next);
|
||||
|
||||
if (r == std::codecvt_base::error)
|
||||
throw std::logic_error("character conversion failed");
|
||||
// 'partial' is not an error, it just means not all source characters
|
||||
// we converted. However, we need to check that at least one new target
|
||||
// character was produced. If not, it means the source data is
|
||||
// incomplete, and since we don't have extra data to add to source, it's
|
||||
// error.
|
||||
if (to_next == buffer)
|
||||
throw std::logic_error("character conversion failed");
|
||||
|
||||
// Add converted characters
|
||||
result.append(buffer, to_next);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}}
|
||||
|
||||
namespace boost {
|
||||
|
||||
std::wstring
|
||||
from_8_bit(const std::string& s,
|
||||
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt)
|
||||
{
|
||||
return detail::convert<wchar_t>(
|
||||
s,
|
||||
boost::bind(boost::mem_fn(&codecvt<wchar_t, char, mbstate_t>::in),
|
||||
&cvt,
|
||||
_1, _2, _3, _4, _5, _6, _7));
|
||||
}
|
||||
|
||||
std::string
|
||||
to_8_bit(const std::wstring& s,
|
||||
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt)
|
||||
{
|
||||
return detail::convert<char>(
|
||||
s,
|
||||
boost::bind(boost::mem_fn(&codecvt<wchar_t, char, mbstate_t>::out),
|
||||
&cvt,
|
||||
_1, _2, _3, _4, _5, _6, _7));
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
boost::program_options::detail::utf8_codecvt_facet<wchar_t, char>
|
||||
utf8_facet;
|
||||
}
|
||||
|
||||
|
||||
std::wstring
|
||||
from_utf8(const std::string& s)
|
||||
{
|
||||
return from_8_bit(s, utf8_facet);
|
||||
}
|
||||
|
||||
std::string
|
||||
to_utf8(const std::wstring& s)
|
||||
{
|
||||
return to_8_bit(s, utf8_facet);
|
||||
}
|
||||
|
||||
std::wstring
|
||||
from_local_8_bit(const std::string& s)
|
||||
{
|
||||
return from_8_bit(s,
|
||||
use_facet< codecvt<wchar_t, char, mbstate_t> >(
|
||||
locale()));
|
||||
}
|
||||
|
||||
std::string
|
||||
to_local_8_bit(const std::wstring& s)
|
||||
{
|
||||
return to_8_bit(s,
|
||||
use_facet< codecvt<wchar_t, char, mbstate_t> >(
|
||||
locale()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
28
src/option.cpp
Normal file
28
src/option.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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/option.hpp>
|
||||
#include <boost/program_options/detail/convert.hpp>
|
||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
namespace {
|
||||
|
||||
}
|
||||
|
||||
woption::woption(const option& opt)
|
||||
{
|
||||
this->string_key = opt.string_key;
|
||||
this->position_key = opt.position_key;
|
||||
|
||||
std::transform(opt.value.begin(), opt.value.end(),
|
||||
back_inserter(value),
|
||||
bind(from_8_bit, _1, ref(facet)));
|
||||
}
|
||||
|
||||
}}
|
||||
332
src/options_description.cpp
Normal file
332
src/options_description.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
// FIXME: this is only to get multiple_occureces class
|
||||
// should move that to a separate headers.
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <boost/detail/workaround.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <sstream>
|
||||
using namespace std;
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
option_description::option_description()
|
||||
{
|
||||
}
|
||||
|
||||
option_description::
|
||||
option_description(const char* name,
|
||||
const value_semantic* s)
|
||||
: m_value_semantic(s)
|
||||
{
|
||||
this->name(name);
|
||||
}
|
||||
|
||||
|
||||
option_description::
|
||||
option_description(const char* name,
|
||||
const value_semantic* s,
|
||||
const char* description)
|
||||
: m_description(description), m_value_semantic(s)
|
||||
{
|
||||
this->name(name);
|
||||
}
|
||||
|
||||
option_description::~option_description()
|
||||
{
|
||||
}
|
||||
|
||||
option_description&
|
||||
option_description::name(const char* _name)
|
||||
{
|
||||
std::string name(_name);
|
||||
string::size_type n = name.find(',');
|
||||
if (n != string::npos) {
|
||||
assert(n == name.size()-2);
|
||||
m_long_name = name.substr(0, n);
|
||||
m_short_name = name.substr(n+1,1);
|
||||
} else {
|
||||
m_long_name = name;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::string&
|
||||
option_description::short_name() const
|
||||
{
|
||||
return m_short_name;
|
||||
}
|
||||
|
||||
const std::string&
|
||||
option_description::long_name() const
|
||||
{
|
||||
return m_long_name;
|
||||
}
|
||||
|
||||
const std::string&
|
||||
option_description::description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
shared_ptr<const value_semantic>
|
||||
option_description::semantic() const
|
||||
{
|
||||
return m_value_semantic;
|
||||
}
|
||||
|
||||
std::string
|
||||
option_description::format_name() const
|
||||
{
|
||||
if (!short_name().empty())
|
||||
return string("-").append(short_name()).append(" [ --").
|
||||
append(long_name()).append(" ]");
|
||||
else
|
||||
return string("--").append(long_name());
|
||||
}
|
||||
|
||||
std::string
|
||||
option_description::format_parameter() const
|
||||
{
|
||||
if (!m_value_semantic->zero_tokens())
|
||||
return m_value_semantic->name();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
options_description_easy_init::
|
||||
options_description_easy_init(options_description* owner)
|
||||
: owner(owner)
|
||||
{}
|
||||
|
||||
options_description_easy_init&
|
||||
options_description_easy_init::
|
||||
operator()(const char* name,
|
||||
const char* description)
|
||||
{
|
||||
// Create untypes semantic which accepts zero tokens: i.e.
|
||||
// no value can be specified on command line.
|
||||
// FIXME: does not look exception-safe
|
||||
shared_ptr<option_description> d(
|
||||
new option_description(name, new untyped_value(true), description));
|
||||
|
||||
owner->add(d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
options_description_easy_init&
|
||||
options_description_easy_init::
|
||||
operator()(const char* name,
|
||||
const value_semantic* s)
|
||||
{
|
||||
shared_ptr<option_description> d(new option_description(name, s));
|
||||
owner->add(d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
options_description_easy_init&
|
||||
options_description_easy_init::
|
||||
operator()(const char* name,
|
||||
const value_semantic* s,
|
||||
const char* description)
|
||||
{
|
||||
shared_ptr<option_description> d(new option_description(name, s, description));
|
||||
|
||||
owner->add(d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
options_description::options_description()
|
||||
{}
|
||||
|
||||
options_description::options_description(const string& caption)
|
||||
: m_caption(caption)
|
||||
{}
|
||||
|
||||
void
|
||||
options_description::add(shared_ptr<option_description> desc)
|
||||
{
|
||||
const string& s = desc->short_name();
|
||||
const string& l = desc->long_name();
|
||||
assert(!s.empty() || !l.empty());
|
||||
if (!s.empty())
|
||||
if (name2index.count("-" + s) != 0)
|
||||
throw duplicate_option_error("Short name '" + s + "' is already present");
|
||||
else
|
||||
name2index["-" + s] = options.size();
|
||||
if (!l.empty())
|
||||
if (name2index.count(s) != 0)
|
||||
throw duplicate_option_error("Long name '" + s + "' is already present");
|
||||
else
|
||||
name2index[l] = options.size();
|
||||
options.push_back(desc);
|
||||
belong_to_group.push_back(false);
|
||||
}
|
||||
|
||||
options_description&
|
||||
options_description::add(const options_description& desc)
|
||||
{
|
||||
groups.push_back(desc);
|
||||
|
||||
for (size_t i = 0; i < desc.options.size(); ++i) {
|
||||
add(desc.options[i]);
|
||||
belong_to_group.back() = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
options_description_easy_init
|
||||
options_description::add_options()
|
||||
{
|
||||
return options_description_easy_init(this);
|
||||
}
|
||||
|
||||
unsigned
|
||||
options_description::count(const std::string& name) const
|
||||
{
|
||||
return name2index.count(name);
|
||||
}
|
||||
|
||||
unsigned
|
||||
options_description::count_approx(const std::string& prefix) const
|
||||
{
|
||||
approximation_range er = find_approximation(prefix);
|
||||
return distance(er.first, er.second);
|
||||
}
|
||||
|
||||
const option_description&
|
||||
options_description::find(const std::string& name) const
|
||||
{
|
||||
assert(this->count(name) != 0);
|
||||
return *options[name2index.find(name)->second];
|
||||
}
|
||||
|
||||
const option_description&
|
||||
options_description::find_approx(const std::string& prefix) const
|
||||
{
|
||||
approximation_range er = find_approximation(prefix);
|
||||
assert(distance(er.first, er.second) == 1);
|
||||
return *options[er.first->second];
|
||||
}
|
||||
|
||||
std::set<std::string>
|
||||
options_description::keys() const
|
||||
{
|
||||
set<string> result;
|
||||
for (map<string, int>::const_iterator i = name2index.begin();
|
||||
i != name2index.end();
|
||||
++i)
|
||||
result.insert(i->first);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::set<std::string>
|
||||
options_description::primary_keys() const
|
||||
{
|
||||
set<string> result;
|
||||
for (size_t i = 0; i < options.size(); ++i)
|
||||
if (options[i]->long_name().empty())
|
||||
result.insert("-" + options[i]->short_name());
|
||||
else
|
||||
result.insert(options[i]->long_name());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::set<std::string>
|
||||
options_description::approximations(const std::string& prefix) const
|
||||
{
|
||||
approximation_range er = find_approximation(prefix);
|
||||
set<string> result;
|
||||
for (name2index_iterator i = er.first; i != er.second; ++i)
|
||||
result.insert(i->first);
|
||||
return result;
|
||||
}
|
||||
|
||||
options_description::approximation_range
|
||||
options_description::find_approximation(const std::string& prefix) const
|
||||
{
|
||||
name2index_iterator b = name2index.lower_bound(prefix);
|
||||
name2index_iterator e = name2index.upper_bound(prefix + char(CHAR_MAX));
|
||||
return make_pair(b, e);
|
||||
}
|
||||
|
||||
|
||||
DECL
|
||||
std::ostream& operator<<(std::ostream& os, const options_description& desc)
|
||||
{
|
||||
desc.print(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void format_one(std::ostream& os, const option_description& opt,
|
||||
unsigned first_column_width)
|
||||
{
|
||||
stringstream ss;
|
||||
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
|
||||
|
||||
// Don't use ss.rdbuf() since g++ 2.96 is buggy on it.
|
||||
os << ss.str();
|
||||
|
||||
if (!opt.description().empty()) {
|
||||
|
||||
for(int pad = first_column_width - ss.str().size(); pad > 0; --pad) {
|
||||
os.put(' ');
|
||||
}
|
||||
os << " : " << opt.description();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
options_description::print(std::ostream& os) const
|
||||
{
|
||||
if (!m_caption.empty())
|
||||
os << m_caption << ":\n";
|
||||
|
||||
/* Find the maximum width of the option column */
|
||||
unsigned width(24);
|
||||
for (unsigned i = 0; i < options.size(); ++i)
|
||||
{
|
||||
const option_description& opt = *options[i];
|
||||
stringstream ss;
|
||||
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
|
||||
width = max(width, ss.str().size());
|
||||
}
|
||||
|
||||
/* The options formatting style is stolen from Subversion. */
|
||||
for (unsigned i = 0; i < options.size(); ++i)
|
||||
{
|
||||
if (belong_to_group[i])
|
||||
continue;
|
||||
|
||||
const option_description& opt = *options[i];
|
||||
|
||||
format_one(os, opt, width);
|
||||
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < groups.size(); ++j) {
|
||||
os << "\n" << groups[j];
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
347
src/parsers.cpp
Normal file
347
src/parsers.cpp
Normal file
@@ -0,0 +1,347 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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/config.hpp>
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/positional_options.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
|
||||
|
||||
#include <boost/program_options/detail/cmdline.hpp>
|
||||
#include <boost/program_options/detail/config_file.hpp>
|
||||
#include <boost/program_options/environment_iterator.hpp>
|
||||
#include <boost/program_options/detail/convert.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
|
||||
#if !defined(__GNUC__) || __GNUC__ < 3
|
||||
#include <iostream>
|
||||
#else
|
||||
#include <istream>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// It appears that on Mac OS X the 'environ' variable is not
|
||||
// available to dynamically linked libraries.
|
||||
#if defined(__APPLE__) && defined(__DYNAMIC__)
|
||||
#include <crt_externs.h>
|
||||
static char** environ = *_NSGetEnviron();
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
namespace {
|
||||
woption woption_from_option(const option& opt)
|
||||
{
|
||||
woption result;
|
||||
result.string_key = opt.string_key;
|
||||
result.position_key = opt.position_key;
|
||||
|
||||
std::transform(opt.value.begin(), opt.value.end(),
|
||||
back_inserter(result.value),
|
||||
bind(from_utf8, _1));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
basic_parsed_options<wchar_t>
|
||||
::basic_parsed_options(const parsed_options& po)
|
||||
: description(po.description),
|
||||
utf8_encoded_options(po)
|
||||
{
|
||||
for (unsigned i = 0; i < po.options.size(); ++i)
|
||||
options.push_back(woption_from_option(po.options[i]));
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
void
|
||||
parse_command_line(cmdline& cmd, parsed_options& result);
|
||||
}
|
||||
|
||||
basic_command_line_parser<char>::
|
||||
basic_command_line_parser(const std::vector<std::string>& args)
|
||||
: m_style(0), m_desc(0), m_positional(0), m_args(args)
|
||||
{}
|
||||
|
||||
basic_command_line_parser<char>::
|
||||
basic_command_line_parser(int argc, char* argv[])
|
||||
: m_style(0), m_desc(0), m_positional(0)
|
||||
#if ! defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
|
||||
,m_args(argv+1, argv+argc)
|
||||
#endif
|
||||
{
|
||||
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
|
||||
m_args.reserve(argc);
|
||||
copy(argv+1, argv+argc, inserter(m_args, m_args.end()));
|
||||
#endif
|
||||
}
|
||||
|
||||
parsed_options
|
||||
basic_command_line_parser<char>::run() const
|
||||
{
|
||||
parsed_options result(m_desc);
|
||||
detail::cmdline cmd(m_args, m_style, true);
|
||||
cmd.set_additional_parser(m_ext);
|
||||
|
||||
if (m_desc) {
|
||||
set<string> keys = m_desc->primary_keys();
|
||||
for (set<string>::iterator i = keys.begin(); i != keys.end(); ++i) {
|
||||
const option_description& d = m_desc->find(*i);
|
||||
char s = d.short_name().empty() ? '\0' : d.short_name()[0];
|
||||
|
||||
shared_ptr<const value_semantic> vs = d.semantic();
|
||||
char flags;
|
||||
if (vs->zero_tokens())
|
||||
flags = '|';
|
||||
else
|
||||
if (vs->is_implicit())
|
||||
if (vs->is_multitoken())
|
||||
flags = '*';
|
||||
else
|
||||
flags = '?';
|
||||
else if (vs->is_multitoken())
|
||||
flags = '+';
|
||||
else flags = ':';
|
||||
|
||||
cmd.add_option(d.long_name(), s, flags, 1);
|
||||
}
|
||||
}
|
||||
|
||||
parse_command_line(cmd, result);
|
||||
|
||||
if (m_positional)
|
||||
{
|
||||
unsigned position = 0;
|
||||
for (unsigned i = 0; i < result.options.size(); ++i) {
|
||||
option& opt = result.options[i];
|
||||
if (opt.position_key != -1) {
|
||||
if (position >= m_positional->max_total_count())
|
||||
{
|
||||
throw too_many_positional_options_error(
|
||||
"too much positional options");
|
||||
}
|
||||
opt.string_key = m_positional->name_for_position(position);
|
||||
++position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::vector<string> utf8_args(
|
||||
const std::vector<std::wstring>& args)
|
||||
{
|
||||
std::vector<string> r;
|
||||
r.reserve(args.size());
|
||||
transform(args.begin(), args.end(), back_inserter(r),
|
||||
bind(to_utf8, _1));
|
||||
return r;
|
||||
}
|
||||
|
||||
// This version exists since some compiler crash on
|
||||
// template vector ctor, so we can't just create vector inline
|
||||
// and pass it to the above function.
|
||||
std::vector<string> utf8_args(int argc, wchar_t* argv[])
|
||||
{
|
||||
std::vector<string> r;
|
||||
r.reserve(argc);
|
||||
transform(argv + 1, argv + argc, back_inserter(r),
|
||||
bind(to_utf8, _1));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
basic_command_line_parser<wchar_t>::
|
||||
basic_command_line_parser(const std::vector<std::wstring>& args)
|
||||
: m_style(0), m_desc(0), m_positional(0), m_args(utf8_args(args))
|
||||
{}
|
||||
|
||||
basic_command_line_parser<wchar_t>::
|
||||
basic_command_line_parser(int argc, wchar_t* argv[])
|
||||
: m_style(0), m_desc(0), m_positional(0), m_args(utf8_args(argc, argv))
|
||||
{}
|
||||
|
||||
wparsed_options
|
||||
basic_command_line_parser<wchar_t>::run() const
|
||||
{
|
||||
command_line_parser ascii_parser(m_args);
|
||||
ascii_parser.style(m_style);
|
||||
if (m_desc)
|
||||
ascii_parser.options(*m_desc);
|
||||
if (m_positional)
|
||||
ascii_parser.positional(*m_positional);
|
||||
ascii_parser.extra_parser(m_ext);
|
||||
|
||||
return wparsed_options(ascii_parser.run());
|
||||
}
|
||||
|
||||
|
||||
namespace detail {
|
||||
void
|
||||
parse_command_line(cmdline& cmd, parsed_options& result)
|
||||
{
|
||||
int position(0);
|
||||
|
||||
while(++cmd) {
|
||||
|
||||
option n;
|
||||
|
||||
if (cmd.at_option()) {
|
||||
if (*cmd.option_name().rbegin() != '*') {
|
||||
n.string_key = cmd.option_name();
|
||||
}
|
||||
else {
|
||||
n.string_key = cmd.raw_option_name();
|
||||
}
|
||||
n.value = cmd.option_values();
|
||||
} else {
|
||||
n.position_key = position++;
|
||||
n.value.clear();
|
||||
n.value.push_back(cmd.argument());
|
||||
}
|
||||
result.options.push_back(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class charT>
|
||||
basic_parsed_options<charT>
|
||||
parse_config_file(std::basic_istream<charT>& is,
|
||||
const options_description& desc)
|
||||
{
|
||||
set<string> allowed_options;
|
||||
set<string> pm = desc.primary_keys();
|
||||
for (set<string>::iterator i = pm.begin(); i != pm.end(); ++i) {
|
||||
const option_description& d = desc.find(*i);
|
||||
|
||||
if (d.long_name().empty())
|
||||
throw error("long name required for config file");
|
||||
|
||||
allowed_options.insert(d.long_name());
|
||||
}
|
||||
|
||||
// Parser return char strings
|
||||
parsed_options result(&desc);
|
||||
copy(detail::basic_config_file_iterator<charT>(is, allowed_options),
|
||||
detail::basic_config_file_iterator<charT>(),
|
||||
back_inserter(result.options));
|
||||
// Convert char strings into desired type.
|
||||
return basic_parsed_options<charT>(result);
|
||||
}
|
||||
|
||||
template
|
||||
basic_parsed_options<char>
|
||||
parse_config_file(std::basic_istream<char>& is,
|
||||
const options_description& desc);
|
||||
|
||||
template
|
||||
basic_parsed_options<wchar_t>
|
||||
parse_config_file(std::basic_istream<wchar_t>& is,
|
||||
const options_description& desc);
|
||||
|
||||
// This versio, which accepts any options without validation, is disabled,
|
||||
// in the hope that nobody will need it and we cant drop it altogether.
|
||||
// Besides, probably the right way to handle all options is the '*' name.
|
||||
#if 0
|
||||
DECL parsed_options
|
||||
parse_config_file(std::istream& is)
|
||||
{
|
||||
detail::config_file_iterator cf(is, false);
|
||||
parsed_options result(0);
|
||||
copy(cf, detail::config_file_iterator(),
|
||||
back_inserter(result.options));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
DECL parsed_options
|
||||
parse_environment(const options_description& desc,
|
||||
const function1<std::string, std::string>& name_mapper)
|
||||
{
|
||||
parsed_options result(&desc);
|
||||
|
||||
// MinGW unconditionally #defines 'environ',
|
||||
// so the following line won't compile.
|
||||
// If initialization is in the same line as declaration
|
||||
// VC 7.1 parser chokes.
|
||||
// Finally, if the variable is called 'environ', then VC refuses
|
||||
// to link.
|
||||
char **env;
|
||||
#if defined(_WIN32) && !defined( __MINGW32__ )
|
||||
env = _environ;
|
||||
#else
|
||||
env = environ;
|
||||
#endif
|
||||
for(environment_iterator i(environ), e; i != e; ++i) {
|
||||
string option_name = name_mapper(i->first);
|
||||
|
||||
if (!option_name.empty()) {
|
||||
option n;
|
||||
n.string_key = option_name;
|
||||
n.value.push_back(i->second);
|
||||
result.options.push_back(n);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class prefix_name_mapper {
|
||||
public:
|
||||
prefix_name_mapper(const std::string& prefix)
|
||||
: prefix(prefix)
|
||||
{}
|
||||
|
||||
std::string operator()(const std::string& s)
|
||||
{
|
||||
string result;
|
||||
if (s.find(prefix) == 0) {
|
||||
for(string::size_type n = prefix.size(); n < s.size(); ++n)
|
||||
{
|
||||
result.push_back(tolower(s[n]));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private:
|
||||
std::string prefix;
|
||||
};
|
||||
}
|
||||
|
||||
DECL parsed_options
|
||||
parse_environment(const options_description& desc,
|
||||
const std::string& prefix)
|
||||
{
|
||||
return parse_environment(desc, prefix_name_mapper(prefix));
|
||||
}
|
||||
|
||||
DECL parsed_options
|
||||
parse_environment(const options_description& desc, const char* prefix)
|
||||
{
|
||||
return parse_environment(desc, string(prefix));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}}
|
||||
54
src/positional_options.cpp
Normal file
54
src/positional_options.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <boost/program_options/positional_options.hpp>
|
||||
|
||||
#include <boost/limits.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
positional_options_description::positional_options_description()
|
||||
{}
|
||||
|
||||
void
|
||||
positional_options_description::add(const char* name, int max_count)
|
||||
{
|
||||
assert(max_count != -1 || m_trailing.empty());
|
||||
|
||||
if (max_count == -1)
|
||||
m_trailing = name;
|
||||
else {
|
||||
m_names.resize(m_names.size() + max_count, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsigned
|
||||
positional_options_description::max_total_count() const
|
||||
{
|
||||
return m_trailing.empty() ?
|
||||
m_names.size() : std::numeric_limits<unsigned>::max();
|
||||
}
|
||||
|
||||
const std::string&
|
||||
positional_options_description::name_for_position(unsigned position) const
|
||||
{
|
||||
assert(position < max_total_count());
|
||||
|
||||
if (position < m_names.size())
|
||||
return m_names[position];
|
||||
else
|
||||
return m_trailing;
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
345
src/utf8_codecvt_facet.cpp
Normal file
345
src/utf8_codecvt_facet.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// utf8_codecvt_facet.cpp
|
||||
|
||||
// Copyright © 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
|
||||
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu).
|
||||
// Use, modification and distribution is subject to 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 <cstdlib> // for multi-byte converson routines
|
||||
|
||||
#include <cassert>
|
||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// implementation for wchar_t
|
||||
|
||||
// Translate incoming UTF-8 into UCS-4
|
||||
std::codecvt_base::result utf8_codecvt_facet_wchar_t::do_in(
|
||||
mbstate_t& state,
|
||||
const char * from,
|
||||
const char * from_end,
|
||||
const char * & from_next,
|
||||
wchar_t * to,
|
||||
wchar_t * to_end,
|
||||
wchar_t * & to_next
|
||||
) const {
|
||||
// Basic algorithm: The first octet determines how many
|
||||
// octets total make up the UCS-4 character. The remaining
|
||||
// "continuing octets" all begin with "10". To convert, subtract
|
||||
// the amount that specifies the number of octets from the first
|
||||
// octet. Subtract 0x80 (1000 0000) from each continuing octet,
|
||||
// then mash the whole lot together. Note that each continuing
|
||||
// octet only uses 6 bits as unique values, so only shift by
|
||||
// multiples of 6 to combine.
|
||||
while (from != from_end && to != to_end) {
|
||||
|
||||
// Error checking on the first octet
|
||||
if (invalid_leading_octet(*from)){
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
return std::codecvt_base::error;
|
||||
}
|
||||
|
||||
// The first octet is adjusted by a value dependent upon
|
||||
// the number of "continuing octets" encoding the character
|
||||
const int cont_octet_count = get_cont_octet_count(*from);
|
||||
const wchar_t octet1_modifier_table[] = {
|
||||
0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
|
||||
};
|
||||
|
||||
// The unsigned char conversion is necessary in case char is
|
||||
// signed (I learned this the hard way)
|
||||
wchar_t ucs_result =
|
||||
(unsigned char)(*from++) - octet1_modifier_table[cont_octet_count];
|
||||
|
||||
// Invariants :
|
||||
// 1) At the start of the loop, 'i' continuing characters have been
|
||||
// processed
|
||||
// 2) *from points to the next continuing character to be processed.
|
||||
int i = 0;
|
||||
while(i != cont_octet_count && from != from_end) {
|
||||
|
||||
// Error checking on continuing characters
|
||||
if (invalid_continuing_octet(*from)) {
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
return std::codecvt_base::error;
|
||||
}
|
||||
|
||||
ucs_result *= (1 << 6);
|
||||
|
||||
// each continuing character has an extra (10xxxxxx)b attached to
|
||||
// it that must be removed.
|
||||
ucs_result += (unsigned char)(*from++) - 0x80;
|
||||
++i;
|
||||
}
|
||||
|
||||
// If the buffer ends with an incomplete unicode character...
|
||||
if (from == from_end && i != cont_octet_count) {
|
||||
// rewind "from" to before the current character translation
|
||||
from_next = from - (i+1);
|
||||
to_next = to;
|
||||
return std::codecvt_base::partial;
|
||||
}
|
||||
*to++ = ucs_result;
|
||||
}
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
|
||||
// Were we done converting or did we run out of destination space?
|
||||
if(from == from_end) return std::codecvt_base::ok;
|
||||
else return std::codecvt_base::partial;
|
||||
}
|
||||
|
||||
std::codecvt_base::result utf8_codecvt_facet_wchar_t::do_out(
|
||||
mbstate_t& state,
|
||||
const wchar_t * from,
|
||||
const wchar_t * from_end,
|
||||
const wchar_t * & from_next,
|
||||
char * to,
|
||||
char * to_end,
|
||||
char * & to_next
|
||||
) const
|
||||
{
|
||||
// RG - consider merging this table with the other one
|
||||
const wchar_t octet1_modifier_table[] = {
|
||||
0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
|
||||
};
|
||||
|
||||
while (from != from_end && to != to_end) {
|
||||
|
||||
// Check for invalid UCS-4 character
|
||||
if (*from > WCHAR_MAX) {
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
return std::codecvt_base::error;
|
||||
}
|
||||
|
||||
int cont_octet_count = get_cont_octet_out_count(*from);
|
||||
|
||||
// RG - comment this formula better
|
||||
int shift_exponent = (cont_octet_count) * 6;
|
||||
|
||||
// Process the first character
|
||||
*to++ = octet1_modifier_table[cont_octet_count] +
|
||||
(unsigned char)(*from / (1 << shift_exponent));
|
||||
|
||||
// Process the continuation characters
|
||||
// Invariants: At the start of the loop:
|
||||
// 1) 'i' continuing octets have been generated
|
||||
// 2) '*to' points to the next location to place an octet
|
||||
// 3) shift_exponent is 6 more than needed for the next octet
|
||||
int i = 0;
|
||||
while (i != cont_octet_count && to != to_end) {
|
||||
shift_exponent -= 6;
|
||||
*to++ = 0x80 + ((*from / (1 << shift_exponent)) % (1 << 6));
|
||||
++i;
|
||||
}
|
||||
// If we filled up the out buffer before encoding the character
|
||||
if(to == to_end && i != cont_octet_count) {
|
||||
from_next = from;
|
||||
to_next = to - (i+1);
|
||||
return std::codecvt_base::partial;
|
||||
}
|
||||
*from++;
|
||||
}
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
// Were we done or did we run out of destination space
|
||||
if(from == from_end) return std::codecvt_base::ok;
|
||||
else return std::codecvt_base::partial;
|
||||
}
|
||||
|
||||
// How many char objects can I process to get <= max_limit
|
||||
// wchar_t objects?
|
||||
int utf8_codecvt_facet_wchar_t::do_length(
|
||||
const mbstate_t &,
|
||||
const char * from,
|
||||
const char * from_end,
|
||||
size_t max_limit
|
||||
) const throw()
|
||||
{
|
||||
// RG - this code is confusing! I need a better way to express it.
|
||||
// and test cases.
|
||||
|
||||
// Invariants:
|
||||
// 1) last_octet_count has the size of the last measured character
|
||||
// 2) char_count holds the number of characters shown to fit
|
||||
// within the bounds so far (no greater than max_limit)
|
||||
// 3) from_next points to the octet 'last_octet_count' before the
|
||||
// last measured character.
|
||||
int last_octet_count=0;
|
||||
size_t char_count = 0;
|
||||
const char* from_next = from;
|
||||
// Use "<" because the buffer may represent incomplete characters
|
||||
while (from_next+last_octet_count <= from_end && char_count <= max_limit) {
|
||||
from_next += last_octet_count;
|
||||
last_octet_count = (get_octet_count(*from_next));
|
||||
++char_count;
|
||||
}
|
||||
return from_next-from_end;
|
||||
}
|
||||
|
||||
unsigned int utf8_codecvt_facet_wchar_t::get_octet_count(
|
||||
unsigned char lead_octet
|
||||
){
|
||||
// if the 0-bit (MSB) is 0, then 1 character
|
||||
if (lead_octet <= 0x7f) return 1;
|
||||
|
||||
// Otherwise the count number of consecutive 1 bits starting at MSB
|
||||
// assert(0xc0 <= lead_octet && lead_octet <= 0xfd);
|
||||
|
||||
if (0xc0 <= lead_octet && lead_octet <= 0xdf) return 2;
|
||||
else if (0xe0 <= lead_octet && lead_octet <= 0xef) return 3;
|
||||
else if (0xf0 <= lead_octet && lead_octet <= 0xf7) return 4;
|
||||
else if (0xf8 <= lead_octet && lead_octet <= 0xfb) return 5;
|
||||
else return 6;
|
||||
}
|
||||
}}}
|
||||
|
||||
namespace {
|
||||
template<size_t s>
|
||||
int get_cont_octet_out_count_impl(wchar_t word){
|
||||
if (word < 0x80) {
|
||||
return 0;
|
||||
}
|
||||
if (word < 0x800) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
template<>
|
||||
int get_cont_octet_out_count_impl<4>(wchar_t word){
|
||||
if (word < 0x80) {
|
||||
return 0;
|
||||
}
|
||||
if (word < 0x800) {
|
||||
return 1;
|
||||
}
|
||||
if (word < 0x10000) {
|
||||
return 2;
|
||||
}
|
||||
if (word < 0x200000) {
|
||||
return 3;
|
||||
}
|
||||
if (word < 0x4000000) {
|
||||
return 4;
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
// How many "continuing octets" will be needed for this word
|
||||
// == total octets - 1.
|
||||
int utf8_codecvt_facet_wchar_t::get_cont_octet_out_count(
|
||||
wchar_t word
|
||||
) const {
|
||||
return get_cont_octet_out_count_impl<sizeof(wchar_t)>(word);
|
||||
}
|
||||
}}}
|
||||
|
||||
#if 0 // not used?
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// implementation for char
|
||||
|
||||
std::codecvt_base::result utf8_codecvt_facet_char::do_in(
|
||||
mbstate_t & state,
|
||||
const char * from,
|
||||
const char * from_end,
|
||||
const char * & from_next,
|
||||
char * to,
|
||||
char * to_end,
|
||||
char * & to_next
|
||||
) const
|
||||
{
|
||||
while(from_next < from_end){
|
||||
wchar_t w;
|
||||
wchar_t *wnext = & w;
|
||||
utf8_codecvt_facet_wchar_t::result ucs4_result;
|
||||
ucs4_result = base_class::do_in(
|
||||
state,
|
||||
from, from_end, from_next,
|
||||
wnext, wnext + 1, wnext
|
||||
);
|
||||
if(codecvt_base::ok != ucs4_result)
|
||||
return ucs4_result;
|
||||
// if the conversion succeeds.
|
||||
int length = std::wctomb(to_next, w);
|
||||
// assert(-1 != length);
|
||||
to_next += length;
|
||||
}
|
||||
return codecvt_base::ok;
|
||||
}
|
||||
|
||||
std::codecvt_base::result utf8_codecvt_facet_char::do_out(
|
||||
std::mbstate_t & state,
|
||||
const char * from,
|
||||
const char * from_end,
|
||||
const char * & from_next,
|
||||
char * to,
|
||||
char * to_end,
|
||||
char * & to_next
|
||||
) const
|
||||
{
|
||||
while(from_next < from_end){
|
||||
wchar_t w;
|
||||
int result = std::mbtowc(&w, from_next, MB_LENGTH_MAX);
|
||||
// assert(-1 != result);
|
||||
from_next += result;
|
||||
utf8_codecvt_facet_wchar_t::result ucs4_result;
|
||||
|
||||
const wchar_t *wptr = & w;
|
||||
ucs4_result = base_class::do_out(
|
||||
state,
|
||||
wptr, wptr+1, wptr,
|
||||
to_next, to_end, to_next
|
||||
);
|
||||
if(codecvt_base::ok != ucs4_result)
|
||||
return ucs4_result;
|
||||
}
|
||||
return codecvt_base::ok;
|
||||
}
|
||||
|
||||
// How many bytes objects can I process to get <= max_limit
|
||||
// char objects?
|
||||
int utf8_codecvt_facet_char::do_length(
|
||||
const mbstate_t & initial_state,
|
||||
const char * from_next,
|
||||
const char * from_end,
|
||||
size_t max_limit
|
||||
) const
|
||||
{
|
||||
int total_length = 0;
|
||||
const char *from = from_next;
|
||||
mbstate_t state = initial_state;
|
||||
while(from_next < from_end){
|
||||
wchar_t w;
|
||||
wchar_t *wnext = & w;
|
||||
utf8_codecvt_facet_wchar_t::result ucs4_result;
|
||||
ucs4_result = base_class::do_in(
|
||||
state,
|
||||
from_next, from_end, from_next,
|
||||
wnext, wnext + 1, wnext
|
||||
);
|
||||
|
||||
if(codecvt_base::ok != ucs4_result)
|
||||
break;
|
||||
|
||||
char carray[MB_LENGTH_MAX];
|
||||
size_t count = wctomb(carray, w);
|
||||
if(count > max_limit)
|
||||
break;
|
||||
|
||||
max_limit -= count;
|
||||
total_length = from_next - from;
|
||||
}
|
||||
return total_length;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
182
src/value_semantic.cpp
Normal file
182
src/value_semantic.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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)
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/value_semantic.hpp>
|
||||
#include <boost/program_options/detail/convert.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
using namespace std;
|
||||
|
||||
void
|
||||
value_semantic_codecvt_helper<char>::
|
||||
parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens,
|
||||
bool utf8) const
|
||||
{
|
||||
if (utf8) {
|
||||
// Need to convert to local encoding.
|
||||
std::vector<string> local_tokens;
|
||||
for (unsigned i = 0; i < new_tokens.size(); ++i) {
|
||||
std::wstring w = from_utf8(new_tokens[i]);
|
||||
local_tokens.push_back(to_local_8_bit(w));
|
||||
}
|
||||
parse(value_store, local_tokens);
|
||||
} else {
|
||||
// Already in local encoding, pass unmodified
|
||||
parse(value_store, new_tokens);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
value_semantic_codecvt_helper<wchar_t>::
|
||||
parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens,
|
||||
bool utf8) const
|
||||
{
|
||||
std::vector<wstring> tokens;
|
||||
if (utf8) {
|
||||
// Convert from utf8
|
||||
for (unsigned i = 0; i < new_tokens.size(); ++i) {
|
||||
tokens.push_back(from_utf8(new_tokens[i]));
|
||||
}
|
||||
|
||||
} else {
|
||||
// Convert from local encoding
|
||||
for (unsigned i = 0; i < new_tokens.size(); ++i) {
|
||||
tokens.push_back(from_local_8_bit(new_tokens[i]));
|
||||
}
|
||||
}
|
||||
|
||||
parse(value_store, tokens);
|
||||
}
|
||||
|
||||
|
||||
string arg("arg");
|
||||
|
||||
std::string
|
||||
untyped_value::name() const
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
|
||||
void
|
||||
untyped_value::parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens) const
|
||||
{
|
||||
if (!value_store.empty())
|
||||
throw multiple_occurences("multiple_occurences");
|
||||
if (new_tokens.size() > 1)
|
||||
throw multiple_values("multiple_values");
|
||||
value_store = new_tokens.empty() ? std::string("") : new_tokens.front();
|
||||
}
|
||||
|
||||
DECL typed_value<bool>*
|
||||
bool_switch()
|
||||
{
|
||||
return bool_switch(0);
|
||||
}
|
||||
|
||||
DECL typed_value<bool>*
|
||||
bool_switch(bool* v)
|
||||
{
|
||||
typed_value<bool>* r = new typed_value<bool>(v);
|
||||
r->default_value(0);
|
||||
r->implicit();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Validates bool value.
|
||||
Any of "1", "true", "yes", "on" will be converted to "1".<br>
|
||||
Any of "0", "false", "no", "off" will be converted to "0".<br>
|
||||
Case is ignored. Regardless of name passed, parameter will always
|
||||
be optional.
|
||||
*/
|
||||
template<>
|
||||
void validator<bool, char>::operator()(any& v, const vector<string>& xs)
|
||||
{
|
||||
check_first_occurence(v);
|
||||
string s(get_single_string(xs, true));
|
||||
|
||||
for (size_t i = 0; i < s.size(); ++i)
|
||||
s[i] = char(tolower(s[i]));
|
||||
|
||||
if (s.empty() || s == "on" || s == "yes" || s == "1" || s == "true")
|
||||
v = any(true);
|
||||
else if (s == "off" || s == "no" || s == "0" || s == "false")
|
||||
v = any(false);
|
||||
else
|
||||
throw validation_error("'" + s + "' doesn't look like a bool value.");
|
||||
}
|
||||
|
||||
// This is blatant copy-paste. However, templating this will cause a problem,
|
||||
// since wstring can't be constructed/compared with char*. We'd need to
|
||||
// create auxilliary 'widen' routine to convert from char* into
|
||||
// needed string type, and that's more work.
|
||||
template<>
|
||||
void validator<bool, wchar_t>::operator()(any& v, const vector<wstring>& xs)
|
||||
{
|
||||
check_first_occurence(v);
|
||||
wstring s(get_single_string(xs, true));
|
||||
|
||||
for (size_t i = 0; i < s.size(); ++i)
|
||||
s[i] = wchar_t(tolower(s[i]));
|
||||
|
||||
if (s.empty() || s == L"on" || s == L"yes" || s == L"1" || s == L"true")
|
||||
v = any(true);
|
||||
else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
|
||||
v = any(false);
|
||||
else
|
||||
throw validation_error("invalid bool value");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Input quoted with either ' or " will be unquoted.
|
||||
All other input will be unchanged.
|
||||
*/
|
||||
template<>
|
||||
void validator<string, char>::operator()(any& v, const vector<string>& xs)
|
||||
{
|
||||
check_first_occurence(v);
|
||||
string s(get_single_string(xs));
|
||||
if (*s.begin() == '\'' && *s.rbegin() == '\'' ||
|
||||
*s.begin() == '"' && *s.rbegin() == '"')
|
||||
v = any(s.substr(1, s.size()-2));
|
||||
else
|
||||
v = any(s);
|
||||
}
|
||||
|
||||
template<>
|
||||
void validator<string, wchar_t>::operator()(any& v, const vector<wstring>& xs)
|
||||
{
|
||||
check_first_occurence(v);
|
||||
wstring s(get_single_string(xs));
|
||||
if (*s.begin() == L'\'' && *s.rbegin() == L'\'' ||
|
||||
*s.begin() == L'"' && *s.rbegin() == L'"')
|
||||
v = any(s.substr(1, s.size()-2));
|
||||
else
|
||||
v = any(s);
|
||||
}
|
||||
|
||||
|
||||
namespace validators {
|
||||
|
||||
void check_first_occurence(const boost::any& value)
|
||||
{
|
||||
if (!value.empty())
|
||||
throw multiple_occurences("multiple_occurences");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}}
|
||||
|
||||
156
src/variables_map.cpp
Normal file
156
src/variables_map.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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)
|
||||
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/value_semantic.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#define DECL BOOST_PROGRAM_OPTIONS_DECL
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
using namespace std;
|
||||
|
||||
// First, performs semantic actions for 'oa'.
|
||||
// Then, stores in 'm' all options that are defined in 'desc'.
|
||||
DECL void store(const parsed_options& options, variables_map& xm,
|
||||
bool utf8)
|
||||
{
|
||||
assert(options.description);
|
||||
const options_description& desc = *options.description;
|
||||
|
||||
// We need to access map's operator[], not the overriden version
|
||||
// variables_map. Ehmm.. messy.
|
||||
std::map<std::string, variable_value>& m = xm;
|
||||
|
||||
// The set of existing values that should not be changed.
|
||||
std::set<std::string> final;
|
||||
for (map<string, variable_value>::iterator k = m.begin();
|
||||
k != m.end();
|
||||
++k)
|
||||
{
|
||||
if (!k->second.defaulted()) {
|
||||
// TODO: what if we have different definition
|
||||
// for the same option name during different calls
|
||||
// 'store'.
|
||||
bool composing = desc.count(k->first)
|
||||
&& desc.find(k->first).semantic()->is_composing();
|
||||
|
||||
if (!composing)
|
||||
final.insert(k->first);
|
||||
}
|
||||
}
|
||||
|
||||
// First, convert/store all given options
|
||||
for (size_t i = 0; i < options.options.size(); ++i) {
|
||||
|
||||
const string& name = options.options[i].string_key;
|
||||
// Skip positional options without name
|
||||
if (name.empty())
|
||||
continue;
|
||||
|
||||
// If option has final value, skip this assignment
|
||||
if (final.count(name))
|
||||
continue;
|
||||
|
||||
// Ignore options which are not described
|
||||
if (desc.count(name) == 0)
|
||||
continue;
|
||||
|
||||
const option_description& d = desc.find(name);
|
||||
|
||||
variable_value& v = m[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();
|
||||
}
|
||||
|
||||
// Second, apply default values.
|
||||
set<string> keys = desc.primary_keys();
|
||||
for (set<string>::const_iterator j = keys.begin(); j != keys.end(); ++j)
|
||||
if (m.count(*j) == 0) {
|
||||
const option_description& d = desc.find(*j);
|
||||
|
||||
boost::any def;
|
||||
if (d.semantic()->apply_default(def)) {
|
||||
m[*j] = variable_value(def, true);
|
||||
m[*j].m_value_semantic = d.semantic();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DECL void store(const wparsed_options& options, variables_map& m)
|
||||
{
|
||||
store(options.utf8_encoded_options, m, true);
|
||||
}
|
||||
|
||||
DECL void notify(variables_map& vm)
|
||||
{
|
||||
// Lastly, run notify actions.
|
||||
for (map<string, variable_value>::iterator k = vm.begin();
|
||||
k != vm.end();
|
||||
++k)
|
||||
{
|
||||
k->second.m_value_semantic->notify(k->second.value());
|
||||
}
|
||||
}
|
||||
|
||||
abstract_variables_map::abstract_variables_map()
|
||||
: m_next(0)
|
||||
{}
|
||||
|
||||
abstract_variables_map::
|
||||
abstract_variables_map(const abstract_variables_map* next)
|
||||
: m_next(next)
|
||||
{}
|
||||
|
||||
const variable_value&
|
||||
abstract_variables_map::operator[](const std::string& name) const
|
||||
{
|
||||
const variable_value& v = get(name);
|
||||
if (v.empty() && m_next)
|
||||
return (*m_next)[name];
|
||||
else if (v.defaulted() && m_next) {
|
||||
const variable_value& v2 = (*m_next)[name];
|
||||
if (!v2.empty() && !v2.defaulted())
|
||||
return v2;
|
||||
else return v;
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
abstract_variables_map::next(abstract_variables_map* next)
|
||||
{
|
||||
m_next = next;
|
||||
}
|
||||
|
||||
variables_map::variables_map()
|
||||
{}
|
||||
|
||||
variables_map::variables_map(const abstract_variables_map* next)
|
||||
: abstract_variables_map(next)
|
||||
{}
|
||||
|
||||
const variable_value&
|
||||
variables_map::get(const std::string& name) const
|
||||
{
|
||||
static variable_value empty;
|
||||
const_iterator i = this->find(name);
|
||||
if (i == this->end())
|
||||
return empty;
|
||||
else
|
||||
return i->second;
|
||||
}
|
||||
}}
|
||||
20
test/Jamfile
Normal file
20
test/Jamfile
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
subproject libs/program_options/test ;
|
||||
|
||||
rule program-options-test ( name )
|
||||
{
|
||||
unit-test $(name) : $(name).cpp <lib>../build/boost_program_options
|
||||
<lib>../../test/build/boost_test_exec_monitor
|
||||
: <include>$(BOOST_ROOT)
|
||||
;
|
||||
}
|
||||
|
||||
test-suite program_options :
|
||||
[ program-options-test options_description_test ]
|
||||
[ program-options-test parsers_test ]
|
||||
[ program-options-test variable_map_test ]
|
||||
[ program-options-test cmdline_test ]
|
||||
[ program-options-test positional_options_test ]
|
||||
[ program-options-test unicode_test ]
|
||||
;
|
||||
|
||||
34
test/Jamfile.v2
Normal file
34
test/Jamfile.v2
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
project
|
||||
: requirements
|
||||
<library>../build//program_options
|
||||
<library>/boost/test//boost_test_exec_monitor
|
||||
<hardcode-dll-paths>true
|
||||
;
|
||||
|
||||
unit-test options_description_test
|
||||
: options_description_test.cpp
|
||||
;
|
||||
|
||||
unit-test parsers_test
|
||||
: parsers_test.cpp
|
||||
;
|
||||
|
||||
unit-test variable_map_test
|
||||
: variable_map_test.cpp
|
||||
;
|
||||
|
||||
unit-test cmdline_test
|
||||
: cmdline_test.cpp
|
||||
;
|
||||
|
||||
unit-test positional_options_test
|
||||
: positional_options_test.cpp
|
||||
;
|
||||
|
||||
unit-test unicode_test
|
||||
: unicode_test.cpp
|
||||
;
|
||||
|
||||
exe test_convert : test_convert.cpp ../build//program_options ;
|
||||
|
||||
516
test/cmdline_test.cpp
Normal file
516
test/cmdline_test.cpp
Normal file
@@ -0,0 +1,516 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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/cmdline.hpp>
|
||||
#include <boost/program_options/detail/cmdline.hpp>
|
||||
using namespace boost::program_options;
|
||||
using boost::program_options::detail::cmdline;
|
||||
|
||||
|
||||
#include <boost/test/test_tools.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
using namespace std;
|
||||
|
||||
/* To facilitate testing, declare a number of error codes. Otherwise,
|
||||
we'd have to specify the type of exception that should be thrown.
|
||||
*/
|
||||
|
||||
const int s_success = 0;
|
||||
const int s_unknown_option = 1;
|
||||
const int s_ambiguous_option = 2;
|
||||
const int s_long_not_allowed = 3;
|
||||
const int s_long_adjacent_not_allowed = 4;
|
||||
const int s_short_adjacent_not_allowed = 5;
|
||||
const int s_empty_adjacent_parameter = 6;
|
||||
const int s_missing_parameter = 7;
|
||||
const int s_extra_parameter = 8;
|
||||
|
||||
int translate_syntax_error_kind(invalid_command_line_syntax::kind_t k)
|
||||
{
|
||||
invalid_command_line_syntax::kind_t table[] = {
|
||||
invalid_command_line_syntax::long_not_allowed,
|
||||
invalid_command_line_syntax::long_adjacent_not_allowed,
|
||||
invalid_command_line_syntax::short_adjacent_not_allowed,
|
||||
invalid_command_line_syntax::empty_adjacent_parameter,
|
||||
invalid_command_line_syntax::missing_parameter,
|
||||
invalid_command_line_syntax::extra_parameter,
|
||||
};
|
||||
invalid_command_line_syntax::kind_t *b, *e, *i;
|
||||
b = table;
|
||||
e = table + sizeof(table)/sizeof(table[0]);
|
||||
i = std::find(b, e, k);
|
||||
assert(i != e);
|
||||
return std::distance(b, i) + 3;
|
||||
}
|
||||
|
||||
|
||||
struct test_case {
|
||||
const char* input;
|
||||
int expected_status;
|
||||
const char* expected_result;
|
||||
};
|
||||
|
||||
/* Parses the syntax description in 'syntax' and initialized
|
||||
'cmd' accordingly' */
|
||||
void apply_syntax(detail::cmdline& cmd, const char* syntax)
|
||||
{
|
||||
string s;
|
||||
stringstream ss;
|
||||
ss << syntax;
|
||||
while(ss >> s) {
|
||||
string long_name;
|
||||
char short_name = '\0';
|
||||
char properties = '|';
|
||||
|
||||
if (*(s.end()-1) == '=') {
|
||||
properties = ':';
|
||||
s.resize(s.size()-1);
|
||||
} else if (*(s.end()-1) == '?') {
|
||||
properties = '?';
|
||||
s.resize(s.size()-1);
|
||||
} else if (*(s.end()-1) == '*') {
|
||||
properties = '*';
|
||||
s.resize(s.size()-1);
|
||||
} else if (*(s.end()-1) == '+') {
|
||||
properties = '+';
|
||||
s.resize(s.size()-1);
|
||||
}
|
||||
string::size_type n = s.find(',');
|
||||
if (n == string::npos) {
|
||||
long_name = s;
|
||||
} else {
|
||||
assert(n == s.size()-2);
|
||||
long_name = s.substr(0, s.size()-2);
|
||||
short_name = *s.rbegin();
|
||||
}
|
||||
cmd.add_option(long_name, short_name, properties, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void test_cmdline(const char* syntax,
|
||||
command_line_style::style_t style,
|
||||
const test_case* cases)
|
||||
{
|
||||
for (int i = 0; cases[i].input; ++i) {
|
||||
// Parse input
|
||||
vector<string> xinput;
|
||||
{
|
||||
string s;
|
||||
stringstream ss;
|
||||
ss << cases[i].input;
|
||||
while (ss >> s) {
|
||||
xinput.push_back(s);
|
||||
}
|
||||
}
|
||||
detail::cmdline cmd(xinput, style);
|
||||
|
||||
apply_syntax(cmd, syntax);
|
||||
|
||||
string result;
|
||||
int status = 0;
|
||||
|
||||
try {
|
||||
while(++cmd) {
|
||||
if (cmd.at_argument()) {
|
||||
if (!result.empty())
|
||||
result += " ";
|
||||
result += cmd.argument();
|
||||
} else {
|
||||
if (!result.empty())
|
||||
result += " ";
|
||||
if (*cmd.option_name().rbegin() != '*')
|
||||
result += cmd.option_name() + ":";
|
||||
else
|
||||
result += cmd.raw_option_name() + ":";
|
||||
for (size_t i = 0; i < cmd.option_values().size(); ++i) {
|
||||
if (i != 0)
|
||||
result += "-";
|
||||
result += cmd.option_values()[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(unknown_option& e) {
|
||||
status = s_unknown_option;
|
||||
}
|
||||
catch(ambiguous_option& e) {
|
||||
status = s_ambiguous_option;
|
||||
}
|
||||
catch(invalid_command_line_syntax& e) {
|
||||
status = translate_syntax_error_kind(e.kind());
|
||||
}
|
||||
BOOST_CHECK_EQUAL(status, cases[i].expected_status);
|
||||
BOOST_CHECK_EQUAL(result, cases[i].expected_result);
|
||||
}
|
||||
}
|
||||
|
||||
void test_long_options()
|
||||
{
|
||||
using namespace command_line_style;
|
||||
cmdline::style_t style = cmdline::style_t(
|
||||
allow_long | long_allow_adjacent);
|
||||
|
||||
test_case test_cases1[] = {
|
||||
// Test that long options are recognized and everything else
|
||||
// is treated like arguments
|
||||
{"--foo foo -123 /asd", s_success, "foo: foo -123 /asd"},
|
||||
|
||||
// Unknown option
|
||||
{"--unk", s_unknown_option, ""},
|
||||
|
||||
// Test that abbreviated names do not work
|
||||
{"--fo", s_unknown_option, ""},
|
||||
|
||||
// Test for disallowed parameter
|
||||
{"--foo=13", s_extra_parameter, ""},
|
||||
|
||||
// Test option with required parameter
|
||||
|
||||
{"--bar=", s_empty_adjacent_parameter, ""},
|
||||
{"--bar", s_missing_parameter, ""},
|
||||
{"--bar=123", s_success, "bar:123"},
|
||||
|
||||
// Test option with optional parameter
|
||||
{"--baz", s_success, "baz:"},
|
||||
{"--baz=7", s_success, "baz:7"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline("foo bar= baz?", style, test_cases1);
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_long | long_allow_next);
|
||||
|
||||
test_case test_cases2[] = {
|
||||
{"--foo", s_success, "foo:"},
|
||||
{"--bar=10", s_long_adjacent_not_allowed, ""},
|
||||
{"--bar 10", s_success, "bar:10"},
|
||||
{"--bar", s_missing_parameter, ""},
|
||||
{"--bar --foo", s_missing_parameter, ""},
|
||||
{"--baz", s_success, "baz:"},
|
||||
{"--baz 10", s_success, "baz:10"},
|
||||
{"--baz --foo", s_success, "baz: foo:"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline("foo bar= baz?", style, test_cases2);
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_long | long_allow_adjacent
|
||||
| long_allow_next);
|
||||
|
||||
test_case test_cases3[] = {
|
||||
{"--bar=10", s_success, "bar:10"},
|
||||
{"--bar 11", s_success, "bar:11"},
|
||||
{"--baz=12", s_success, "baz:12"},
|
||||
{"--baz 13", s_success, "baz:13"},
|
||||
{"--baz --foo", s_success, "baz: foo:"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline("foo bar= baz?", style, test_cases3);
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_long | long_allow_adjacent
|
||||
| long_allow_next | case_insentitive);
|
||||
|
||||
// Test case insensitive style.
|
||||
// Note that option names are normalized to lower case.
|
||||
test_case test_cases4[] = {
|
||||
{"--foo", s_success, "foo:"},
|
||||
{"--Foo", s_success, "foo:"},
|
||||
{"--bar=Ab", s_success, "bar:Ab"},
|
||||
{"--Bar=ab", s_success, "bar:ab"},
|
||||
{"--giz", s_success, "Giz:"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline("foo bar= baz? Giz", style, test_cases4);
|
||||
}
|
||||
|
||||
void test_short_options()
|
||||
{
|
||||
using namespace command_line_style;
|
||||
cmdline::style_t style;
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | allow_dash_for_short
|
||||
| short_allow_adjacent);
|
||||
|
||||
test_case test_cases1[] = {
|
||||
{"-d d /bar", s_success, "-d: d /bar"},
|
||||
// This is treated as error when long options are disabled
|
||||
{"--foo", s_long_not_allowed, ""},
|
||||
{"-d13", s_extra_parameter, ""},
|
||||
{"-f14", s_success, "-f:14"},
|
||||
{"-g -f1", s_success, "-g: -f:1"},
|
||||
{"-f", s_missing_parameter, ""},
|
||||
{0}
|
||||
};
|
||||
test_cmdline(",d ,f= ,g?", style, test_cases1);
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | allow_dash_for_short
|
||||
| short_allow_next);
|
||||
|
||||
test_case test_cases2[] = {
|
||||
{"-f 13", s_success, "-f:13"},
|
||||
{"-f -13", s_success, "-f:-13"},
|
||||
{"-f", s_missing_parameter, ""},
|
||||
{"-f --foo", s_missing_parameter, ""},
|
||||
{"-f /foo", s_success, "-f:/foo"},
|
||||
{"-f -d", s_success, "-f:-d"},
|
||||
{"-g 13", s_success, "-g:13"},
|
||||
{"-g", s_success, "-g:"},
|
||||
{"-g --foo", s_long_not_allowed, "-g:"},
|
||||
{"-g /foo", s_success, "-g:/foo"},
|
||||
{"-g -d", s_success, "-g: -d:"},
|
||||
{"-f12", s_short_adjacent_not_allowed, ""},
|
||||
{0}
|
||||
};
|
||||
test_cmdline(",d ,f= ,g?", style, test_cases2);
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | short_allow_next
|
||||
| allow_dash_for_short | short_allow_adjacent);
|
||||
|
||||
test_case test_cases3[] = {
|
||||
{"-f10", s_success, "-f:10"},
|
||||
{"-f 10", s_success, "-f:10"},
|
||||
{"-f -d", s_success, "-f:-d"},
|
||||
{"-g10", s_success, "-g:10"},
|
||||
{"-g 10", s_success, "-g:10"},
|
||||
{"-g -d", s_success, "-g: -d:"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline(",d ,f= ,g?", style, test_cases3);
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | short_allow_next
|
||||
| allow_dash_for_short
|
||||
| short_allow_adjacent | allow_sticky);
|
||||
|
||||
test_case test_cases4[] = {
|
||||
{"-de", s_success, "-d: -e:"},
|
||||
{"-dg", s_success, "-d: -g:"},
|
||||
{"-dg10", s_success, "-d: -g:10"},
|
||||
{"-d12", s_extra_parameter, ""},
|
||||
{"-gd", s_success, "-g:d"},
|
||||
{"-fe", s_success, "-f:e"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline(",d ,f= ,g? ,e", style, test_cases4);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void test_dos_options()
|
||||
{
|
||||
using namespace command_line_style;
|
||||
cmdline::style_t style;
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short
|
||||
| allow_slash_for_short | short_allow_adjacent);
|
||||
|
||||
test_case test_cases1[] = {
|
||||
{"/d d -bar", s_success, "-d: d -bar"},
|
||||
// This is treated as disallowed long option
|
||||
{"--foo", s_long_not_allowed, ""},
|
||||
{"/d13", s_extra_parameter, ""},
|
||||
{"/f14", s_success, "-f:14"},
|
||||
{"/g /f1", s_success, "-g: -f:1"},
|
||||
{"/f", s_missing_parameter, ""},
|
||||
{0}
|
||||
};
|
||||
test_cmdline(",d ,f= ,g?", style, test_cases1);
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short
|
||||
| allow_slash_for_short | short_allow_next
|
||||
| short_allow_adjacent | allow_sticky);
|
||||
|
||||
test_case test_cases2[] = {
|
||||
{"/de", s_extra_parameter, ""},
|
||||
{"/gd", s_success, "-g:d"},
|
||||
{"/fe", s_success, "-f:e"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline(",d ,f= ,g? ,e", style, test_cases2);
|
||||
|
||||
}
|
||||
|
||||
void test_disguised_long()
|
||||
{
|
||||
using namespace command_line_style;
|
||||
cmdline::style_t style;
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | short_allow_adjacent
|
||||
| allow_dash_for_short
|
||||
| short_allow_next | allow_long_disguise
|
||||
| long_allow_adjacent);
|
||||
|
||||
test_case test_cases1[] = {
|
||||
{"-foo -f", s_success, "foo: foo:"},
|
||||
{"-goo=x -gy", s_success, "goo:x goo:y"},
|
||||
{"-bee=x -by", s_success, "bee:x bee:y"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline("foo,f goo,g= bee,b?", style, test_cases1);
|
||||
|
||||
style = cmdline::style_t(style | allow_slash_for_short);
|
||||
test_case test_cases2[] = {
|
||||
{"/foo -f", s_success, "foo: foo:"},
|
||||
{"/goo=x", s_success, "goo:x"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline("foo,f goo,g= bee,b?", style, test_cases2);
|
||||
}
|
||||
|
||||
void test_guessing()
|
||||
{
|
||||
using namespace command_line_style;
|
||||
cmdline::style_t style;
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | short_allow_adjacent
|
||||
| allow_dash_for_short
|
||||
| allow_long | long_allow_adjacent
|
||||
| allow_guessing | allow_long_disguise);
|
||||
|
||||
test_case test_cases1[] = {
|
||||
{"--opt1", s_success, "opt123:"},
|
||||
{"--opt", s_ambiguous_option, ""},
|
||||
{"--f=1", s_success, "foo:1"},
|
||||
{"-far", s_success, "foo:ar"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline("opt123 opt56 foo,f=", style, test_cases1);
|
||||
}
|
||||
|
||||
void test_arguments()
|
||||
{
|
||||
using namespace command_line_style;
|
||||
cmdline::style_t style;
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | allow_long
|
||||
| allow_dash_for_short
|
||||
| short_allow_adjacent | long_allow_adjacent);
|
||||
|
||||
test_case test_cases1[] = {
|
||||
{"-f file -gx file2", s_success, "-f: file -g:x file2"},
|
||||
{"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline(",f ,g= ,e", style, test_cases1);
|
||||
|
||||
// "--" should stop options regardless of whether long options are
|
||||
// allowed or not.
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | short_allow_adjacent
|
||||
| allow_dash_for_short);
|
||||
|
||||
test_case test_cases2[] = {
|
||||
{"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"},
|
||||
{0}
|
||||
};
|
||||
test_cmdline(",f ,g= ,e", style, test_cases2);
|
||||
}
|
||||
|
||||
void test_prefix()
|
||||
{
|
||||
using namespace command_line_style;
|
||||
cmdline::style_t style;
|
||||
|
||||
style = cmdline::style_t(
|
||||
allow_short | allow_long
|
||||
| allow_dash_for_short
|
||||
| short_allow_adjacent | long_allow_adjacent
|
||||
);
|
||||
|
||||
test_case test_cases1[] = {
|
||||
{"--foo.bar=12", s_success, "foo.bar:12"},
|
||||
{0}
|
||||
};
|
||||
|
||||
test_cmdline("foo*=", style, test_cases1);
|
||||
}
|
||||
|
||||
void test_multiple()
|
||||
{
|
||||
using namespace command_line_style;
|
||||
cmdline::style_t style;
|
||||
|
||||
style = cmdline::style_t(
|
||||
unix_style | long_allow_next);
|
||||
|
||||
test_case test_cases1[] = {
|
||||
{"--value 1 2 3 4 --help", s_success, "value:1-2-3-4 help:"},
|
||||
{"--value 1 2 3 4 --", s_success, "value:1-2-3-4"},
|
||||
{0}
|
||||
};
|
||||
|
||||
test_cmdline("value+ help", style, test_cases1);
|
||||
}
|
||||
|
||||
void test_style_errors()
|
||||
{
|
||||
using namespace command_line_style;
|
||||
char* argv[] = {"program"};
|
||||
|
||||
BOOST_CHECK_THROW(cmdline cmd(1, argv, allow_long),
|
||||
invalid_command_line_style);
|
||||
|
||||
BOOST_CHECK_THROW(cmdline cmd(1, argv, allow_short),
|
||||
invalid_command_line_style);
|
||||
|
||||
BOOST_CHECK_THROW(cmdline cmd(1, argv, allow_short |
|
||||
short_allow_next),
|
||||
invalid_command_line_style);
|
||||
}
|
||||
|
||||
int test_main(int ac, char* av[])
|
||||
{
|
||||
// ### detail::test_cmdline_detail();
|
||||
|
||||
test_long_options();
|
||||
test_short_options();
|
||||
test_dos_options();
|
||||
test_disguised_long();
|
||||
test_guessing();
|
||||
test_arguments();
|
||||
test_prefix();
|
||||
test_multiple();
|
||||
test_style_errors();
|
||||
|
||||
cmdline cmd((int)ac, (const char*const *)av,
|
||||
int(command_line_style::unix_style));
|
||||
cmd.add_option("version", 'v');
|
||||
cmd.add_option("help", 'h');
|
||||
cmd.add_option("verbose", 'V');
|
||||
cmd.add_option("magic", 'm');
|
||||
cmd.add_option("output", 'o', ':');
|
||||
|
||||
try {
|
||||
while(++cmd) {
|
||||
|
||||
if (cmd.at_argument()) {
|
||||
cout << "Argument : " << cmd.argument() << "\n";
|
||||
} else {
|
||||
cout << "Option : " << cmd.option_name()
|
||||
<< "(" << cmd.option_value() << ")\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(exception& e) {
|
||||
cout << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
98
test/options_description_test.cpp
Normal file
98
test/options_description_test.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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/options_description.hpp>
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <boost/function.hpp>
|
||||
using namespace boost;
|
||||
|
||||
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
|
||||
#include <boost/test/test_tools.hpp>
|
||||
|
||||
#include <utility>
|
||||
using namespace std;
|
||||
|
||||
/* This is very wierd test case -- it tests trivial things. After writing it,
|
||||
I think that XP folks must be somehow wrong.
|
||||
*/
|
||||
void test_option_description_construction()
|
||||
{
|
||||
option_description d1("a", new untyped_value(), "desc1");
|
||||
BOOST_TEST(d1.long_name() == "a");
|
||||
BOOST_TEST(d1.description() == "desc1");
|
||||
BOOST_TEST(d1.semantic()->name() == "arg");
|
||||
|
||||
// It is not possible to compare boost::function
|
||||
#if 0
|
||||
function<string, string> f1;
|
||||
BOOST_TEST(&option_description("x", "y", "z").
|
||||
validator(f1).validator() ==
|
||||
&f1);
|
||||
function<void, string> f2;
|
||||
BOOST_TEST(&option_description("x", "y", "z").
|
||||
notify(f2).notify() ==
|
||||
&f2);
|
||||
#endif
|
||||
|
||||
option_description d4("foo,f", new untyped_value(), "desc1");
|
||||
BOOST_CHECK(d4.long_name() == "foo");
|
||||
BOOST_CHECK(d4.short_name() == "f");
|
||||
}
|
||||
|
||||
void test_options_description()
|
||||
{
|
||||
options_description desc;
|
||||
|
||||
shared_ptr<option_description> d1(
|
||||
new option_description("first,f", new untyped_value(), ""));
|
||||
desc.add(d1);
|
||||
BOOST_TEST(desc.count("first") == 1);
|
||||
BOOST_TEST(desc.count("-f") == 1);
|
||||
BOOST_TEST(desc.keys().size() == 2);
|
||||
BOOST_TEST(desc.keys().count("first") == 1);
|
||||
BOOST_TEST(desc.keys().count("-f") == 1);
|
||||
|
||||
desc.add_options()
|
||||
("second,s", new untyped_value())
|
||||
("third,t", new untyped_value())
|
||||
;
|
||||
BOOST_TEST(desc.count("second") == 1);
|
||||
BOOST_TEST(desc.count("-s") == 1);
|
||||
|
||||
desc.add_options()
|
||||
(",x", new untyped_value)
|
||||
;
|
||||
BOOST_TEST(desc.primary_keys().size() == 4);
|
||||
BOOST_TEST(desc.primary_keys().count("first") == 1);
|
||||
BOOST_TEST(desc.primary_keys().count("second") == 1);
|
||||
BOOST_TEST(desc.primary_keys().count("third") == 1);
|
||||
BOOST_TEST(desc.primary_keys().count("-x") == 1);
|
||||
}
|
||||
|
||||
void test_approximation()
|
||||
{
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("foo", new untyped_value())
|
||||
("fee", new untyped_value())
|
||||
("baz", new untyped_value());
|
||||
|
||||
BOOST_TEST(desc.count_approx("f") == 2);
|
||||
BOOST_TEST(desc.count_approx("foo") == 1);
|
||||
set<string> a = desc.approximations("f");
|
||||
BOOST_TEST(a.size() == 2);
|
||||
BOOST_TEST(*a.begin() == "fee");
|
||||
BOOST_TEST(*(++a.begin()) == "foo");
|
||||
}
|
||||
|
||||
int test_main(int, char* [])
|
||||
{
|
||||
test_option_description_construction();
|
||||
test_options_description();
|
||||
test_approximation();
|
||||
return 0;
|
||||
}
|
||||
201
test/parsers_test.cpp
Normal file
201
test/parsers_test.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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>
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <boost/function.hpp>
|
||||
using namespace boost;
|
||||
|
||||
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
|
||||
#include <boost/test/test_tools.hpp>
|
||||
|
||||
#include <sstream>
|
||||
using namespace std;
|
||||
|
||||
#include <cstdlib> // for setenv
|
||||
|
||||
#define TEST_CHECK_THROW(expression, exception, description) \
|
||||
try \
|
||||
{ \
|
||||
expression; \
|
||||
BOOST_ERROR(description);\
|
||||
throw 10; \
|
||||
} \
|
||||
catch(exception &) \
|
||||
{ \
|
||||
}
|
||||
|
||||
pair<string, vector< vector<string> > > msp(const string& s1)
|
||||
{
|
||||
return std::make_pair(s1, vector< vector<string> >());
|
||||
}
|
||||
|
||||
|
||||
pair<string, vector< vector<string> > > msp(const string& s1, const string& s2)
|
||||
{
|
||||
vector< vector<string> > v(1);
|
||||
v[0].push_back(s2);
|
||||
return std::make_pair(s1, v);
|
||||
}
|
||||
|
||||
void check_value(const option& option, const char* name, const char* value)
|
||||
{
|
||||
BOOST_CHECK(option.string_key == name);
|
||||
BOOST_REQUIRE(option.value.size() == 1);
|
||||
BOOST_CHECK(option.value.front() == value);
|
||||
}
|
||||
|
||||
void check_value(const woption& option, const char* name, const wchar_t* value)
|
||||
{
|
||||
BOOST_CHECK(option.string_key == name);
|
||||
BOOST_REQUIRE(option.value.size() == 1);
|
||||
BOOST_CHECK(option.value.front() == value);
|
||||
}
|
||||
|
||||
|
||||
void test_command_line()
|
||||
{
|
||||
// The following commented out blocks used to test parsing
|
||||
// command line without syntax specification behaviour.
|
||||
// It is disabled now and probably will never be enabled again:
|
||||
// it is not possible to figure out what command line means without
|
||||
// user's help.
|
||||
#if 0
|
||||
char* cmdline1[] = { "--a", "--b=12", "-f", "-g4", "-", "file" };
|
||||
|
||||
options_and_arguments a1 =
|
||||
parse_command_line(cmdline1,
|
||||
cmdline1 + sizeof(cmdline1)/sizeof(cmdline1[0]));
|
||||
|
||||
BOOST_CRITICAL_TEST(a1.options().size() == 4);
|
||||
BOOST_TEST(a1.options()[0] == msp("a", ""));
|
||||
BOOST_TEST(a1.options()[1] == msp("b", "12"));
|
||||
BOOST_TEST(a1.options()[2] == msp("-f", ""));
|
||||
BOOST_TEST(a1.options()[3] == msp("-g", "4"));
|
||||
BOOST_CRITICAL_TEST(a1.arguments().size() == 2);
|
||||
BOOST_TEST(a1.arguments()[0] == "-");
|
||||
BOOST_TEST(a1.arguments()[1] == "file");
|
||||
|
||||
char* cmdline2[] = { "--a", "--", "file" };
|
||||
|
||||
options_and_arguments a2 =
|
||||
parse_command_line(cmdline2,
|
||||
cmdline2 + sizeof(cmdline2)/sizeof(cmdline2[0]));
|
||||
|
||||
BOOST_CRITICAL_TEST(a2.options().size() == 1);
|
||||
BOOST_TEST(a2.options()[0] == msp("a", ""));
|
||||
BOOST_TEST(a2.arguments().size() == 1);
|
||||
BOOST_TEST(a2.arguments()[0] == "file");
|
||||
#endif
|
||||
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("foo,f", new untyped_value(), "")
|
||||
("bar,b", value<string>()->implicit(), "")
|
||||
("baz", new untyped_value())
|
||||
("plug*", new untyped_value())
|
||||
;
|
||||
char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "--bar", "-b4", "-b",
|
||||
"--plug3=10"};
|
||||
vector<string> cmdline3(cmdline3_,
|
||||
cmdline3_ + sizeof(cmdline3_)/sizeof(cmdline3_[0]));
|
||||
vector<option> a3 =
|
||||
command_line_parser(cmdline3).options(desc).run().options;
|
||||
|
||||
BOOST_CRITICAL_TEST(a3.size() == 7);
|
||||
|
||||
check_value(a3[0], "foo", "12");
|
||||
check_value(a3[1], "foo", "4");
|
||||
check_value(a3[2], "bar", "11");
|
||||
|
||||
BOOST_TEST(a3[3].string_key == "bar");
|
||||
BOOST_CRITICAL_TEST(a3[3].value.size() == 0);
|
||||
|
||||
check_value(a3[4], "bar", "4");
|
||||
|
||||
BOOST_TEST(a3[5].string_key == "bar");
|
||||
BOOST_CRITICAL_TEST(a3[5].value.size() == 0);
|
||||
|
||||
check_value(a3[6], "plug3", "10");
|
||||
|
||||
// Check Unicode,
|
||||
wchar_t* cmdline4_[] = { L"--foo=1\u0FF52", L"-f4", L"--bar=11", L"--bar",
|
||||
L"-b4", L"-b", L"--plug3=10"};
|
||||
vector<wstring> cmdline4(cmdline4_,
|
||||
cmdline4_ + sizeof(cmdline4_)/sizeof(cmdline4_[0]));
|
||||
vector<woption> a4 =
|
||||
wcommand_line_parser(cmdline4).options(desc).run().options;
|
||||
|
||||
BOOST_CRITICAL_TEST(a3.size() == 7);
|
||||
|
||||
check_value(a4[0], "foo", L"1\u0FF52");
|
||||
check_value(a4[1], "foo", L"4");
|
||||
check_value(a4[2], "bar", L"11");
|
||||
}
|
||||
|
||||
void test_config_file()
|
||||
{
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("gv1", new untyped_value)
|
||||
("gv2", new untyped_value)
|
||||
("plug*", new untyped_value)
|
||||
("m1.v1", new untyped_value)
|
||||
("m1.v2", new untyped_value)
|
||||
("b", bool_switch())
|
||||
;
|
||||
|
||||
const char content1[] =
|
||||
" gv1 = 0#asd\n"
|
||||
"plug3 = 7\n"
|
||||
"b = true\n"
|
||||
"[m1]\n"
|
||||
"v1 = 1\n"
|
||||
"\n"
|
||||
"v2 = 2\n"
|
||||
;
|
||||
|
||||
stringstream ss(content1);
|
||||
vector<option> a1 = parse_config_file(ss, desc).options;
|
||||
BOOST_CRITICAL_TEST(a1.size() == 5);
|
||||
check_value(a1[0], "gv1", "0");
|
||||
check_value(a1[1], "plug3", "7");
|
||||
check_value(a1[2], "b", "true");
|
||||
check_value(a1[3], "m1.v1", "1");
|
||||
check_value(a1[4], "m1.v2", "2");
|
||||
|
||||
}
|
||||
|
||||
void test_environment()
|
||||
{
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("foo", new untyped_value, "")
|
||||
("bar", new untyped_value, "")
|
||||
;
|
||||
|
||||
setenv("PO_TEST_FOO", "1", 1);
|
||||
parsed_options p = parse_environment(desc, "PO_TEST_");
|
||||
|
||||
BOOST_REQUIRE(p.options.size() == 1);
|
||||
BOOST_CHECK(p.options[0].string_key == "foo");
|
||||
BOOST_REQUIRE(p.options[0].value.size() == 1);
|
||||
BOOST_CHECK(p.options[0].value[0] == "1");
|
||||
|
||||
//TODO: since 'bar' does not allow a value, it cannot appear in environemt,
|
||||
// which already has a value.
|
||||
}
|
||||
|
||||
int test_main(int, char* [])
|
||||
{
|
||||
test_command_line();
|
||||
test_config_file();
|
||||
test_environment();
|
||||
return 0;
|
||||
}
|
||||
|
||||
84
test/positional_options_test.cpp
Normal file
84
test/positional_options_test.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright Vladimir Prus 2004.
|
||||
// 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/positional_options.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <boost/limits.hpp>
|
||||
#include <boost/test/test_tools.hpp>
|
||||
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
void test_positional_options()
|
||||
{
|
||||
positional_options_description p;
|
||||
p.add("first", 1);
|
||||
|
||||
BOOST_CHECK_EQUAL(p.max_total_count(), 1u);
|
||||
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
|
||||
|
||||
p.add("second", 2);
|
||||
|
||||
BOOST_CHECK_EQUAL(p.max_total_count(), 3u);
|
||||
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
|
||||
BOOST_CHECK_EQUAL(p.name_for_position(1), "second");
|
||||
BOOST_CHECK_EQUAL(p.name_for_position(2), "second");
|
||||
|
||||
p.add("third", -1);
|
||||
|
||||
BOOST_CHECK_EQUAL(p.max_total_count(), std::numeric_limits<unsigned>::max());
|
||||
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
|
||||
BOOST_CHECK_EQUAL(p.name_for_position(1), "second");
|
||||
BOOST_CHECK_EQUAL(p.name_for_position(2), "second");
|
||||
BOOST_CHECK_EQUAL(p.name_for_position(3), "third");
|
||||
BOOST_CHECK_EQUAL(p.name_for_position(10000), "third");
|
||||
}
|
||||
|
||||
void test_parsing()
|
||||
{
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("first", value<int>())
|
||||
("second", value<int>())
|
||||
("input-file", value< vector<string> >())
|
||||
;
|
||||
|
||||
positional_options_description p;
|
||||
p.add("input-file", 2);
|
||||
|
||||
vector<string> args;
|
||||
args.push_back("--first=10");
|
||||
args.push_back("file1");
|
||||
args.push_back("--second=10");
|
||||
args.push_back("file2");
|
||||
|
||||
// Check that positional options are handled.
|
||||
parsed_options parsed =
|
||||
command_line_parser(args).options(desc).positional(p).run();
|
||||
|
||||
BOOST_REQUIRE(parsed.options.size() == 4);
|
||||
BOOST_CHECK_EQUAL(parsed.options[1].string_key, "input-file");
|
||||
BOOST_CHECK_EQUAL(parsed.options[1].value[0], "file1");
|
||||
BOOST_CHECK_EQUAL(parsed.options[3].string_key, "input-file");
|
||||
BOOST_CHECK_EQUAL(parsed.options[3].value[0], "file2");
|
||||
|
||||
args.push_back("file3");
|
||||
|
||||
// Check that excessive number of positional options is detected.
|
||||
BOOST_CHECK_THROW(command_line_parser(args).options(desc).positional(p)
|
||||
.run(),
|
||||
too_many_positional_options_error);
|
||||
}
|
||||
|
||||
int test_main(int, char* [])
|
||||
{
|
||||
test_positional_options();
|
||||
test_parsing();
|
||||
return 0;
|
||||
}
|
||||
|
||||
143
test/test_convert.cpp
Normal file
143
test/test_convert.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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 <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <boost/progress.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
|
||||
#include <boost/program_options/detail/convert.hpp>
|
||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
string file_content(const string& filename)
|
||||
{
|
||||
ifstream ifs(filename.c_str());
|
||||
assert(ifs);
|
||||
|
||||
stringstream ss;
|
||||
ss << ifs.rdbuf();
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// A version of from_8_bit which does not use functional object, for
|
||||
// performance comparison.
|
||||
std::wstring from_8_bit_2(const std::string& s,
|
||||
const codecvt<wchar_t, char, mbstate_t>& cvt)
|
||||
{
|
||||
std::wstring result;
|
||||
|
||||
|
||||
std::mbstate_t state = {0};
|
||||
|
||||
const char* from = s.data();
|
||||
const char* from_end = s.data() + s.size();
|
||||
// The interace of cvt is not really iterator-like, and it's
|
||||
// not possible the tell the required output size without the conversion.
|
||||
// All we can is convert data by pieces.
|
||||
while(from != from_end) {
|
||||
|
||||
// std::basic_string does not provide non-const pointers to the data,
|
||||
// so converting directly into string is not possible.
|
||||
wchar_t buffer[32];
|
||||
|
||||
wchar_t* to_next = buffer;
|
||||
// Try to convert remaining input.
|
||||
std::codecvt_base::result r =
|
||||
cvt.in(state, from, from_end, from, buffer, buffer + 32, to_next);
|
||||
|
||||
if (r == std::codecvt_base::error)
|
||||
throw logic_error("character conversion failed");
|
||||
// 'partial' is not an error, it just means not all source characters
|
||||
// we converted. However, we need to check that at least one new target
|
||||
// character was produced. If not, it means the source data is
|
||||
// incomplete, and since we don't have extra data to add to source, it's
|
||||
// error.
|
||||
if (to_next == buffer)
|
||||
throw logic_error("character conversion failed");
|
||||
|
||||
// Add converted characters
|
||||
result.append(buffer, to_next);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void test_convert(const std::string& input,
|
||||
const std::string& expected_output)
|
||||
{
|
||||
boost::program_options::detail::utf8_codecvt_facet<wchar_t, char> facet;
|
||||
|
||||
std::wstring output;
|
||||
{
|
||||
boost::progress_timer t;
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
output = boost::from_8_bit(
|
||||
input,
|
||||
facet);
|
||||
}
|
||||
|
||||
{
|
||||
boost::progress_timer t;
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
output = from_8_bit_2(
|
||||
input,
|
||||
facet);
|
||||
}
|
||||
|
||||
assert(output.size()*2 == expected_output.size());
|
||||
|
||||
for(unsigned i = 0; i < output.size(); ++i) {
|
||||
|
||||
{
|
||||
unsigned low = output[i];
|
||||
low &= 0xFF;
|
||||
unsigned low2 = expected_output[2*i];
|
||||
low2 &= 0xFF;
|
||||
assert(low == low2);
|
||||
}
|
||||
{
|
||||
unsigned high = output[i];
|
||||
high >>= 8;
|
||||
high &= 0xFF;
|
||||
unsigned high2 = expected_output[2*i+1];
|
||||
assert(high == high2);
|
||||
}
|
||||
}
|
||||
|
||||
string ref = boost::to_8_bit(output, facet);
|
||||
|
||||
assert(ref == input);
|
||||
}
|
||||
|
||||
int test_main(int ac, char* av[])
|
||||
{
|
||||
std::string input = file_content("utf8.txt");
|
||||
std::string expected = file_content("ucs2.txt");
|
||||
|
||||
test_convert(input, expected);
|
||||
|
||||
if (ac > 1) {
|
||||
cout << "Trying to convert the command line argument\n";
|
||||
|
||||
locale::global(locale(""));
|
||||
std::wstring w = boost::from_local_8_bit(av[1]);
|
||||
|
||||
cout << "Got something, printing decimal code point values\n";
|
||||
for (unsigned i = 0; i < w.size(); ++i) {
|
||||
cout << (unsigned)w[i] << "\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
test/ucs2.txt
Normal file
BIN
test/ucs2.txt
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
117
test/unicode_test.cpp
Normal file
117
test/unicode_test.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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/variables_map.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <boost/function.hpp>
|
||||
using namespace boost;
|
||||
|
||||
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
|
||||
#include <boost/test/test_tools.hpp>
|
||||
|
||||
#include <sstream>
|
||||
using namespace std;
|
||||
|
||||
// Test that unicode input is forwarded to unicode option without
|
||||
// problems.
|
||||
void test_unicode_to_unicode()
|
||||
{
|
||||
options_description desc;
|
||||
|
||||
desc.add_options()
|
||||
("foo", wvalue<wstring>(), "unicode option")
|
||||
;
|
||||
|
||||
vector<wstring> args;
|
||||
args.push_back(L"--foo=\u044F");
|
||||
|
||||
variables_map vm;
|
||||
store(wcommand_line_parser(args).options(desc).run(), vm);
|
||||
|
||||
BOOST_CHECK(vm["foo"].as<wstring>() == L"\u044F");
|
||||
}
|
||||
|
||||
// Test that unicode input is property converted into
|
||||
// local 8 bit string. To test this, make local 8 bit encoding
|
||||
// be utf8.
|
||||
void test_unicode_to_native()
|
||||
{
|
||||
std::codecvt<wchar_t, char, mbstate_t>* facet =
|
||||
new boost::program_options::detail::utf8_codecvt_facet<wchar_t, char>;
|
||||
locale::global(locale(locale(), facet));
|
||||
|
||||
options_description desc;
|
||||
|
||||
desc.add_options()
|
||||
("foo", value<string>(), "unicode option")
|
||||
;
|
||||
|
||||
vector<wstring> args;
|
||||
args.push_back(L"--foo=\u044F");
|
||||
|
||||
variables_map vm;
|
||||
store(wcommand_line_parser(args).options(desc).run(), vm);
|
||||
|
||||
BOOST_TEST(vm["foo"].as<string>() == "\xD1\x8F");
|
||||
}
|
||||
|
||||
void test_native_to_unicode()
|
||||
{
|
||||
std::codecvt<wchar_t, char, mbstate_t>* facet =
|
||||
new boost::program_options::detail::utf8_codecvt_facet<wchar_t, char>;
|
||||
locale::global(locale(locale(), facet));
|
||||
|
||||
options_description desc;
|
||||
|
||||
desc.add_options()
|
||||
("foo", wvalue<wstring>(), "unicode option")
|
||||
;
|
||||
|
||||
vector<string> args;
|
||||
args.push_back("--foo=\xD1\x8F");
|
||||
|
||||
variables_map vm;
|
||||
store(command_line_parser(args).options(desc).run(), vm);
|
||||
|
||||
BOOST_TEST(vm["foo"].as<wstring>() == L"\u044F");
|
||||
}
|
||||
|
||||
// Since we've already tested conversion between parser encoding and
|
||||
// option encoding, all we need to check for config file is that
|
||||
// when reading wistream, it generates proper UTF8 data.
|
||||
void test_config_file()
|
||||
{
|
||||
std::codecvt<wchar_t, char, mbstate_t>* facet =
|
||||
new boost::program_options::detail::utf8_codecvt_facet<wchar_t, char>;
|
||||
locale::global(locale(locale(), facet));
|
||||
|
||||
options_description desc;
|
||||
|
||||
desc.add_options()
|
||||
("foo", value<string>(), "unicode option")
|
||||
;
|
||||
|
||||
std::wstringstream stream(L"foo = \u044F");
|
||||
|
||||
variables_map vm;
|
||||
store(parse_config_file(stream, desc), vm);
|
||||
|
||||
BOOST_TEST(vm["foo"].as<string>() == "\xD1\x8F");
|
||||
}
|
||||
|
||||
int test_main(int, char* [])
|
||||
{
|
||||
test_unicode_to_unicode();
|
||||
test_unicode_to_native();
|
||||
test_native_to_unicode();
|
||||
test_config_file();
|
||||
return 0;
|
||||
}
|
||||
|
||||
BIN
test/utf8.txt
Normal file
BIN
test/utf8.txt
Normal file
Binary file not shown.
232
test/variable_map_test.cpp
Normal file
232
test/variable_map_test.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// 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/variables_map.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <boost/function.hpp>
|
||||
using namespace boost;
|
||||
|
||||
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
|
||||
#include <boost/test/test_tools.hpp>
|
||||
|
||||
#include <sstream>
|
||||
using namespace std;
|
||||
|
||||
#define TEST_CHECK_THROW(expression, exception, description) \
|
||||
try \
|
||||
{ \
|
||||
expression; \
|
||||
BOOST_ERROR(description);\
|
||||
throw 10; \
|
||||
} \
|
||||
catch(exception &) \
|
||||
{ \
|
||||
}
|
||||
|
||||
|
||||
void test_variable_map()
|
||||
{
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("foo,f", new untyped_value)
|
||||
("bar,b", value<string>()->implicit())
|
||||
("biz,z", value<string>()->implicit())
|
||||
("baz", new untyped_value())
|
||||
("output,o", new untyped_value(), "")
|
||||
;
|
||||
char* cmdline3_[] = { "--foo='12'", "--bar=11", "-z3", "-ofoo" };
|
||||
vector<string> cmdline3(cmdline3_,
|
||||
cmdline3_ + sizeof(cmdline3_)/sizeof(cmdline3_[0]));
|
||||
parsed_options a3 = command_line_parser(cmdline3).options(desc).run();
|
||||
variables_map vm;
|
||||
store(a3, vm);
|
||||
notify(vm);
|
||||
BOOST_CRITICAL_TEST(vm.size() == 4);
|
||||
BOOST_TEST(vm["foo"].as<string>() == "'12'");
|
||||
BOOST_TEST(vm["bar"].as<string>() == "11");
|
||||
BOOST_TEST(vm.count("biz") == 1);
|
||||
BOOST_TEST(vm["biz"].as<string>() == "3");
|
||||
BOOST_TEST(vm["output"].as<string>() == "foo");
|
||||
|
||||
int i;
|
||||
desc.add_options()
|
||||
("zee", bool_switch(), "")
|
||||
("zoo", bool_switch(), "")
|
||||
("zak", value<int>(&i), "")
|
||||
("opt", bool_switch(), "");
|
||||
|
||||
char* cmdline4_[] = { "--zee=On", "--zoo", "--zak=13" };
|
||||
vector<string> cmdline4(cmdline4_,
|
||||
cmdline4_ + sizeof(cmdline4_)/sizeof(cmdline4_[0]));
|
||||
parsed_options a4 = command_line_parser(cmdline4).options(desc).run();
|
||||
|
||||
variables_map vm2;
|
||||
store(a4, vm2);
|
||||
notify(vm2);
|
||||
BOOST_CRITICAL_TEST(vm2.size() == 4);
|
||||
BOOST_TEST(vm2["zee"].as<bool>() == true);
|
||||
BOOST_TEST(vm2["zoo"].as<bool>() == true);
|
||||
BOOST_TEST(vm2["zak"].as<int>() == 13);
|
||||
BOOST_TEST(vm2["opt"].as<bool>() == false);
|
||||
BOOST_TEST(i == 13);
|
||||
|
||||
options_description desc2;
|
||||
desc2.add_options()
|
||||
("vee", value<string>()->default_value("42"))
|
||||
("voo", value<string>())
|
||||
("iii", value<int>()->default_value(123))
|
||||
;
|
||||
char* cmdline5_[] = { "--voo=1" };
|
||||
vector<string> cmdline5(cmdline5_,
|
||||
cmdline5_ + sizeof(cmdline5_)/sizeof(cmdline5_[0]));
|
||||
parsed_options a5 = command_line_parser(cmdline5).options(desc2).run();
|
||||
|
||||
variables_map vm3;
|
||||
store(a5, vm3);
|
||||
notify(vm3);
|
||||
BOOST_CRITICAL_TEST(vm3.size() == 3);
|
||||
BOOST_TEST(vm3["vee"].as<string>() == "42");
|
||||
BOOST_TEST(vm3["voo"].as<string>() == "1");
|
||||
BOOST_TEST(vm3["iii"].as<int>() == 123);
|
||||
}
|
||||
|
||||
int stored_value;
|
||||
void notifier(const vector<int>& v)
|
||||
{
|
||||
stored_value = v.front();
|
||||
}
|
||||
|
||||
void test_semantic_values()
|
||||
{
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("foo", new untyped_value())
|
||||
("bar", value<int>())
|
||||
("biz", value< vector<string> >())
|
||||
("baz", value< vector<string> >()->multitoken())
|
||||
("int", value< vector<int> >()->notifier(notifier))
|
||||
;
|
||||
|
||||
|
||||
parsed_options parsed(&desc);
|
||||
vector<option>& options = parsed.options;
|
||||
vector<string> v;
|
||||
v.push_back("q");
|
||||
options.push_back(option("foo", vector<string>(1, "1")));
|
||||
options.push_back(option("biz", vector<string>(1, "a")));
|
||||
options.push_back(option("baz", v));
|
||||
options.push_back(option("bar", vector<string>(1, "1")));
|
||||
options.push_back(option("biz", vector<string>(1, "b x")));
|
||||
v.push_back("w");
|
||||
options.push_back(option("baz", v));
|
||||
|
||||
variables_map vm;
|
||||
store(parsed, vm);
|
||||
notify(vm);
|
||||
BOOST_REQUIRE(vm.count("biz") == 1);
|
||||
BOOST_REQUIRE(vm.count("baz") == 1);
|
||||
const vector<string> av = vm["biz"].as< vector<string> >();
|
||||
const vector<string> av2 = vm["baz"].as< vector<string> >();
|
||||
string exp1[] = { "a", "b x" };
|
||||
BOOST_CHECK(av == vector<string>(exp1, exp1 + 2));
|
||||
string exp2[] = { "q", "q", "w" };
|
||||
BOOST_CHECK(av2 == vector<string>(exp2, exp2 + 3));
|
||||
|
||||
options.push_back(option("int", vector<string>(1, "13")));
|
||||
|
||||
variables_map vm2;
|
||||
store(parsed, vm2);
|
||||
notify(vm2);
|
||||
BOOST_REQUIRE(vm2.count("int") == 1);
|
||||
BOOST_CHECK(vm2["int"].as< vector<int> >() == vector<int>(1, 13));
|
||||
BOOST_CHECK_EQUAL(stored_value, 13);
|
||||
|
||||
vector<option> saved_options = options;
|
||||
|
||||
options.push_back(option("bar", vector<string>(1, "2")));
|
||||
variables_map vm3;
|
||||
BOOST_CHECK_THROW(store(parsed, vm3), multiple_occurences);
|
||||
|
||||
options = saved_options;
|
||||
// Now try passing two int in one 'argv' element.
|
||||
// This should not work.
|
||||
options.push_back(option("int", vector<string>(1, "2 3")));
|
||||
variables_map vm4;
|
||||
BOOST_CHECK_THROW(store(parsed, vm4), validation_error);
|
||||
}
|
||||
|
||||
void test_priority()
|
||||
{
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
// Value of this option will be specified in two sources,
|
||||
// and only first one should be used.
|
||||
("first", value< vector<int > >())
|
||||
// Value of this option will have default value in the first source,
|
||||
// and explicit assignment in the second, so the second should be used.
|
||||
("second", value< vector<int > >()->default_value(vector<int>(1, 1), ""))
|
||||
("aux", value< vector<int > >())
|
||||
// This will have values in both sources, and values should be combined
|
||||
("include", value< vector<int> >()->composing())
|
||||
;
|
||||
|
||||
char* cmdline1_[] = { "--first=1", "--aux=10", "--first=3", "--include=1" };
|
||||
vector<string> cmdline1(cmdline1_,
|
||||
cmdline1_ + sizeof(cmdline1_)/sizeof(cmdline1_[0]));
|
||||
|
||||
parsed_options p1 = command_line_parser(cmdline1).options(desc).run();
|
||||
|
||||
char* cmdline2_[] = { "--first=12", "--second=7", "--include=7" };
|
||||
vector<string> cmdline2(cmdline2_,
|
||||
cmdline2_ + sizeof(cmdline2_)/sizeof(cmdline2_[0]));
|
||||
|
||||
parsed_options p2 = command_line_parser(cmdline2).options(desc).run();
|
||||
|
||||
variables_map vm;
|
||||
store(p1, vm);
|
||||
|
||||
BOOST_REQUIRE(vm.count("first") == 1);
|
||||
BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2);
|
||||
BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1);
|
||||
BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3);
|
||||
|
||||
BOOST_REQUIRE(vm.count("second") == 1);
|
||||
BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1);
|
||||
BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 1);
|
||||
|
||||
store(p2, vm);
|
||||
|
||||
// Value should not change.
|
||||
BOOST_REQUIRE(vm.count("first") == 1);
|
||||
BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2);
|
||||
BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1);
|
||||
BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3);
|
||||
|
||||
// Value should change to 7
|
||||
BOOST_REQUIRE(vm.count("second") == 1);
|
||||
BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1);
|
||||
BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 7);
|
||||
|
||||
BOOST_REQUIRE(vm.count("include") == 1);
|
||||
BOOST_REQUIRE(vm["include"].as< vector<int> >().size() == 2);
|
||||
BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[0], 1);
|
||||
BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[1], 7);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int test_main(int, char* [])
|
||||
{
|
||||
test_variable_map();
|
||||
test_semantic_values();
|
||||
test_priority();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user