123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- <?xml version="1.0" standalone="yes"?>
- <!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
- "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
- [
- <!ENTITY % entities SYSTEM "program_options.ent" >
- %entities;
- ]>
- <section id="program_options.howto">
- <title>How To</title>
- <para>This section describes how the library can be used in specific
- situations.</para>
- <!--
- validators
- positional options
- options groups/hidden options
- -->
- <section>
- <title>Non-conventional Syntax</title>
- <para>Sometimes, standard command line syntaxes are not enough. For
- example, the gcc compiler has "-frtti" and -fno-rtti" options, and this
- syntax is not directly supported.
- </para>
- <indexterm><primary>additional parser</primary></indexterm>
- <para>For such cases, the library allows the user to provide an
- <firstterm>additional parser</firstterm> -- a function which will be called on each
- command line element, before any processing by the library. If the
- additional parser recognises the syntax, it returns the option name and
- value, which are used directly. The above example can be handled by the
- following code:
- </para>
- <para>
- <programlisting>
- 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());
- }
- }
- </programlisting>
- Here's the definition of the additional parser. When parsing the command
- line, we pass the additional parser:
- <programlisting>
- store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
- .run(), vm);
- </programlisting>
- The complete example can be found in the "example/custom_syntax.cpp"
- file.
- </para>
- </section>
- <section>
- <title>Response Files</title>
- <indexterm><primary>response files</primary></indexterm>
- <para>Some operating system have very low limits of the command line
- length. The common way to work around those limitations is using
- <firstterm>response files</firstterm>. A response file is just a
- configuration file which uses the same syntax as the command line. If
- the command line specifies a name of response file to use, it's loaded
- and parsed in addition to the command line. The library does not
- provide direct support for response files, so you'll need to write some
- extra code.
- </para>
- <para>
- First, you need to define an option for the response file:
- <programlisting>
- ("response-file", value<string>(),
- "can be specified with '@name', too")
- </programlisting>
- </para>
- <para>Second, you'll need an additional parser to support the standard syntax
- for specifying response files: "@file":
- <programlisting><![CDATA[
- pair<string, string> at_option_parser(string const&s)
- {
- if ('@' == s[0])
- return std::make_pair(string("response-file"), s.substr(1));
- else
- return pair<string, string>();
- }
- ]]>
- </programlisting>
- </para>
- <para>Finally, when the "response-file" option is found, you'll have to
- load that file and pass it to the command line parser. This part is the
- hardest. We'll use the Boost.Tokenizer library, which works but has some
- limitations. You might also consider Boost.StringAlgo. The code is:
- <programlisting><![CDATA[
- if (vm.count("response-file")) {
- // Load the file and tokenize it
- ifstream ifs(vm["response-file"].as<string>().c_str());
- if (!ifs) {
- cout << "Could not open the response file\n";
- return 1;
- }
- // Read the whole file into a string
- stringstream ss;
- ss << ifs.rdbuf();
- // Split the file content
- char_separator<char> sep(" \n\r");
- std::string ResponsefileContents( ss.str() );
- tokenizer<char_separator<char> > tok(ResponsefileContents, sep);
- vector<string> args;
- copy(tok.begin(), tok.end(), back_inserter(args));
- // Parse the file and store the options
- store(command_line_parser(args).options(desc).run(), vm);
- }
- ]]>
- </programlisting>
- The complete example can be found in the "example/response_file.cpp"
- file.
- </para>
- </section>
- <section>
- <title>Winmain Command Line</title>
- <para>On the Windows operating system, GUI applications receive the
- command line as a single string, not split into elements. For that reason,
- the command line parser cannot be used directly. At least on some
- compilers, it is possible to obtain
- the split command line, but it's not clear if all compilers support the
- same mechanism on all versions of the operating system. The
- <code>split_winmain</code> function is a portable mechanism provided
- by the library.</para>
- <para>Here's an example of use:
- <programlisting>
- vector<string> args = split_winmain(lpCmdLine);
- store(command_line_parser(args).options(desc).run(), vm);
- </programlisting>
- The <code>split_winmain</code> function is overloaded for <code>wchar_t</code> strings, so can
- also be used in Unicode applications.
- </para>
- </section>
- <section>
- <title>Option Groups and Hidden Options</title>
- <para>Having a single instance of the &options_description; class with all
- the program's options can be problematic:
- <itemizedlist>
- <listitem>
- <para>Some options make sense only for specific source, for example,
- configuration files.</para>
- </listitem>
- <listitem>
- <para>The user would prefer some structure in the generated help message.</para>
- </listitem>
- <listitem>
- <para>Some options shouldn't appear in the generated help message at all.</para>
- </listitem>
- </itemizedlist>
- </para>
- <para>To solve the above issues, the library allows a programmer to create several
- instances of the &options_description; class, which can be merged in
- different combinations. The following example will define three groups of
- options: command line specific, and two options group for specific program
- modules, only one of which is shown in the generated help message.
- </para>
- <para>Each group is defined using standard syntax. However, you should
- use reasonable names for each &options_description; instance:
- <programlisting><![CDATA[
- options_description general("General options");
- general.add_options()
- ("help", "produce a help message")
- ("help-module", value<string>(),
- "produce a help for a given module")
- ("version", "output the version number")
- ;
- options_description gui("GUI options");
- gui.add_options()
- ("display", value<string>(), "display to use")
- ;
- options_description backend("Backend options");
- backend.add_options()
- ("num-threads", value<int>(), "the initial number of threads")
- ;
- ]]></programlisting>
- </para>
- <para>After declaring options groups, we merge them in two
- combinations. The first will include all options and be used for parsing. The
- second will be used for the "--help" option.
- <programlisting>
- // Declare an options description instance which will include
- // all the options
- options_description all("Allowed options");
- all.add(general).add(gui).add(backend);
- // Declare an options description instance which will be shown
- // to the user
- options_description visible("Allowed options");
- visible.add(general).add(gui);
- </programlisting>
- </para>
- <para>What is left is to parse and handle the options:
- <programlisting><![CDATA[
- variables_map vm;
- store(parse_command_line(ac, av, all), vm);
- if (vm.count("help"))
- {
- cout << visible;
- return 0;
- }
- if (vm.count("help-module")) {
- const string& s = vm["help-module"].as<string>();
- if (s == "gui") {
- cout << gui;
- } else if (s == "backend") {
- cout << backend;
- } else {
- cout << "Unknown module '"
- << s << "' in the --help-module option\n";
- return 1;
- }
- return 0;
- }
- if (vm.count("num-threads")) {
- cout << "The 'num-threads' options was set to "
- << vm["num-threads"].as<int>() << "\n";
- }
- ]]></programlisting>
- When parsing the command line, all options are allowed. The "--help"
- message, however, does not include the "Backend options" group -- the
- options in that group are hidden. The user can explicitly force the
- display of that options group by passing "--help-module backend"
- option. The complete example can be found in the
- "example/option_groups.cpp" file.
- </para>
- </section>
- <section>
- <title>Custom Validators</title>
- <para>By default, the conversion of option's value from string into C++
- type is done using iostreams, which sometimes is not convenient. The
- library allows the user to customize the conversion for specific
- classes. In order to do so, the user should provide suitable overload of
- the <code>validate</code> function.
- </para>
- <para>
- Let's first define a simple class:
- <programlisting><![CDATA[
- struct magic_number {
- public:
- magic_number(int n) : n(n) {}
- int n;
- };
- ]]></programlisting> and then overload the <code>validate</code> function:
- <programlisting><![CDATA[
- void validate(boost::any& v,
- const std::vector<std::string>& values,
- magic_number* target_type, int)
- {
- static regex r("\\d\\d\\d-(\\d\\d\\d)");
- using namespace boost::program_options;
- // Make sure no previous assignment to 'a' was made.
- validators::check_first_occurrence(v);
- // 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)) {
- v = any(magic_number(lexical_cast<int>(match[1])));
- } else {
- throw validation_error(validation_error::invalid_option_value);
- }
- }
- ]]>
- </programlisting>The function takes four parameters. The first is the storage
- for the value, and in this case is either empty or contains an instance of
- the <code>magic_number</code> class. The second is the list of strings
- found in the next occurrence of the option. The remaining two parameters
- are needed to workaround the lack of partial template specialization and
- partial function template ordering on some compilers.
- </para>
- <para>The function first checks that we don't try to assign to the same
- option twice. Then it checks that only a single string was passed
- in. Next the string is verified with the help of the Boost.Regex
- library. If that test is passed, the parsed value is stored into the
- <code>v</code> variable.
- </para>
- <para>The complete example can be found in the "example/regex.cpp" file.
- </para>
- </section>
- <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. To make use of Unicode you'll need
- <emphasis>some</emphasis> Unicode-aware options. They are different from
- ordinary options in 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 an ascii parser passes data to an ascii option, or a Unicode
- parser passes data to a Unicode option, the data are not changed at
- all. So, the ascii option will see a string in local 8-bit encoding, and
- the Unicode option will see whatever string was passed as the Unicode
- input.
- </para>
- <para>What happens when Unicode data is passed to an ascii option, and
- vice versa? The library automatically performs the conversion from
- Unicode to local 8-bit encoding. For example, if command line is in
- ascii, but you use <code>wstring</code> options, then the ascii input
- will be converted into Unicode.
- </para>
- <para>To perform the conversion, the library uses the <code>codecvt<wchar_t,
- char></code> locale facet from the global locale. If
- you want to work with strings that 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 the user's selected
- locale.
- </para>
- <para>It's wise to check the status of the C++ locale support on your
- implementation, though. The quick test involves three steps:
- <orderedlist>
- <listitem>
- <para>Go the the "test" directory and build the "test_convert" binary.</para>
- </listitem>
- <listitem>
- <para>Set some non-ascii locale in the environment. 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 with any non-ascii string in the
- selected encoding as its parameter. 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>
- <title>Allowing Unknown Options</title>
- <para>Usually, the library throws an exception on unknown option names. This
- behaviour can be changed. For example, only some part of your application uses
- <libraryname>Program_options</libraryname>, and you wish to pass unrecognized options to another part of
- the program, or even to another application.</para>
- <para>To allow unregistered options on the command line, you need to use
- the &basic_command_line_parser; class for parsing (not &parse_command_line;)
- and call the <methodname alt="boost::program_options::basic_command_line_parser::allow_unregistered">allow_unregistered</methodname>
- method of that class:
- <programlisting>
- parsed_options parsed =
- command_line_parser(argc, argv).options(desc).allow_unregistered().run();
- </programlisting>
- For each token that looks like an option, but does not have a known name,
- an instance of &basic_option; will be added to the result.
- The <code>string_key</code> and <code>value</code> fields of the instance will contain results
- of syntactic parsing of the token, the <code>unregistered</code> field will be set to <code>true</code>,
- and the <code>original_tokens</code> field will contain the token as it appeared on the command line.
- </para>
- <para>If you want to pass the unrecognized options further, the
- <functionname alt="boost::program_options::collect_unrecognized">collect_unrecognized</functionname> function can be used.
- The function will collect original tokens for all unrecognized values, and optionally, all found positional options.
- Say, if your code handles a few options, but does not handle positional options at all, you can use the function like this:
- <programlisting>
- vector<string> to_pass_further = collect_unrecognized(parsed.options, include_positional);
- </programlisting>
- </para>
- </section>
- <section>
- <title>Testing Option Presence</title>
- <para>Until now we have tested whether an option has been set using the
- <methodname alt="boost::program_options::variables_map::count">count</methodname> method on the &variables_map;
- class; as you are repeating the (string literal) name of the option this is prone to typos and/or errors
- resulting from renaming the option in one place but not the other:
- <programlisting><![CDATA[
- po::options_description desc("Allowed options");
- desc.add_options()
- ("compression", po::value<int>(), "set compression level")
- ;
- po::variables_map vm;
- po::store(po::parse_command_line(ac, av, desc), vm);
- po::notify(vm);
- if (vm.count("compression")) {
- cout << "Compression level was set to "
- << vm["compression"].as<int>() << ".\n";
- } else {
- cout << "Compression level was not set.\n";
- }
- ]]>
- </programlisting>
- </para>
- <para>Instead, you can use a variable of type <classname alt="boost::optional">boost::optional</classname>;
- <libraryname>Program_options</libraryname> provides special support for <libraryname>Boost.Optional</libraryname>
- such that if the user specifies the option the <classname alt="boost::optional">boost::optional</classname>
- variable will be initialized to the appropriate value:
- <programlisting><![CDATA[
- po::options_description desc("Allowed options");
- boost::optional<int> compression;
- desc.add_options()
- ("compression", po::value(&compression), "set compression level")
- ;
- po::variables_map vm;
- po::store(po::parse_command_line(ac, av, desc), vm);
- po::notify(vm);
- if (compression) {
- cout << "Compression level was set to " << *compression << ".\n";
- } else {
- cout << "Compression level was not set.\n";
- }
- ]]>
- </programlisting>
- </para>
- </section>
- </section>
- <!--
- Local Variables:
- mode: nxml
- sgml-indent-data: t
- sgml-parent-document: ("userman.xml" "chapter")
- sgml-set-face: t
- End:
- -->
|