C++ Klasse zum einfachen Parsen der Kommandozeilenargumente
C++ class template for parsing command line arguments
(Die Dokumentation lass ich mal in Englisch stehen. Source code ganz unten.)
*NIX/Linux style command line argument handling class template. Parses
options and positional arguments similar to ::getopt(), except that
defining accepted options and retrieving their values is simpler.
Additionally ...
- You can define what data types the argument must have, and can directly retrieve the converted values with the type conversion operators (bool), (double) and (std::string). 
- You can specify if the argument is - required (error if it is omitted or no value is given),
- optional (default value if it is omitted, error if no value given),
- flag (can be omitted or set, but does not accept a value),
- optinoal/val(default if omitted, other default if no value is given).
 
- It can implicitly handle errors (unknown options, missing options, type mismatch). 
- It can generate a complete program help with synopsis, program description, detailed argument help etc (*NIX/Linux style help/man text). Implicit soft wrapping of text. 
- It can allow you to ignore unknown option errors and retrieve the value of these options. 
- It can return the list of positional arguments. 
- You can retrieve the positions of all parsed arguments (e.g. in case that an option must occur before or after a positional argument or another options, etc). 
- Options can occur multiple times (e.g. - -i file1 -i file2 -i file3).
- You can easily retrieve numeric option values / positional argument values using the argument class typecast operators - (double),- (bool), also- (string), e.g.- cout << (bool)cli_args["verbose"] << endl;.
- Allows value assignments with "=" for short options as well, e.g. - -f=file1.
- Accepts commonly known separators like - --for "end of options" or the values- -for "use stdin/stdout";- -0..- -9can be defined as short options and will be differed from negative numbers.
- The class works without exceptions (except exceptions that can can come from the STL containers). 
Datei
File
Examples how arguments can be specified from the command line:
$my_tar -x -v -z -f ~/file.tar.gz
$my_tar -xvzf ~/file.tar.gz
$my_tar -xvzf~/file.tar.gz
$my_tar -xvzf=~/file.tar.gz
$my_tar --extract -vz --file ~/file.tar.gz
$my_tar --extract -vz --file=~/file.tar.gz
 
$my_prog --pi=3.1415 'positional 1' -- --no-option --neither-option
$my_prog -9  # Short opt if it is defined, a positional negative number
$my_prog -i - -o - # Single "-" often also known as "use STDIN/STDOUT".Template and object
namespace sw {
 
  // Template
  namespace templates {
    template <typename str_t> class basic_cli_args;
  }
 
  // Standard specialisation (std::string) : (class) sw::cli_args_t
  typedef templates::basic_cli_args<std::string> cli_args_t;
 
  // Used default global object
  cli_args_t cli_args = cli_args_t();
}As normally only one instance is required, latter is initialised as global
object sw::cli_args with the template specialisation std::string (typedef'ed
sw::cli_args_t). Around this class there are some helpers for argument
definitions and contents and iostream operations. They are all accessible
as typedefs in sw::cli_args_t and allow smart auto-completion in IDEs.
Essentially the usage is:
- Include this file, then you have the object - sw::cli_args.
- In - main(), pass- argcand- argvto the object using the operator ():- int main(int argc, char** argv) { cli_args(argc, argv); [...] } 
- In whatever function/method you like, define the arguments you accept, as well using the - operator()(this overload takes only string arguments).- cli_args( SHORT_OPT, // e.g. "i" LONG_OPT, // e.g. "input-file" DATA_TYPE, // e.g. "s" for string DEFAULT_IF_ARGUMENT_IS_COMPLETELY_OMMITTED, // e.g. "" DEFAULT_IF_ARGUMENT_HAS_NO_VALUE, // e.g. "" HELP_TEXT_FOR_THIS_ARGUMENT, // e.g. "Input file to ..." ); // You can chain call this operator cli_args("i","in", "s")("o","out", "s")("v","verbose", "b", "", "1");`
- Call - cli_args.parse();
- Now you can get arguments and other information. Use the operator[] to query arguments. If an argument is not found an empty argument ( - narg) is returned. You can use- cli_args["string"]to get options, and- cli_args[UINT]to get arguments by their position.- Get option values by short/long name: - string ifile = (string) cli_args["i"]; string ofile = (string) cli_args["out"]; bool verbose = (bool) cli_args["v"];
- Get argument values by position: - string arg = (string) cli_args[0];
- Get information about options/arguments: - if(cli_args["i"].is_narg()) { Option -i is missing } if(cli_args["i"].is_ok()) { All ok with this option } if(cli_args["i"].is_bool()) { Its a boolean per definition } if(cli_args["i"].is_float()) { Its a floating point number } if(cli_args[0].is_positional()) { First argument is positional } if(cli_args[0].value()=="") { value() returns the string value }
- Getting errors: - if(cli_args.has_errors()) { Something missing, wrong data type etc. } cli_args_t::str_vect_t errors = cli_args.errors(); // Errors as string vector // Or simpler: Let the class print errors to stderr and exit (code 1) cli_args.handle_help_and_errors();
- Getting other information: - string name = cli_args.program_name(); // Program (base)name cli_args_t::args_t positionals = cli_args.positional_arguments();
- Stream operations / object dump: - cerr << cli_args << endl; // Dump object cerr << cli_args["i"] << endl; // Dump single argument object cerr << cli_args.positional_arguments() << endl; // Dump positionals cli_args.help(); // Print defined options help
 
Data type definitions (all given as string values)
- "s": string
- "b": bool, nonzero numbers=true, literals like yes,no,off,ja,oui accepted.
- "n": number, means floating point number
- "i": number, integer
- "f": file, practically a string with some restrictions
"Kind of option" definition: This is done with the two default values.
Depending on weather a default is an empty string or not, the meaning changes:
- Empty "option omitted", empty "value omitted": This menas the option and its value is required, e.g. for an input file: - E.g. - cli_args("i", "inp", "s", "", "").- Needs one of: - -i file,- -i=file,- -ifile,- --inp=file,- --inp file.
- Empty "option omitted", set "value omitted": Means that this options is a flag. It does not accept a value, except the value is given with "=" and corresponds to the default value. - E.g. - ("x", "extract", "b", "", "1").- Valid would be: - -x,- -x=yes,- --extract,- --extract=true,- -x=enabled.
- Set "option omitted", empty "value omitted": That means this option optional and can be omitted, but if it is set it must have a value as well. - E.g. - ("b", "bit-rate", "i", "115200", "").- If - -bor- --bit-rateis both missing, 115200 will be retrieved. Otherwise the argument must look like:- -b9600,- -b 9600,- -b=9600,- --bit-rate=9600,- --bit-rate 9600.
- Set "option omitted", set "value omitted": Means the option can be omitted and it can be set like a flag without a value. Verbosity is an example for this: - E.g. - ("v", "verbose", "i", "0", "1").- Now, if no - -vor- --verboseis set 0 is assumed. If the user says- -vor- --verbose, the missing value defaults to 1. If the user says- -v2or- --verbose=2you will read 2.
Things to be aware of ...
- Using optional arguments with optional values you can run into conditions where the parser cannot be sure if an argument is positional or the optional value of an option. E.g. - ("v", "verbose", "i", "0", "1"). Saying- -v 2it could be "verbose level 2" or "verbose level 1 and positional 2". A similar problem occurrs when inlining options, like- tar -xvzf. If- vwould a string data type it is not clear if the default string should be used or if the optional value is like- -v=zf. For this reason the class follows some rules to differ:- --opt VALUE is NOT allowed like this. VALUE will be positional. --opt=VALUE is ok -o VALUE again, VALUE will be positional. -o=VALUE is ok -opq (joined short opts): If `p` is a known flag or on data type missmatch it will be like `-o -pq`, if not it is like `-o=pq`.- In short: The parser tries to get what the user means, but simply prefer required arguments, flags and "optinoal with value" to avoid ambiguous argument interpretations. 
- When defining numeric short options like - -0to- -9be aware that your program should not deal with numbers that can be negative.
- Positions in the argument list are not the equivalent to the - argv[]indices that- main()gets, because option value is not counted as position. As well, position 0 is not the program path, it is the first argument or option. The program name is retrieved with- program_name(). E.g.- $./myprog -f FILE POS1 POS2- will get: - - cli_args.program_name() = "myprog" - cli_args.arguments()[0] = { pos:0, key: "-f", val: "FILE", def:"(its defin)"} - cli_args.arguments()[1] = { pos:1, key: "", val: "POS1", def:"(positional)" } - cli_args.arguments()[2] = { pos:2, key: "", val: "POS2", def:"(positional)" } - cli_args.arguments()[>2]= (NARG)- This might be confusing at the first look, however, it saves practically a lot of work, as the positions are the logical positions of the arguments that the program gets, index starting at - 0as vectors do.
- Don't define single character long options if you define a short option with the same name. E.g. - -vand- --v. Class can't differ them and will return the first that it sees in the argument list.
- If an argument is defined multiple times, the - operator[string key]returns the first of them. Iterate through- arguments()or use- operator[](int index)to get all of them. E.g.- $prog -i file1 -i file2 -i file3-->- cli_args["i"]=="file1";
Beispiele
Examples
#include <cli_args.hh>
#include <iostream>
 
using namespace sw;
using namespace std;
 
int main(int argc, char** argv)
{
  // The cli_args object of type class cli_args_t
  cli_args(argc, argv) // Assign argc and argv
  .allow_unknown_options(true) // We allow options not in the list above
  .parse()                    // Parse, make argument list, check errors
  .handle_help_and_errors(    // Quick way to handle -h/--help and errors.
    "Program to calculate something.", // Help description, default: ""
    "v1.0, stfwi, 2010",          // Version, author, etc, default: ""
    "[c1 [c2 [...]]]",            // Positionals synopsis help, default: ""
    " 0: successful\n>0: error",  // Return values help, default: ""
    0,                            // Exit code (if it exits), default: 1
    cerr                          // ostream to use, default: std::cerr
  );
  cout << cli_args << endl;
  return 0;
}Microtest:
#include <cli_args.hh>
#include "test.hh"
 
void test1()
{
 const char* argv[] = {
    "cli_args"
    , "--help"
    , "-i", "FILE1"
    , "--output-file=FILE2"
    , "--verbose", "2"  // that will NOT be --verbose==2; 2 is treated positional
    , "-b=2" // same decl as --verbose, but b will be 2 because it is known here that 2 belongs to b.
    , "-r=.2"
    , "-f", "rrr"
    , "-a=2"
    , "--flag1"
    , "--flag2"
    , "--flag3"
    , "--"
    , "--"
    , "POS"
    , NULL
  };
 
  {
    std::stringstream ss;
    ss << "Test args: ";
    for(const char** p=argv; *p!=NULL; ++p) ss << " '" << *p << "'";
    test_comment(ss.str());
  }
 
  using sw::cli_args;
 
  int argc = (sizeof(argv)/sizeof(char*))-1;
  cli_args(argc, argv)
    ("i", "input-file" , "f", ""    , "" , "The input file to use.")
    ("o", "output-file", "f", ""    , "" , "The output file to save to.")
    ("v", "verbose",     "i", "0"   , "1", "Verbosity level.")
    ("r", "noise-ratio", "n", "0.1" , "" , "Expected noise ratio.")
    ("n", "normalize",   "b", ""    , "1", "Normalise output.")
    ("f", "filter",      "s", "rms" , "" , "Filter to apply.")
    // --pi: optional number with value, default: 3.14....
    ("" , "pi"   , "n", "3.1415926")
    // -i/--int: optional (i)nt, if omitted -> 0, if no value set -> 1000
    ("b", "int",  "i", "1", "0")   // -b,--bool: optional, if omitted:true
    ("", "flag1",  "s", "", "doit") // flag1, string
    ("", "flag2",  "i", "", "10")  // flag2, int
    ("", "flag3",  "n", "", "1.1") // flag3, float
    ("", "flag4",  "n", "", "5")   // flag4, float
 
    .allow_unknown_options(true)
    .parse()
    .handle_help_and_errors(
      "Microtest 1.\n\n"
      "- This text will be wrapped. This text will be wrapped This text "
        "will be wrapped. This text will be wrapped.",
      " VERSION_COPY_STATEMENT",
      " SYNOPSIS_STATEMENT",
      " RETURNCODE_STATEMENT",
      0,
      cerr
    );
 
  // Fetch arguments
//  test_comment( "Positional arguments:" << cli_args.positional_arguments() );
//  test_comment( "Args dump: " << cli_args );
 
 
  // flag4 is a flag with a numeric value.
  test_expect( !cli_args["flag4"].is_undefined_option() );
  test_expect(  cli_args["flag4"].is_narg() );
  test_expect(  cli_args["flag4"].is_ok() );
  test_expect( !cli_args["flag4"].is_bool() );
  test_expect( !cli_args["flag4"].is_file() );
  test_expect(  cli_args["flag4"].definition().is_float() );
  test_expect( !cli_args["flag4"].is_int() );
  test_expect( !cli_args["flag4"].is_positional() );
  test_expect( !cli_args["flag4"].is_string() );
  test_expect( (string) cli_args["flag4"] == "" );
  test_expect( (double) cli_args["flag4"] == 0);
  test_expect( (bool) cli_args["flag4"] == false);
 
  // --help used in argument list, but defined in the expected arguments, hence:
  test_expect(  cli_args["help"].is_undefined_option() );
  test_expect( !cli_args["help"].is_narg() );       // it is in the argument list --> not narg
  test_expect( !cli_args["help"].is_ok() );         // not ok because not expected
  test_expect( !cli_args["help"].is_bool() );       // everything below must return false ...
  test_expect( !cli_args["help"].is_file() );       //  ... because unexpected arg
  test_expect( !cli_args["help"].is_float() );
  test_expect( !cli_args["help"].is_int() );
  test_expect( !cli_args["help"].is_positional() );
  test_expect( !cli_args["help"].is_string() );
  test_expect( (bool) cli_args["help"] == true );   // bool === not 0 or not empty
  test_expect( (string) cli_args["help"] == "" );
  test_expect( sw::utest::isnan((double) cli_args["help"]));
 
  // -i FILE1 used in argument list, i expects an argument "f"===file===string
  test_expect( !cli_args["i"].is_undefined_option() );  // is registered in cli args
  test_expect( !cli_args["i"].is_narg() );              // given argument, not narg
  test_expect(  cli_args["i"].is_ok() );                // is file --> ok
  test_expect( !cli_args["i"].is_bool() );              // defined as file, not bool
  test_expect(  cli_args["i"].is_file() );              // defined as file
  test_expect( !cli_args["i"].is_float() );             // etc .
  test_expect( !cli_args["i"].is_int() );
  test_expect( !cli_args["i"].is_positional() );
  test_expect(  cli_args["i"].is_string() );            // File is implicitly string
  test_expect( (string) cli_args["i"] == "FILE1" );
  test_expect( (bool) cli_args["i"] == true );          // bool === not 0 or not empty
  test_expect( sw::utest::isnan((double) cli_args["i"]));
 
  // --output-file=FILE2 used in argument list, --output-file is mapped together with -o
  // and expects an argument "f"===file===string --- same as -i:
  test_expect( !cli_args["o"].is_undefined_option() );
  test_expect( !cli_args["o"].is_narg() );
  test_expect(  cli_args["o"].is_ok() );
  test_expect( !cli_args["o"].is_bool() );
  test_expect(  cli_args["o"].is_file() );
  test_expect( !cli_args["o"].is_float() );
  test_expect( !cli_args["o"].is_int() );
  test_expect( !cli_args["o"].is_positional() );
  test_expect(  cli_args["o"].is_string() );
  test_expect( (string) cli_args["o"] == "FILE2" );
  test_expect( (bool) cli_args["o"] == true );          // bool === not 0 or not empty
  test_expect( sw::utest::isnan((double) cli_args["o"]));
 
  // --verbose is optional with optional arg. if --verbose=2 had been specified,
  // the values would be 2, but on --verbose 2 the value assignment is ambiguous,
  // and in doubt the the argument is positional. Hence, --verbose counts as specified
  // but without given value, default is then --verbose===1:
  test_expect( !cli_args["verbose"].is_undefined_option() );
  test_expect( !cli_args["verbose"].is_narg() );
  test_expect(  cli_args["verbose"].is_ok() );
  test_expect( !cli_args["verbose"].is_bool() );
  test_expect( !cli_args["verbose"].is_file() );
  test_expect( !cli_args["verbose"].is_float() );
  test_expect(  cli_args["verbose"].is_int() );
  test_expect( !cli_args["verbose"].is_positional() );
  test_expect( !cli_args["verbose"].is_string() );
  test_expect( (double) cli_args["verbose"] == 1);
  test_expect( (int) cli_args["verbose"] == 1 );
  test_expect( (bool) cli_args["verbose"] == true );
 
  // -b is optional with optional arg, and given as -b=2, so it's not ambiguous.
  test_expect( !cli_args["b"].is_undefined_option() );
  test_expect( !cli_args["b"].is_narg() );
  test_expect(  cli_args["b"].is_ok() );
  test_expect( !cli_args["b"].is_bool() );
  test_expect( !cli_args["b"].is_file() );
  test_expect( !cli_args["b"].is_float() );
  test_expect(  cli_args["b"].is_int() );
  test_expect( !cli_args["b"].is_positional() );
  test_expect( !cli_args["b"].is_string() );
  test_expect( (double) cli_args["b"] == 2);
  test_expect( (int) cli_args["b"] == 2 );
  test_expect( (bool) cli_args["b"] == true );
 
  // -r or --noise-ratio, defined as numeric, default 0.1, given as 0.2.
  test_expect( !cli_args["r"].is_undefined_option() );
  test_expect( !cli_args["r"].is_narg() );
  test_expect(  cli_args["r"].is_ok() );
  test_expect( !cli_args["r"].is_bool() );
  test_expect( !cli_args["r"].is_file() );
  test_expect(  cli_args["r"].is_float() );
  test_expect( !cli_args["r"].is_int() );
  test_expect( !cli_args["r"].is_positional() );
  test_expect( !cli_args["r"].is_string() );
  test_expect( (double) cli_args["r"] == 0.2 );
  test_expect( (int) cli_args["r"] == 0 );
  test_expect( (bool) cli_args["r"] == true ); // bool === not 0 or not empty
 
  // -n or --normalize, optional and not specified
  test_expect( !cli_args["n"].is_undefined_option() );
  test_expect(  cli_args["n"].is_narg() );
  test_expect(  cli_args["n"].is_ok() );
  test_expect(  cli_args["n"].is_bool() );
  test_expect( !cli_args["n"].is_file() );
  test_expect( !cli_args["n"].is_float() );
  test_expect( !cli_args["n"].is_int() );
  test_expect( !cli_args["n"].is_positional() );
  test_expect( !cli_args["n"].is_string() );
  test_expect( (double) cli_args["n"] == 0);
  test_expect( (int) cli_args["n"] == 0);
  test_expect( (bool) cli_args["n"] == false );
 
  // -f is expected as string and a given argument:
  test_expect( !cli_args["f"].is_undefined_option() );
  test_expect( !cli_args["f"].is_narg() );
  test_expect(  cli_args["f"].is_ok() );
  test_expect( !cli_args["f"].is_bool() );
  test_expect( !cli_args["f"].is_file() );
  test_expect( !cli_args["f"].is_float() );
  test_expect( !cli_args["f"].is_int() );
  test_expect( !cli_args["f"].is_positional() );
  test_expect(  cli_args["f"].is_string() );
  test_expect( (string) cli_args["f"] == "rrr" );
  test_expect( (int) cli_args["f"] == (int) cli_args.narg );
  test_expect( sw::utest::isnan((double) cli_args["f"]) );
  test_expect( (bool) cli_args["f"] == true ); // not empty --> true
 
  // -a is not registered as expected or optional argument, however, allow_unknown_options(true)
  // does not cause exiting, and we can fetch the value, too. The following expectation apply:
  test_expect(  cli_args["a"].is_undefined_option() );
  test_expect( !cli_args["a"].is_narg() );
  test_expect( !cli_args["a"].is_ok() );            // Not defined, hence, no definition, hence
  test_expect( !cli_args["a"].is_bool() );          // all type queries return false:
  test_expect( !cli_args["a"].is_file() );
  test_expect( !cli_args["a"].is_float() );
  test_expect( !cli_args["a"].is_int() );
  test_expect( !cli_args["a"].is_positional() );
  test_expect( !cli_args["a"].is_string() );
  test_expect( (string) cli_args["a"] == "2" );
  test_expect( (bool) cli_args["a"] == true); // not 0, not empty --> true
  test_expect( (double) cli_args["a"] == 2);
  test_expect( (int) cli_args["a"] == 2 );
 
  // flag1 is a flag, with a string value if given in the argument list. If the flag is
  // specified, the value must be exactly the expected value, or the argument will be not ok.
  test_expect( !cli_args["flag1"].is_undefined_option() );
  test_expect( !cli_args["flag1"].is_narg() );
  test_expect(  cli_args["flag1"].is_ok() );
  test_expect( !cli_args["flag1"].is_bool() );
  test_expect( !cli_args["flag1"].is_file() );
  test_expect( !cli_args["flag1"].is_float() );
  test_expect( !cli_args["flag1"].is_int() );
  test_expect( !cli_args["flag1"].is_positional() );
  test_expect(  cli_args["flag1"].is_string() );
  test_expect( (string) cli_args["flag1"] == "doit" );
  test_expect( (bool) cli_args["flag1"] == true); // not 0, not empty --> true
 
  // flag2 is a flag, with a int value if given in the argument list.
  test_expect( !cli_args["flag2"].is_undefined_option() );
  test_expect( !cli_args["flag2"].is_narg() );
  test_expect(  cli_args["flag2"].is_ok() );
  test_expect( !cli_args["flag2"].is_bool() );
  test_expect( !cli_args["flag2"].is_file() );
  test_expect( !cli_args["flag2"].is_float() );
  test_expect(  cli_args["flag2"].is_int() );
  test_expect( !cli_args["flag2"].is_positional() );
  test_expect( !cli_args["flag2"].is_string() );
  test_expect( (int) cli_args["flag2"] == 10 );
  test_expect( (bool) cli_args["flag2"] == true);
 
  // flag3 is a flag with a numeric value.
  test_expect( !cli_args["flag3"].is_undefined_option() );
  test_expect( !cli_args["flag3"].is_narg() );
  test_expect(  cli_args["flag3"].is_ok() );
  test_expect( !cli_args["flag3"].is_bool() );
  test_expect( !cli_args["flag3"].is_file() );
  test_expect(  cli_args["flag3"].is_float() );
  test_expect( !cli_args["flag3"].is_int() );
  test_expect( !cli_args["flag3"].is_positional() );
  test_expect( !cli_args["flag3"].is_string() );
  test_expect( (string) cli_args["flag3"] == "1.1" );
  test_expect( (double) cli_args["flag3"] == 1.1 );
  test_expect( (int) cli_args["flag3"] == 1 );
  test_expect( (bool) cli_args["flag3"] == true); // not 0, not empty --> true
 
  // Positionals:
  // cli_args --help .... --verbose 2 -b=2 ... -- -- POS
  //          ______________________^__________^^_^^_^^^
  // ________________________________separator_||
  if(test_expect_cond(cli_args.positional_arguments().size() == 3)) {
    const char* vals[3] = { "2", "--", "POS" };
    for(int i=0; i<3; ++i) {
      test_expect( !cli_args.positional_arguments()[i].is_undefined_option() );
      test_expect( !cli_args.positional_arguments()[i].is_narg() );
      test_expect(  cli_args.positional_arguments()[i].is_ok() );     // always ok, but the type is
      test_expect( !cli_args.positional_arguments()[i].is_bool() );   // never known ...
      test_expect( !cli_args.positional_arguments()[i].is_float() );
      test_expect( !cli_args.positional_arguments()[i].is_int() );
      test_expect( !cli_args.positional_arguments()[i].is_string() );
      test_expect( (string) cli_args.positional_arguments()[i] == vals[i] );
      test_expect( (bool) cli_args.positional_arguments()[i] == true );
    }
  }
}
 
void test()
{
  test1();
}Quelltext
Source code
/**
 * @package de.atwillys.cc.swl.util
 * @license BSD (simplified)
 * @author Stefan Wilhelm (stfwi)
 *
 * @file cli_args.hh
 * @ccflags
 * @ldflags
 * @platform linux, bsd, windows
 * @standard >= c++98
 *
 * -----------------------------------------------------------------------------
 *
 * *NIX/Linux style command line argument handling class template. Parses
 * options and positional arguments similar to `::getopt()`, except that
 * defining accepted options and retrieving their values is simpler.
 * Additionally ...
 *
 *   - You can define what data types the argument must have, and can directly
 *     retrieve the converted values with the type conversion operators (bool),
 *     (double) and (std::string).
 *
 *   - You can specify if the argument is
 *      - required    (error if it is omitted or no value is given),
 *      - optional    (default value if it is omitted, error if no value given),
 *      - flag        (can be omitted or set, but does not accept a value),
 *      - optinoal/val(default if omitted, other default if no value is given).
 *
 *   - It can implicitly handle errors (unknown options, missing options, type
 *     mismatch).
 *
 *   - It can generate a complete program help with synopsis, program description,
 *     detailed argument help etc (*NIX/Linux style help/man text). Implicit
 *     soft wrapping of text.
 *
 *   - It can allow you to ignore unknown option errors and retrieve the value
 *     of these options.
 *
 *   - It can return the list of positional arguments.
 *
 *   - You can retrieve the positions of all parsed arguments (e.g. in case
 *     that an option must occur before or after a positional argument or another
 *     options, etc).
 *
 *   - Options can occur multiple times (e.g. `-i file1 -i file2 -i file3`).
 *
 *   - You can easily retrieve numeric option values / positional argument
 *     values using the argument class typecast operators `(double)`, `(bool)`,
 *     also `(string)`, e.g. `cout << (bool)cli_args["verbose"] << endl;`.
 *
 *   - Allows value assignments with "=" for short options as well,
 *     e.g. `-f=file1`.
 *
 *   - Accepts commonly known separators like `--` for "end of options" or
 *     the values `-` for "use stdin/stdout"; `-0` .. `-9` can be defined as
 *     short options and will be differed from negative numbers.
 *
 * The class works without exceptions (except exceptions that can can come from
 * the STL containers).
 *
 * Examples how arguments can be specified from the command line:
 *
 * - $my_tar -x -v -z -f ~/file.tar.gz
 * - $my_tar -xvzf ~/file.tar.gz
 * - $my_tar -xvzf~/file.tar.gz
 * - $my_tar -xvzf=~/file.tar.gz
 * - $my_tar --extract -vz --file ~/file.tar.gz
 * - $my_tar --extract -vz --file=~/file.tar.gz
 *
 * - $my_prog --pi=3.1415 'positional 1' -- --no-option --neither-option
 *                                       ^^
 *                                       ``-- END OF OPTIONS SEPARATOR
 *
 * - $my_prog -9  # Short opt if it is defined, a positional negative number
 *
 * - $my_prog -i - -o - # Single "-" often also known as "use STDIN/STDOUT".
 *
 * -----------------------------------------------------------------------------
 *
 * Template:
 *
 *  - template <typename str_t> class basic_cli_args;
 *
 * As normally only one instance is required, latter is initialised as global
 * object `sw::cli_args` with the template specialisation std::string (typedef'ed
 * `sw::cli_args_t`). Around this class there are some helpers for argument
 * definitions and contents and iostream operations. They are all accessible
 * as typedefs in `sw::cli_args_t` and allow smart auto-completion in IDEs.
 *
 * --------------------------------------------------------------------------
 *
 * Essentially the usage is:
 *
 *  - Include this file, then you have the object sw::cli_args.
 *
 *  - In main(), pass `argc` and `argv` to the object using the operator ():
 *
 *      int main(int argc, char** argv) { cli_args(argc, argv); [...] }
 *
 *  - In whatever function/method you like, define the arguments you accept,
 *    as well using the `operator()` (this overload takes only string arguments).
 *
 *      cli_args(
 *        SHORT_OPT,                                  // e.g. "i"
 *        LONG_OPT,                                   // e.g. "input-file"
 *        DATA_TYPE,                                  // e.g. "s" for string
 *        DEFAULT_IF_ARGUMENT_IS_COMPLETELY_OMMITTED, // e.g. ""
 *        DEFAULT_IF_ARGUMENT_HAS_NO_VALUE,           // e.g. ""
 *        HELP_TEXT_FOR_THIS_ARGUMENT,                // e.g. "Input file to ..."
 *      );
 *
 *      // You can chain call this operator
 *      cli_args("i","in", "s")("o","out", "s")("v","verbose", "b", "", "1");
 *
 *  - Call `cli_args.parse();`
 *
 *  - Now you can get arguments and other information. Use the operator[] to
 *    query arguments. If an argument is not found an empty argument (`narg`)
 *    is returned. You can use `cli_args["string"]` to get options, and
 *    `cli_args[UINT]` to get arguments by their position.
 *
 *    - Get option values by short/long name:
 *
 *       string ifile = (string) cli_args["i"];
 *       string ofile = (string) cli_args["out"];
 *       bool verbose = (bool)   cli_args["v"];
 *
 *    - Get argument values by position:
 *
 *       string arg = (string) cli_args[0];
 *
 *    - Get information about options/arguments:
 *
 *      if(cli_args["i"].is_narg())     { Option -i is missing }
 *      if(cli_args["i"].is_ok())       { All ok with this option }
 *      if(cli_args["i"].is_bool())     { Its a boolean per definition }
 *      if(cli_args["i"].is_float())    { Its a floating point number  }
 *      if(cli_args[0].is_positional()) { First argument is positional }
 *      if(cli_args[0].value()=="")     { value() returns the string value }
 *
 *    - Getting errors:
 *
 *      if(cli_args.has_errors())   { Something missing, wrong data type etc. }
 *      cli_args_t::str_vect_t errors = cli_args.errors(); // Errors as string vector
 *
 *    - Getting other information:
 *
 *      string name = cli_args.program_name(); // Program (base)name
 *      cli_args_t::args_t positionals = cli_args.positional_arguments();
 *
 *    - Stream operations / object dump:
 *
 *      cerr << cli_args << endl;      // Dump object
 *      cerr << cli_args["i"] << endl; // Dump single argument object
 *      cerr << cli_args.positional_arguments() << endl; // Dump positionals
 *
 *      cli_args.help();               // Print defined options help
 *
 * --------------------------------------------------------------------------
 *
 * - Data type definitions (all given as string values)
 *
 *   - "s" : string
 *   - "b" : bool, nonzero numbers=true, literals like yes,no,off,ja,oui accepted.
 *   - "n" : number, means floating point number
 *   - "i" : number, integer
 *   - "f" : file
 *
 * - "Kind of option" definition: This is done with the two default values.
 *   Depending on weather a default is an empty string or not, the meaning
 *   changes:
 *
 *   - Empty "option omitted", empty "value omitted":
 *     This menas the option and its value is required, e.g. for an input file:
 *
 *     E.g. `cli_args("i", "inp", "s", "", "")`.
 *
 *     Needs one of: `-i file`, `-i=file`, `-ifile` ,`--inp=file`, `--inp file`.
 *
 *   - Empty "option omitted", set "value omitted":
 *     Means that this options is a flag. It does not accept a value, except
 *     the value is given with "=" and corresponds to the default value.
 *
 *     E.g. `("x", "extract", "b", "", "1")`.
 *
 *     Valid would be: `-x`, `-x=yes`, `--extract`, `--extract=true`, `-x=enabled`.
 *
 *   - Set "option omitted", empty "value omitted":
 *     That means this option optional and can be omitted, but if it is set
 *     it must have a value as well.
 *
 *     E.g. `("b", "bit-rate", "i", "115200", "")`.
 *
 *     If `-b` or `--bit-rate` is both missing, 115200 will be retrieved. Otherwise
 *     the argument must look like: `-b9600`, `-b 9600`, `-b=9600`, `--bit-rate=9600`,
 *     `--bit-rate 9600`.
 *
 *   - Set "option omitted", set "value omitted":
 *     Means the option can be omitted and it can be set like a flag without a
 *     value. Verbosity is an example for this:
 *
 *     E.g. `("v", "verbose", "i", "0", "1")`.
 *
 *     Now, if no `-v` or `--verbose` is set 0 is assumed. If the user says
 *     `-v` or `--verbose`, the missing value defaults to 1. If the user says
 *     `-v2` or `--verbose=2` you will read 2.
 *
 * -----------------------------------------------------------------------------
 *
 * - Things to keep in mind ...
 *
 *   - With optional arguments with optional values you can run into conditions
 *     where the parser cannot be sure if an argument is positional or the
 *     optional value of an option.E.g. `("v", "verbose", "i", "0", "1")`.
 *     Saying `-v 2` it could be "verbose level 2" or "verbose level 1 and
 *     positional 2". A similar problem occurrs when inlining options, like
 *     `tar -xvzf`. If `v` would a string data type it is not clear if the
 *     default string should be used or if the optional value is like `-v=zf`.
 *     For this reason it follows somwe rules to differ.
 *
 *     - `--opt VALUE` is NOT allowed like this. VALUE will be positional.
 *     - `--opt=VALUE` is ok
 *     - `-o VALUE`    again, VALUE will be positional.
 *     - `-o=VALUE`    is ok
 *     - `-opq`        (joined short opts): If `p` is a known flag or on data
 *                     type missmatch it will be like `-o -pq`, if not it is
 *                     like `-o=pq`.
 *
 *    In short: The parser tries to get what the user means, but simply prefer
 *    required arguments, flags and "optinoal with value" to avoid ambiguous
 *    argument interpretations.
 *
 *  - When defining numeric short options like `-0` to `-9` be aware that your
 *    program should not deal with numbers that can be negative.
 *
 *  - Positions in the argument list are not the equivalent to the argv[] indices
 *    that main() gets, because option value is not counted as position. As well,
 *    position 0 is not the program path, it is the first argument or option. The
 *    program name is retrieved with `program_name()`. E.g.
 *
 *      `$./myprog -f FILE POS1 POS2` will get:
 *        - program_name() = "myprog"
 *        - arguments()[0] = { pos:0, key: "-f", val: "FILE", def:"(its defin)"}
 *        - arguments()[1] = { pos:1, key: "", val: "POS1", def:"(positional)" }
 *        - arguments()[2] = { pos:2, key: "", val: "POS2", def:"(positional)" }
 *        - arguments()[>2]= (NARG)
 *
 *  - Don't define single character long options if you define a short option
 *    with the same name. E.g. '-v' and '--v'. Class can't differ them and
 *    will return the first that it sees in the argument list.
 *
 *  - If an argument is defined multiple times, the `operator[]` returns the
 *    first of them. Iterate through `arguments()` to get all of them.
 *    E.g. `$prog -i file1 -i file2 -i file3` --> `cli_args["i"]=="file1"`;
 *
 * -----------------------------------------------------------------------------
 *
 * Example source code:
 *
 *  int mmain(int argc, char** argv)
 *  {
 *    // The cli_args object of type class cli_args_t
 *    cli_args(argc, argv) // Assign argc and argv
 *    // Definitions
 *    ("i", "input-file" , "f", ""    , "" , "The input file to use.")
 *    ("o", "output-file", "f", ""    , "" , "The output file to save to.")
 *    ("v", "verbose",     "i", "0"   , "1", "Verbosity level.")
 *    ("r", "noise-ratio", "n", "0.1" , "" , "Expected noise ratio.")
 *    ("n", "normalize",   "b", ""    , "1", "Normalise output.")
 *    ("f", "filter",      "s", "s-T1", "" , "Filter to apply.")
 *    .allow_unknown_options(true)   // We allow options not in the list above
 *    .parse()                       // Parse, make argument list, check errors
 *    .handle_help_and_errors(       // Quick way to handle -h/--help and errors
 *      "Program to calculate something.\n\n"   // Help description, default: ""
 *      "- This text will be wrapped. This text will be wrapped This text "
 *        "will be wrapped. This text will be wrapped.",
 *      "v1.0, stfwi, 2010",          // Version, author, etc, default: ""
 *      "[c1 [c2 [...]]]",            // Positionals synopsis help, default: ""
 *      " 0: successful\n>0: error",  // Return values help, default: ""
 *      0,                            // Exit code (if it exits), default: 1
 *      cerr                          // ostream to use, default: std::cerr
 *    );
 *
 *    // Fetch arguments
 *    string ifile = (string) cli_args["i"];
 *    string ofile = (string) cli_args["o"];
 *    int loglevel = (int) cli_args["v"];
 *    double s2n   = (double) cli_args["r"];
 *    bool norm    = (bool) cli_args["n"];
 *    string filt  = (string) cli_args["f"];
 *    string unknown_a = (string) cli_args["a"]; // Retrieve unknown option -a
 *
 *    // Dump them
 *    cout << "input       : " << ifile << endl
 *         << "output      : " << ofile << endl
 *         << "log level   : " << loglevel << endl
 *         << "signal2noise: " << s2n << endl
 *         << "do normalise: " << norm << endl
 *         << "filter funct: " << filt << endl
 *         << "unknown -a  : " << unknown_a << endl
 *         << endl
 *         << "Positional arguments :" << endl
 *         << cli_args.positional_arguments()  << endl
 *        ;
 *
 *    // Dump the whole object, this shows a good overview what is defined,
 *    // what arguments have been parsed and what errors we have (debugging
 *    // feature).
 *    cout << cli_args << endl;
 *
 *    return 0;
 *  }
 *
 *
 * -----------------------------------------------------------------------------
 * +++ BSD license header +++
 * Copyright (c) 2008-2014, Stefan Wilhelm (stfwi, <cerbero s@atwilly s.de>)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met: (1) Redistributions
 * of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer. (2) Redistributions in binary form must reproduce
 * the above copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the distribution.
 * (3) Neither the name of atwillys.de nor the names of its contributors may be
 * used to endorse or promote products derived from this software without specific
 * prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
 * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * --------------------------------------------------------------------------
 */
#ifndef SW_CLI_ARGS_HH
#define SW_CLI_ARGS_HH
 
// <editor-fold desc="preprocessor" defaultstate="collapsed">
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <cmath>
#include <cctype>
#include <limits>
#include <algorithm>
 
#ifdef __MSC_VER
namespace { namespace std { template <typename T> bool isnan(T d) { return !!_isnan(d); } } }
#endif
#define NaN (std::numeric_limits<double>::quiet_NaN())
#ifdef DEBUG
#include <cassert>
#define dbg_assert(X) assert(X)
#define dbg(X) std::cerr << X << std::endl;
#else
#define dbg(X)
#define dbg_assert(X)
#endif
// </editor-fold>
 
// <editor-fold desc="aux functions" defaultstate="collapsed">
namespace sw { namespace detail {
 
template <typename str_t=std::string> class basic_cli_args;
 
/**
 * Auxiliary strict string-to-double conversion. Returns NaN on any inconsistancies.
 * @param const str_t &s
 * @return double
 */
template <typename str_t>
double basic_cli_args_s2d(const str_t &s)
{
  if(s.length()<1 || s.length() > 62) return NaN;
  double d;
  char cs[64];
  for(unsigned i=0; i<s.length(); ++i) cs[i] = (char)s[i];
  cs[s.length()] = '\0';
  char *cp[1] = {cs};
  d = ::strtod(cs, cp);
  if((*cp) != cs + s.length()) d = NaN;
  return d;
}
/**
 * String soft wrap auxiliary function
 * @param const str_t &s, unsigned width=80
 * @param const str_t &wrap_after_chars=".,:;/?!@\\-+=*%^"
 * @param const str_t &indent_ctl_chars="-*"
 * @return str_t
 */
template <typename str_t>
str_t basic_cli_args_softwrap(
    const str_t &s, unsigned width=80,
    const str_t &wrap_after_chars=".,;?!@\\",
    const str_t &indent_ctl_chars="-*")
{
  if(width < 8 || s.empty()) return s;
  str_t o;
 
  typename str_t::size_type sz=0, indent=0, i, j, end = s.length();
  const typename str_t::value_type nl = '\n';
  o.reserve(s.length() + s.length()/width + 1);
 
  // Indent of first line
  for(indent=0, i=0; i < end; ++i) {
    if(!::isspace(s[i]) && indent_ctl_chars.find_first_of(s[i]) == str_t::npos) break;
    ++indent;
  }
  indent %= width;
 
  for(i=0; i < end; ++i) {
    if(s[i]=='\r') continue;
    if(s[i]=='\n') {
      // Remove teiling spaces in the output line
      while(o.length() > 0 && std::isspace(o[o.length()-1]) &&
          o[o.length()-1] != nl) o.erase(o.length()-1);
      o.push_back(nl);
      // Indent for next line
      for(indent= 0, j= i+1; j < end; ++j) {
        if(!std::isspace(s[j]) && indent_ctl_chars.find_first_of(s[j])==str_t::npos) {
          break;
        }
        indent++;
      }
      indent %= width;
      sz = indent;
    } else {
      o.push_back(s[i]);
      if(++sz >= width) {
        // Find last space decrementing it and cropping o.
        int p = o.length()-1;
        int n = width;
        j = i;
        while(n >= 0 && p >= 0 && j>0 && !std::isspace(o[p]) &&
                wrap_after_chars.find_first_of(o[p]) == str_t::npos) {
          --j; --p; --n;
        }
        if(wrap_after_chars.find_first_of(o[p]) != str_t::npos) {
          ++p; ++j; ++n;
        };
        if(n > 0) { i = j; o.erase(p, str_t::npos); } // Soft wrap
        sz = indent;
        o.push_back(nl);
        o += str_t(indent, ' ');
      }
    }
  }
  while(o.length()>0 && std::isspace(o[o.length()-1])) {
    // Remove tailing whitesoaces
    o.erase(o.length()-1, str_t::npos);
  }
  return o;
}
 
// Will be undefined at end of file
#define ostream_t std::basic_ostream<typename str_t::value_type>
 
}}
// </editor-fold>
 
// <editor-fold desc="basic_cli_arg_def" defaultstate="collapsed">
namespace sw { namespace detail {
 
/**
 * Argument definition
 */
template <typename str_t>
class basic_cli_arg_def
{
public:
 
  typedef typename str_t::value_type char_t;
 
  /**
   * Standard constructor. Initialises the object so that it is equivalent to
   * ndef.
   */
  inline basic_cli_arg_def() : t_('!'), s_(), l_(), vm_(), vs_(), h_()
  { ; }
 
  /**
   * Detailed assignment constructor
   * @param const str_t & short_opt
   * @param const str_t & long_opt=""
   * @param str_t argument_type=""
   * @param const str_t & default_missing=""
   * @param const str_t & default_no_value=""
   * @param const str_t help=""
   */
  inline basic_cli_arg_def(
    const str_t & short_opt,
    const str_t & long_opt="",
    str_t argument_type="",
    const str_t & default_missing="",
    const str_t & default_no_value="",
    const str_t help=""
  ) : t_('b'), s_(short_opt), l_(long_opt), vm_(default_missing),
      vs_(default_no_value), h_(help)
  {
    if(argument_type.empty()) {
      t_ = 'b';
    } else {
      argument_type = " " + argument_type + " ";
      if(str_t(" b bool flag ").find(argument_type) != str_t::npos) {
        t_ = 'b';
      } else if(str_t(" s string ").find(argument_type) != str_t::npos) {
        t_ = 's';
      } else if(str_t(" f file ").find(argument_type) != str_t::npos) {
        t_ = 'f';
      } else if(str_t(" n number float double ").find(argument_type) != str_t::npos) {
        t_ = 'n';
      } else if(str_t(" i int integer ").find(argument_type) != str_t::npos) {
        t_ = 'i';
      } else {
        t_ = '!';
      }
    }
  }
 
  virtual ~basic_cli_arg_def()
  { ; }
 
  /**
   * Returns the argument type specification character, one of
   * 's','n','i','b','f','!'.
   * @return char_t
   */
  inline char_t type() const throw()
  { return t_; }
 
  /**
   * Returns the short option of the definition, "" if not set.
   * @return const str_t &
   */
  inline const str_t & short_opt() const
  { return s_; }
 
  /**
   * Returns the long option of the definition, "" if not set.
   * @return const str_t &
   */
  inline const str_t & long_opt() const
  { return l_; }
 
  /**
   * Returns the value that shall be used if the option is omitted, "" if not
   * set.
   * @return const str_t &
   */
  inline const str_t & value_if_omitted() const
  { return vm_; }
 
  /**
   * Returns the value that shall be used if the option set but has no value,
   * "" if not set.
   * @return const str_t &
   */
  inline const str_t & value_if_no_value() const
  { return vs_; }
 
  /**
   * Returns the help text for this option.
   * @return const str_t &
   */
  inline const str_t & help() const
  { return h_; }
 
  /**
   * Returns a string representation for the option type.
   * @return str_t
   */
  inline str_t type_name() const
  { return (
      t_=='b' ? "boolean" : (
      t_=='s' ? "string" : (
      t_=='f' ? "file" : (
      t_=='n' ? "number" : (
      t_=='i' ? "integer" : (
      "(INVALID TYPE SPECIFIED)"
    ))))));
  }
 
  /**
   * True if this option is undefined/unknown
   * @return bool
   */
  inline bool is_ndef() const
  { return s_.empty() && l_.empty(); }
 
  /**
   * True if the option cannot be omitted and requires a value.
   * [ empty "omitted" default, empty "set" default ]
   * @return bool
   */
  inline bool is_required() const
  { return vm_.empty() && vs_.empty(); }
 
  /**
   * True if this option can be omitted, but if it is set there must be a given
   * value as well.
   * [ existing "omitted" default, empty "set" default ]
   * @return bool
   */
  inline bool is_value_required() const
  { return !vm_.empty() && vs_.empty(); }
 
  /**
   * True if this option can be omitted, and the value is optional with a
   * defined default.
   * [ existing "omitted" default, existing "set" default ]
   * @return bool
   */
  inline bool is_optional() const
  { return !vm_.empty() && !vs_.empty(); }
 
  /**
   * True if the option can be omitted and does not accept accept a value.
   * [ empty "omitted" default, existing "set" default ]
   * @return bool
   */
  inline bool is_flag() const
  { return vm_.empty() && !vs_.empty(); }
 
 
  /**
   * Returns true if the definition of type string or file, which is implicitly string, too.
   * @return bool
   */
  inline bool is_string() const
  { return t_ == 's' || t_ == 'f'; }
 
  /**
   * Returns true if the definition is of type file.
   * @return bool
   */
  inline bool is_file() const
  { return t_ == 'f'; }
 
  /**
   * Returns true if the definition of type number.
   * @return bool
   */
  inline bool is_float() const
  { return t_ == 'n'; }
 
  /**
   * Returns true if the definition of type integer.
   * @return bool
   */
  inline bool is_int() const
  { return t_ == 'i'; }
 
  /**
   * Returns true if the definition is boolean.
   * @return bool
   */
  inline bool is_bool() const
  { return t_ == 'b'; }
 
  /**
   * True if short opt matches OR long opt matches
   * @param def
   * @return bool
   */
  inline bool operator == (const basic_cli_arg_def& d) const
  { return d.s_==s_ || d.l_==l_; }
 
  /**
   * False if short opt matches OR long opt matches
   * @param def
   * @return bool
   */
  inline bool operator != (const basic_cli_arg_def& d) const
  { return d.s_!=s_ && d.l_!=l_; }
 
public:
 
  /**
   * ostream / dump settings
   */
  static unsigned line_width, help_indent, typehint_indent, line_indent;
 
  /**
   * "Undefined option" object.
   * @var static const basic_cli_arg_def
   */
  static const basic_cli_arg_def ndef;
 
protected:
 
  char_t t_;       // type
  str_t s_, l_;    // short, long,
  str_t vm_, vs_;  // value (if missing), value (if set without value)
  str_t h_;        // help
};
 
/**
 * Static variables
 */
template <typename str_t>
unsigned basic_cli_arg_def<str_t>::line_indent = 2;
 
template <typename str_t>
unsigned basic_cli_arg_def<str_t>::line_width = 80;
 
template <typename str_t>
unsigned basic_cli_arg_def<str_t>::help_indent = 3;
 
template <typename str_t>
unsigned basic_cli_arg_def<str_t>::typehint_indent = 4;
 
template <typename str_t>
const basic_cli_arg_def<str_t> basic_cli_arg_def<str_t>::ndef = basic_cli_arg_def<str_t>();
 
/**
 * ostream for basic_cli_arg_def< >
 * @param ostream& os
 * @param basic_cli_arg_def<str_t> a
 * @return ostream& os
 */
template <typename str_t>
inline ostream_t& operator <<
    (ostream_t& os, basic_cli_arg_def<str_t> a)
{
  if(a.is_ndef()) { os << "(NDEF)"; return os; }
  if(a.short_opt().empty() && a.long_opt().empty()) return os;
  str_t s;
  unsigned line_indent = basic_cli_arg_def<str_t>::line_indent;
  unsigned hint_indent = basic_cli_arg_def<str_t>::typehint_indent;
  unsigned help_indent = basic_cli_arg_def<str_t>::help_indent;
  unsigned line_width  = basic_cli_arg_def<str_t>::line_width;
 
  s = str_t(line_indent, ' ');
  if(!a.short_opt().empty()) {
    s += str_t("-") + a.short_opt() + (a.long_opt().empty() ? "" : ", ");
  }
  if(!a.long_opt().empty()) s += str_t("--") + a.long_opt();
  if(s.length() < hint_indent) s += str_t(hint_indent-s.length(), ' ');
  os << s << " (";
  if(a.is_required()) {
    os << a.type_name() << ", required";
  } else if(a.is_flag()) {
    os << "flag";
  } else if(a.is_value_required()) {
    os << "optional " << a.type_name();
  } else {
    os << "optional " << a.type_name() <<  ", default: "
       << ((a.type()=='s' || a.type()=='f') ? "'":"")
       << a.value_if_omitted()
       << ((a.type()=='s' || a.type()=='f') ? "'":"");
  }
  os << ")";
  if(!a.help().empty()) {
    os  << std::endl << std::endl;
    std::vector<str_t> lines;
    typename str_t::size_type p;
    str_t hlp = a.help();
    if((p=hlp.find_last_not_of("\r\t\n ")) != str_t::npos) hlp = hlp.substr(0, p+1);
    while(!hlp.empty()) {
      if((p=hlp.find_first_of("\n")) == str_t::npos) {
        lines.push_back(hlp);
        hlp = "";
        break;
      } else {
        str_t s = hlp.substr(0, p);
        hlp = hlp.substr(p+1); // cannot be last char, see above
        if((p=s.find_last_not_of("\r\t\n ")) != str_t::npos) s = s.substr(0, p+1);
        lines.push_back(s);
      }
    }
    for(unsigned k=0; k<lines.size(); k++) {
      os << basic_cli_args_softwrap(str_t(line_indent+help_indent, ' ') +
            lines[k],line_width) << std::endl;
    }
  }
  return os;
}
 
}}
// </editor-fold>
 
// <editor-fold desc="basic_cli_arg_defs" defaultstate="collapsed">
namespace sw { namespace detail {
 
/**
 * List class for definitions
 */
template <typename str_t>
class basic_cli_arg_defs : public std::vector<basic_cli_arg_def<str_t> >
{
public:
 
  typedef basic_cli_arg_def<str_t> def_t;
  typedef basic_cli_arg_defs my_t;
  typedef typename std::vector<basic_cli_arg_def<str_t> > base_t;
  typedef typename base_t::iterator iterator;
  typedef typename base_t::const_iterator const_iterator;
  typedef typename base_t::size_type size_type;
 
public:
 
  basic_cli_arg_defs() : base_t()
  { ; }
 
  explicit basic_cli_arg_defs(const base_t &v) : base_t(v)
  { ; }
 
  basic_cli_arg_defs(const my_t &v) : base_t(v)
  { ; }
 
  virtual ~basic_cli_arg_defs()
  { ; }
 
public:
 
  /**
   * Finds a short or long option definition by its key. No leading "-" / "--".
   * @param const str_t& key
   * @return const def_t&
   */
  inline const def_t& operator [] (const str_t& key) const
  { return find(key); }
 
  /**
   * Returns definition by index or ndef if index exceeds size.
   * @param unsigned i
   * @return const def_t&
   */
  inline const def_t& operator [] (unsigned i) const
  { return i>=base_t::size() ? def_t::ndef : base_t::operator[](i); }
 
  /**
   * Finds a short or long option definition by its key. No leading "-" / "--".
   * @param const str_t& key
   * @return const def_t&
   */
  inline const def_t& find(const str_t& key) const
  {
    for(const_iterator it=this->begin(); it!=this->end(); ++it)
      if((*it).short_opt() == key || (*it).long_opt() == key) return *it;
    return def_t::ndef;
  }
 
  /**
   * Finds a short option definition by its key. No leading "-".
   * @param const str_t& key
   * @return const def_t&
   */
  inline const def_t& find_short_opt(const str_t& key) const
  {
    for(const_iterator it=this->begin(); it!=this->end(); ++it)
      if((*it).short_opt() == key) return *it;
    return def_t::ndef;
  }
 
  /**
   * Finds a long option definition by its key. No leading "--".
   * @param const str_t& key
   * @return const def_t&
   */
  inline const def_t& find_long_opt(const str_t& key) const
  {
    for(const_iterator it=this->begin(); it!=this->end(); ++it)
      if((*it).long_opt() == key) return *it;
    return def_t::ndef;
  }
 
};
 
template <typename str_t>
ostream_t& operator << (ostream_t& os, const basic_cli_arg_defs<str_t>& v)
{
  typename basic_cli_arg_defs<str_t>::const_iterator it=v.begin();
  while(it != v.end()) {
    os << "  " << *it;
    if(++it!=v.end()) os << std::endl;
  }
  return os;
}
}}
// </editor-fold>
 
// <editor-fold desc="basic_cli_arg" defaultstate="collapsed">
namespace sw { namespace detail {
 
/**
 * Argument
 */
template <typename str_t>
class basic_cli_arg
{
public:
 
  typedef basic_cli_arg_def<str_t> def_t;
 
public:
 
  /**
   * Standard constructor. Creates the object so that it is equivalent to
   * narg.
   */
  inline basic_cli_arg() : def_(0), p_(npos), k_(), v_(), nv_(NaN), ok_(false)
  { ; }
 
  /**
   * Detailed assignment with existing definition
   * @param int p
   * @param str_t key
   * @param str_t value
   * @param const def_t &def
   * @param bool ok=false
   */
  inline basic_cli_arg(int p, str_t key, str_t value, const def_t &def) :
    def_(&def), p_(p), k_(key), v_(value), nv_(NaN), ok_(false)
  { assign(value); }
 
  /**
   * Detailed assignment without existing definition (for unknown options)
   * @param int p
   * @param str_t key
   * @param str_t value
   */
  inline basic_cli_arg(int p, str_t key, str_t value) :
    def_(0), p_(p), k_(key), v_(value), nv_(NaN), ok_(false)
  { assign(value); }
 
  virtual ~basic_cli_arg()
  { ; }
 
  /**
   * Assignment, identical copy
   * @param const basic_cli_arg& a
   */
  inline basic_cli_arg& operator= (const basic_cli_arg& a)
  { def_=a.def_; p_=a.p_; k_=a.k_; v_=a.v_; ok_=a.ok_; nv_=a.nv_; return *this; }
 
public:
 
  /**
   * Returns the position in the program argument list where this option and
   * its value has been retrieved from.
   * @return unsigned
   */
  inline int position() const
  { return p_; }
 
  /**
   * Returns the key, t.m. the exact option name string from the command line
   * without leading "-". This key is empty for positional arguments.
   * @return const str_t &
   */
  inline const str_t & key() const
  { return k_; }
 
  /**
   * Returns the string value of this option or positional argument.
   * @return const str_t &
   */
  inline const str_t & value() const
  { return v_; }
 
  /**
   * Returns the definition that was determined while parsing this argument or
   * def_t::ndef constant if no definition was found ("Unknown option").
   * @return const def_t &
   */
  inline const def_t & definition() const
  { return (def_!=0) ? (*def_) : def_t::ndef; }
 
  /**
   * True if this argument is positional
   * @return bool
   */
  inline bool is_positional() const
  { return (!def_ || def_->is_ndef()) && k_.empty() && p_>=0; }
 
  /**
   * True if this argument does not belong to a defined option definition.
   * ("Unknown option").
   * @return bool
   */
  inline bool is_undefined_option() const
  { return (!def_ || def_->is_ndef()) && !k_.empty() && p_>=0; }
 
  /**
   * True if this argument is narg, means not found in the given program
   * argument list.
   * @return bool
   */
  inline bool is_narg() const
  { return p_<0 && v_.empty(); } // && (!def_||def_->is_ndef()) && k_.empty() &&
 
  /**
   * True if everything seems to be ok with this option: Definition exists, and
   * the value that was parsed meets the requirements for this definition.
   * @return bool
   */
  inline bool is_ok() const
  { return ok_; }
 
  /**
   * Returns true if the definition says this argument is of type string OR if
   * there is no definition for this key/option. Type file is implicitly type
   * string as well.
   * @return bool
   */
  inline bool is_string() const
  { return def_ && (def_->type() == 's' || def_->type() == 'f'); }
 
  /**
   * Returns true if the definition says this argument is of type file
   * @return bool
   */
  inline bool is_file() const
  { return def_ && def_->type() == 'f'; }
 
  /**
   * Returns true if the definition exists and says this argument is a number.
   * @return bool
   */
  inline bool is_float() const
  { return def_ && def_->type() == 'n'; }
 
  /**
   * Returns true if the definition exists and says this argument is an integer.
   * @return bool
   */
  inline bool is_int() const
  { return def_ && def_->type() == 'i'; }
 
  /**
   * Returns true if the definition exists and says this argument boolean.
   * @return bool
   */
  inline bool is_bool() const
  { return def_ && def_->type() == 'b'; }
 
public:
 
  /**
   * Returns the numeric value (effectively int if type is int, {0,1} if bool,
   * or a floating point number if the definition type is "number".
   * @return double
   */
  inline operator double () const
  { return nv_; }
 
  /**
   * Returns the string value if the argument. Independent of the type, this
   * always reads the unmodified text how it appears in the given argument list.
   * @return str_t
   */
  inline operator str_t() const
  { return v_; }
 
  /**
   * Boolean comparison, return: NaN and 0 := false, other values := true.
   * @param bool b
   */
  inline bool operator == (bool b) const
  { return (!std::isnan(nv_) && nv_!=0) == b; }
 
  /**
   * Boolean comparison, return: NaN and 0 := true, other values := false.
   * @param bool b
   */
  inline bool operator != (bool b) const
  { return !(operator==(b)); }
 
  /**
   * Negation
   * @return bool
   */
  inline bool operator ! () const
  { return (operator==(false)); }
 
public:
 
  /**
   * Assigns the value and derives the numeric/boolean value depending on the
   * type of (the existing) definition.
   * @param const str_t &val
   * @return void
   */
  void assign(const str_t &val)
  {
    nv_ = basic_cli_args_s2d(v_);
    ok_ = false;
    if(p_<0) p_ = npos;
    if(!def_) def_ = &def_t::ndef;
    if(p_<0 && k_.empty()) {
      // Either narg or negative positional --> not ok, set to narg
      v_ = narg.value();
    } else if(k_.empty()) {
      // Positional
      v_ = val;
      ok_ = true;
    } else if(def_->is_ndef() || def_->type() == 's' || def_->type() == 'f') {
      // String, applies as well to unknown options
      v_ = val;
      ok_ = !def_->is_ndef();
    } else {
      if(val.empty() && def_->is_flag()) {
        // The default for unset flags is 0 and ""
        v_ = "";
        nv_ = 0;
      }
      ok_ = !std::isnan(nv_);
      switch(def_->type()) {
        case 'b':
          if(!ok_) {
            str_t s = val;
            std::transform(s.begin(), s.end(), s.begin(), ::tolower);
            if(s.empty()) {
            } else if(str_t(",true,yes,on,enabled,an,ja,oui,si,tak,")
                .find(str_t(",")+s+",") != str_t::npos) {
              nv_ = 1; ok_ = true;
            } else if(str_t(",false,no,off,disabled,aus,nein,non,nie,geen,")
                .find(str_t(",")+s+",") != str_t::npos) {
              nv_ = 0; ok_ = true;
            }
            if(!ok_ && def_->is_flag()) {
              nv_ = 0; ok_ = true;
            }
          }
          break;
        case 'n':
           break;
        case 'i':
          if(ok_) {
            ok_ = std::abs(nv_-((long)nv_)) <= std::numeric_limits<double>::epsilon();
          } else {
            nv_ = (int) narg; // Precaution for type conversion problems with NaN
          }
          break;
        default:
          // Invalid type
          ok_ = false;
          nv_ = NaN;
      }
    }
  }
 
public:
 
  /**
   * Represents a nonexisting argument.
   * @return const basic_cli_arg&
   */
  static const basic_cli_arg narg;
 
  /**
   * Position that represents a nonexisting argument.
   * @return const basic_cli_arg&
   */
  static const int npos;
 
protected:
 
  const def_t *def_;  // Definition
  int p_;             // Position
  str_t k_;           // Option seen (short, long or other if not in def list)
  str_t v_;           // Value
  double nv_;         // Numeric value (inclusively bool)
  bool ok_;           // Argument valid?
};
 
/**
 * No argument
 */
template <typename str_t>
const basic_cli_arg<str_t> basic_cli_arg<str_t>::narg = basic_cli_arg<str_t>();
 
template <typename str_t>
const int basic_cli_arg<str_t>::npos = -1;
 
/**
 * ostream <<
 * @param ostream&
 * @param const basic_cli_arg<str_t>& a
 * @return ostream&
 */
template <typename str_t>
inline ostream_t& operator << (ostream_t& os, const basic_cli_arg<str_t>& a)
{
  if(a.is_narg()) {
    os << "(NARG)";
  } else {
    os << "{ pos: \"" << a.position() << "\""
       << ", key: \"" << a.key() << "\""
       << ", val: \"" << a.value() << "\""
       << ", def: \"";
    if(a.is_positional()) {
      os << "(positional)";
    } else if(a.is_undefined_option()) {
      os << "(undefined option)";
    } else if(a.definition().is_ndef()) {
      os << "(ndef)";
    } else {
      if(!a.definition().short_opt().empty()) {
        os << "-" << a.definition().short_opt();
      }
      if(!a.definition().short_opt().empty() && !a.definition().long_opt().empty()) {
        os << "/";
      }
      if(!a.definition().long_opt().empty()) {
        os << "--" << a.definition().long_opt();
      }
    }
    os << "\", ok: " << (a.is_ok() ? "true" : "false");
    os << " }";
  }
  return os;
}
 
}}
// </editor-fold>
 
// <editor-fold desc="basic_cli_arg_vector" defaultstate="collapsed">
namespace sw { namespace detail {
 
/**
 * List class for parsed arguments
 */
template <typename str_t>
class basic_cli_arg_vector : public std::vector<basic_cli_arg<str_t> >
{
public:
 
  typedef typename std::vector<basic_cli_arg<str_t> > base_t;
  typedef basic_cli_arg_vector my_t;
  typedef basic_cli_arg_def<str_t> def_t;
  typedef basic_cli_arg<str_t> arg_t;
  typedef typename base_t::iterator iterator;
  typedef typename base_t::const_iterator const_iterator;
  typedef typename base_t::size_type size_type;
 
public:
 
  basic_cli_arg_vector() : base_t()
  { ; }
 
  explicit basic_cli_arg_vector(const base_t &v) : base_t(v)
  { ; }
 
  basic_cli_arg_vector(const my_t &v) : base_t(v)
  { ; }
 
  virtual ~basic_cli_arg_vector()
  { ; }
 
public:
 
  /**
   * Returns the first option matching the given `key` or arg_t::narg if
   * no argument was found.
   * @param const str_t & key
   * @return arg_t
   */
  const arg_t& find(const str_t& key) const
  {
    if(key.empty()) return arg_t::narg;
    for(const_iterator it=this->begin(); it!=this->end(); ++it) {
      if((*it).definition().short_opt() == key) return *it; // Defined short
      if((*it).definition().long_opt() == key) return *it; // Defined long
      if((*it).key() == key) return (*it); // No definition but specified in argv
    }
    return arg_t::narg;
  }
 
  /**
   * Returns the first option matching the given `key` or arg_t::narg if
   * no argument was found.
   * @param const str_t & sopt
   * @param const str_t & lopt
   * @return arg_t
   */
  const arg_t& find(const str_t& sopt, const str_t& lopt) const
  {
    if(sopt.empty() && lopt.empty()) return arg_t::narg;
    for(const_iterator it=this->begin(); it!=this->end(); ++it) {
      if(it->definition().short_opt() == sopt && !sopt.empty()) return *it; // Defined short
      if(it->definition().long_opt() == lopt && !lopt.empty()) return *it; // Defined long
      if((!it->key().empty()) && (it->key()==sopt || it->key()==lopt)) {
        return (*it); // No definition but specified in argv
      }
    }
    return arg_t::narg;
  }
 
  /**
   * Returns the first option matching the given `key` or arg_t::narg if
   * no argument was found.
   * @param const str_t & key
   * @return arg_t
   */
  const arg_t& operator [] (const str_t& key) const
  { return find(key); }
 
  /**
   * Returns option with the index `index` or arg_t::narg if not existing.
   * @param unsigned index
   * @return arg_t
   */
  const arg_t& operator [] (unsigned index) const
  {
    if(index >= base_t::size()) return arg_t::narg;
    const arg_t& r = base_t::at(index);
    // position() < 0 === added default value
    return r.position() >= 0 ? r : arg_t::narg;
  }
};
 
/**
 * ostream <<
 * @param ostream& os
 * @param const basic_cli_arg_vector<str_t>& v
 * @return ostream&
 */
template <typename str_t>
inline ostream_t& operator << (ostream_t& os, const basic_cli_arg_vector<str_t>& v)
{
  os << "[" << std::endl;
  typename basic_cli_arg_vector<str_t>::const_iterator it=v.begin();
  while(it != v.end()) {
    os << "  " << *it;
    os << (++it==v.end() ? "" : ",") << std::endl;
  }
  os << "]" << std::endl;
  return os;
}
}}
// </editor-fold>
 
// <editor-fold desc="basic_cli_args" defaultstate="collapsed">
namespace sw { namespace detail {
 
/**
 * CLI Args Main Class
 */
template <typename str_t>
class basic_cli_args
{
public:
 
  /**
   * Text character type
   */
  typedef typename str_t::value_type char_t;
 
  /**
   * String vector type
   */
  typedef typename std::vector<str_t> str_vect_t;
 
  /**
   * Argument definition class type
   */
  typedef basic_cli_arg_def<str_t> def_t;
 
  /**
   * Argument definition list class type
   */
  typedef basic_cli_arg_defs<str_t> defs_t;
 
  /**
   * Argument class type
   */
  typedef basic_cli_arg<str_t> arg_t;
 
  /**
   * Argument list class type
   */
  typedef basic_cli_arg_vector<str_t> args_t;
 
public:
 
  /**
   * Reference to the definition object representing "definition not
   * found", "nonexisting definition" etc.
   * @return const def_t&
   */
  static const def_t& ndef;
 
  /**
   * Reference to the argument object representing "this is no argument",
   * "argument not found" etc.
   * @return arg_t&
   */
  static const arg_t& narg;
 
  /**
   * The argument position that represents "no position"
   * @return int
   */
  static const int npos;
 
public:
 
  /**
   * Standard constructor
   */
  inline basic_cli_args() : allow_unknown_opts_(false)
  { ; }
 
  /**
   * Constructor / assign argc/argv
   * @param int argc
   * @param char** argv
   */
  inline basic_cli_args(int argc, char** argv) : allow_unknown_opts_(false)
  { assign(argc, (const char**)argv); }
 
  /**
   * Constructor / assign argc/argv
   * @param int argc
   * @param const char** argv
   */
  inline basic_cli_args(int argc, const char** argv) : allow_unknown_opts_(false)
  { assign(argc, argv); }
 
  virtual ~basic_cli_args()
  { ; }
 
public:
 
  /**
   * Returns the definitions
   * @return const defs_t&
   */
  inline const defs_t& definitions() const
  { return defs_; }
 
  /**
   * Returns the basename of the program.
   * @return const str_t&
   */
  inline const str_t& program_name() const
  { return app_; }
 
  /**
   * Returns the parsed arguments including internally added optional options
   * that were omitted.
   * @return const args_t&
   */
  inline const args_t& arguments() const
  { return args_; }
 
  /**
   * Returns the list of errors
   * @return str_vect_t
   */
  inline const str_vect_t errors() const
  { return errs_; }
 
  /**
   * (Getter) True of unknown options don't raise errors.
   * @return bool
   */
  inline bool allow_unknown_options() const throw()
  { return allow_unknown_opts_; }
 
  /**
   * (Setter) Set to true of unknown options shall not raise errors. These
   * options can also be fetched using operator[](str_t) and contain set values,
   * but `is_ok()` will return false as type the definition is unclear.
   * THIS METHOD MUST BE CALLED BEFORE `parse()`.
   * @param bool allow
   * @return basic_cli_args&
   */
  inline basic_cli_args& allow_unknown_options(bool allow) throw()
  { allow_unknown_opts_ = allow; return *this; }
 
  /**
   * Returns true if there are errors.
   * @return bool
   */
  inline bool has_errors() const
  { return errs_.size() > 0; }
 
  /**
   * Returns the list of given positional arguments.
   * @return args_t
   */
  inline args_t positional_arguments() const
  {
    args_t r;
    for(typename args_t::const_iterator it=args_.begin(); it!=args_.end(); ++it) {
      if(it->is_positional()) r.push_back(*it);
    }
    return r;
  }
 
public:
 
  /**
   * Resets the object
   * @return basic_cli_args&
   */
  inline basic_cli_args& clear()
  { app_=""; argv_.clear(); defs_.clear(); args_.clear(); errs_.clear(); }
 
public:
 
  /**
   * Transfers the main() arguments into the corresponding instance variables.
   * @param int argc
   * @param const char** argv
   * @return basic_cli_args&
   */
  basic_cli_args& assign(int argc, const char** argv)
  {
    argv_.clear(); app_.clear();
    if(argc<0 || !argv || !argv[0]) return *this;
    app_ = argv[0];
    while(!app_.empty() && ((app_[app_.length()-1]=='/') || (app_[app_.length()-1]=='\\'))) {
      app_.resize(app_.length()-1);
    }
    typename str_t::size_type p =app_.find_last_of("\\/");
    if(p != str_t::npos) app_ = app_.substr(p+1);
    for(int i=1; i<argc && argv[i]; ++i) argv_.push_back(argv[i]);
    return *this;
  }
 
  /**
   * Transfers the main() arguments into the corresponding instance variables.
   * @param int argc
   * @param const char** argv
   * @return basic_cli_args&
   */
  inline basic_cli_args& operator () (int argc, char** argv)
  { return assign(argc, (const char**)argv); }
 
   /**
   * Transfers the main() arguments into the corresponding instance variables.
   * @param int argc
   * @param const char** argv
   * @return basic_cli_args&
   */
  inline basic_cli_args& operator () (int argc, const char** argv)
  { return assign(argc, argv); }
 
  /**
   * Returns a parsed argument or def_t::ndef if not found.
   * @param const str_t & key
   * @return const arg_t&
   */
  inline const arg_t& operator [] (const str_t& key) const
  { return args_.find(key); }
 
  /**
   * Returns the argument with the index `index` or narg if exceeded.
   * @param unsigned index
   * @return const arg_t&
   */
  inline const arg_t& operator [] (unsigned index) const
  { return index>=args_.size() ? arg_t::narg : args_[index]; }
 
  /**
   * Add an argument definition
   * @param const str_t& short_opt
   * @param const str_t& long_opt = ""
   * @param const str_t& argument_type=""
   * @param const str_t & default_if_missing="",
   * @param const str_t & default_if_no_value="",
   * @param str_t help = ""
   * @return basic_cli_args&
   */
  inline basic_cli_args& operator () (
    const str_t& short_opt,
    const str_t& long_opt="",
    const str_t& argument_type="",
    const str_t & default_if_missing="",
    const str_t & default_if_no_value="",
    const str_t& help=""
  )
  {
    defs_.push_back(def_t(short_opt, long_opt, argument_type, default_if_missing,
        default_if_no_value, help)); return *this;
  }
 
  /**
   * Parses the given arguments using the specified definitions
   * @return basic_cli_args&
   */
  basic_cli_args& parse() {
    // (1) Parse to args_
    typename str_vect_t::const_iterator it = argv_.begin();
    typename str_t::size_type p;
    int pos = 0;
    str_t key, val;
    while(it != argv_.end()) {
      str_t arg = *it; ++it;
      key = val = "";
      const def_t* def = &def_t::ndef;
      if(arg.empty() || arg.find('-') != 0 || arg.find("---") == 0 || arg=="-"
          || arg=="--=" || arg=="-=") {
        // Means: empty ('' / "") input given or something that cannot be an option
        // ("-" etc).
        // This is positional then.
        args_.push_back(arg_t(pos++, "", arg));
        continue;
      } else if(arg == "--") {
        // No more options, please
        while(it != argv_.end()) {
          args_.push_back(arg_t(pos++, "", *it)); ++it;
        }
        break;
      } else if(arg.find("--") == 0) {
        // Long option, non empty key
        arg = arg.substr(2);
        if(((p=arg.find('=')) != str_t::npos)) {
          // --key='val', --key=
          key = arg.substr(0, p);
          val = p<arg.length()-1 ? arg.substr(p+1) : "";
        } else {
          // --key OR --key 'val'
          key = arg;
          val = (it!=argv_.end()) ? (*it) : "";
        }
        if((def=&defs_.find_long_opt(key))->is_ndef()) {
          // No definition for this, but it really looks like an option, we can't
          // take it as positional argument.
          args_.push_back(arg_t(pos++, key, "", def_t::ndef));
        } else if(def->is_value_required() || def->is_required()) {
          // No "default if missing" defined --> required, the argv[i+1] must
          // be the value if no "=" contained in the actual arg. (Even if this
          // argv[i+1] starts with an "-", or "--").
          args_.push_back(arg_t(pos++, key, val, *def));
          if(p==str_t::npos && it!=argv_.end()) ++it; // Forward for --key 'val'
        } else if(p!=str_t::npos) {
          // Defined option, explicitly set with "=", if the string after "=" is
          // empty it is assumed it should be empty and no default value is used.
          // Flag checked later, optional value covered here.
          args_.push_back(arg_t(pos++, key, val, *def));
        } else {
          // Last non ambiguous case left: No "=" and a "default if no value"
          // defined.
          args_.push_back(arg_t(pos++, key, def->value_if_no_value(), *def)); // --key
        }
      } else if(std::isdigit(arg[1])) {
        // Numbers are positional arguments, except there is a short flag option
        // like -9 or -0
        if(arg.length()==2 && (def=&defs_.find_short_opt(str_t(1,arg[1])))->is_flag()) {
          args_.push_back(arg_t(pos++, str_t(1, arg[1]), def->value_if_no_value(), *def));
        } else {
          // Positional, double conversion will be done during argument object
          // creation
          args_.push_back(arg_t(pos++, "", arg));
        }
      } else {
        // Short option, non empty key, means:
        // Multi character options without explicit "=" value (at least not
        // yet):
        // E.g. -abck, -k val, -kVAL, -abckVAL, , -abck=VAL
        // Where abc are flags
        // Rules:
        // - The first option with accepts a value uses the remainder of the
        //   argument as value.
        // - All other options must be flags (missing and no value default
        //   defined).
        // - If a value is required and the option is the last option, the
        //   next argv value is used as value.
        // - All characters after "=" are used as value
        arg = arg.substr(1);
        while(!arg.empty()) {
          str_t key = arg.substr(0,1);
          arg = arg.length()==1 ? "" : arg.substr(1);
          if(key == "=") { arg.clear(); break; } // Catch potential trouble
          def = &defs_.find_short_opt(key);
          if(arg.length()>0 && arg[0] == '=') {
            // -k=value, all chars after "=" belong to the value, '-k=' is
            // interpreted as given empty value.
            // Note: Flag checks are done later
            val = arg.length()<2 ? "" : arg.substr(1);
            arg.clear(); // next arg
            args_.push_back(arg_t(pos, key, val, *def)); // Can be NDEF as well
          } else if(arg.length()==0) {
            // Single character option (or character left) without explicit
            // "=" value: E.g. -k, -k val
            val = (it!=argv_.end()) ? (*it) : ""; // (potential value)
            arg.clear(); // next arg
            if(def->is_ndef()) {
              // Unknown option. Next argv is NOT a value.
              args_.push_back(arg_t(pos, key, "", *def));
            } else if(def->is_value_required() || def->is_required()) {
              // Single option character, next argv must be a value
              // E.g. -k 'VAL'
              args_.push_back(arg_t(pos, key, val, *def));
              if(it!=argv_.end()) ++it;
            } else {
              // Value is optional/flag, means here it must be interpreted flag-like.
              // (Next argv value could not be differed from a positional parameter).
              args_.push_back(arg_t(pos, key, def->value_if_no_value(), *def));
            }
          } else if(def->is_ndef()) {
            // Undefined option
            args_.push_back(arg_t(pos, key, arg, *def));
            arg.clear();
          } else if(def->is_required() || def->is_value_required()) {
            // More chars left in arg, actual arg requires a value.
            // The rest of the string is the value
            args_.push_back(arg_t(pos, key, arg, *def));
            arg.clear();
          } else if(def->is_flag()) {
            // Flag set: use "no value" default. If omitted, the flag would
            // read an empty value.
            args_.push_back(arg_t(pos, key, def->value_if_no_value(), *def));
          } else {
            // Optional option with optional value. "Smart detection check"
            // needed, more simple: If the next char is not a known flag, use the
            // rest as value, otherwise use the default.
            dbg_assert(def->is_optional());
            const def_t& next_arg_def = defs_.find_short_opt(arg.substr(1, 1));
            if(next_arg_def.is_ndef()) {
              args_.push_back(arg_t(pos, key, arg, *def));
              arg.clear();
            } else {
              args_.push_back(arg_t(pos, key, def->value_if_no_value(), *def));
            }
          }
        }
        pos++;
      }
    }
 
    // Checks
    for(typename defs_t::const_iterator it=defs_.begin(); it != defs_.end(); ++it) {
      const def_t& def = *it;
      const arg_t& arg = args_.find(def.short_opt(), def.long_opt());
      if(arg.is_narg()) {
        str_t k = def.short_opt().empty() ? def.long_opt() : def.short_opt();
        if(def.is_required()) {
          errs_.push_back(str_t("Missing option ")
            + (def.short_opt().empty() ? "" : (str_t("-") + def.short_opt()))
            + (def.short_opt().empty() || def.long_opt().empty() ? "" : "/")
            + (def.long_opt().empty() ? "" : (str_t("--") + def.long_opt()))
          );
        } else if(!(def.value_if_omitted().empty())) {
          str_t v = def.value_if_omitted();
          args_.push_back(arg_t(arg_t::npos, k,v, def));
        } else if(def.is_flag()) {
          args_.push_back(arg_t(arg_t::npos, k, "", def));
        }
      }
    }
 
    for(typename args_t::const_iterator it=args_.begin(); it != args_.end(); ++it) {
      if(!it->is_ok()) {
        if(it->is_undefined_option()) {
          if(!allow_unknown_opts_) {
            errs_.push_back(str_t("Unknown option '")
              + (it->key().length() > 1 ? "--" : "-") + it->key() + "'"
            );
          }
        } else {
          errs_.push_back(
            str_t("Invalid option value '")
            + str_t(it->value())
            + str_t("' for ")
            + (it->definition().short_opt().empty() ? "" : (str_t("-") + it->definition().short_opt()))
            + (it->definition().short_opt().empty() || it->definition().long_opt().empty() ? "" : "/")
            + (it->definition().long_opt().empty() ? "" : (str_t("--") + it->definition().long_opt()))
          );
        }
      }
    }
    return *this;
  }
 
  /**
   * Checks the definitions and adds faults to the list of `errors()`.
   * @return basic_cli_args&
   */
  basic_cli_args& check_definitions()
  {
    #define defe(X) errs_.push_back(str_t("Option definition error (-") \
      + it->short_opt() + "/--" + it->long_opt() + "): " + X);
    for(typename defs_t::const_iterator it=defs_.begin(); it!=defs_.end(); ++it) {
      if(it->is_ndef()) {
        defe("Option without short and long opt.");
        continue;
      }
      if(it->short_opt().length()>1) {
        defe("Short option with more than one character.");
      }
      if(it->long_opt().find_first_of("\t ") != str_t::npos) {
        defe("Long option with whitespace.");
      }
      if(it->long_opt().length()>0 && it->long_opt()[0]=='-') {
        defe("Don't use -- in definitions.");
      }
      double dm,  ds;
      switch(it->type()) {
        case 's':
        case 'f':
          break;
        case 'n':
        case 'i':
        case 'b':
          dm = it->value_if_omitted().empty() ? 0 : basic_cli_args_s2d(it->value_if_omitted());
          ds = it->value_if_no_value().empty() ? 0 : basic_cli_args_s2d(it->value_if_no_value());
          switch(it->type()) {
            case 'b':
              if((dm!=0 && dm!=1) || (ds!=0 && ds!=1)) {
                defe("Default value for bool is not '0' or '1'.");
              }
              break;
            case 'n':
              if(std::isnan(dm) || std::isnan(ds)) {
                defe("Default value is no number.");
              }
              break;
            case 'i':
              if(std::isnan(dm) || std::isnan(ds) || (long)dm != dm || (long)ds != ds) {
                defe("Default value is no integer.");
              }
              break;
          }
          break;
        default:
          defe("Unknown type specification character.");
      }
    }
    return *this;
    #undef defe
  }
 
  /**
   * Prints errors, if any there.
   * @param ostream &os = std::cerr
   * @return basic_cli_args&
   */
  basic_cli_args& print_errors(ostream_t& os=std::cerr)
  {
    if(errs_.empty()) return *this;
    if(errs_.size()==1) {
      os << "Error: " << errs_.front() << std::endl;
    } else {
      os << "Errors: - " << errs_.front();
      typename str_vect_t::const_iterator it=errs_.begin();
      while(++it!=errs_.end()) os << std::endl << "        - " << *it;
      os << std::endl << std::endl;
    }
    return *this;
  }
 
  /**
   * Prints arguments help to the specified stream.
   * @param ostream& where_to=std::cerr
   * @return basic_cli_args&
   */
  inline basic_cli_args& help_arguments(ostream_t& where_to=std::cerr)
  {
    for(size_t i=0; i<defs_.size(); ++i) where_to << defs_[i] << std::endl;
    return *this;
  }
 
  /**
   * Prints synopsis in the same order as they are defined.
   * @param ostream& where_to=std::cerr
   * @return basic_cli_args&
   */
  basic_cli_args& help_synopsis(const str_t & positionals_help="",
    ostream_t& where_to=std::cerr, bool wrap_text=true)
  {
    std::basic_stringstream<typename str_t::value_type> ss;
 
    for(unsigned i=0; i<definitions().size(); ++i) {
      const def_t& def = definitions()[i];
      if(!def.is_required()) ss << "["; // [-a]/[--aa] for optionals and flags
      if(def.short_opt().empty()) {
        ss << "--" << def.long_opt();
      } else {
        ss << "-" << def.short_opt();
      }
      if(!def.is_flag()) {
        if(!def.is_value_required() && !def.is_required()) ss << "[";
        ss << "=";
        ss << "<" << def.type_name() << ">";
        if(!def.is_value_required() && !def.is_required()) ss << "]";
      }
      if(!def.is_required()) ss << "]";
      ss << " ";
    }
    ss << positionals_help;
 
    std::string s = ss.str();
    if(wrap_text) {
      unsigned w = program_name().length() + def_t::line_indent;
      s = basic_cli_args_softwrap(str_t(w, ' ')+s, def_t::line_width);
      s = s.substr(w);
    }
    where_to << str_t(def_t::line_indent, ' ') << program_name() << " " << s;
    return *this;
  }
 
  /**
   * Prints complete help
   * @param const str_t& help_introduction=""
   * @param const str_t version_author_copyright=""
   * @param const str_t& positionals_help=""
   * @param const str_t& return_values_help=""
   * @param ostream& where_to=std::cerr
   * @return basic_cli_args&
   */
  basic_cli_args& help(
    const str_t& help_introduction="",
    const str_t version_author_copyright="",
    const str_t& positionals_help="",
    const str_t& return_values_help="",
    ostream_t& where_to=std::cerr)
  {
    const typename str_t::value_type nl = '\n';
    ostream_t& os = where_to;
    unsigned l_in = def_t::line_indent;
    unsigned l_wi = def_t::line_width;
    os << "NAME" << nl << nl
       << str_t(l_in, ' ') << program_name() << nl << nl
       << "SYNOPSIS" << nl << nl;
       help_synopsis(positionals_help, os);
 
    if(!help_introduction.empty()) {
      std::string txt(help_introduction);
      if(def_t::line_indent > 0) {
        str_t ind = str_t("\n") + str_t(l_in, ' ');
        typename str_t::size_type p = 0;
        typename std::string::size_type nnl=0;
        for(typename std::string::const_iterator it=txt.begin(); it!=txt.end(); ++it) {
          if(*it=='\n') nnl++;
        }
        txt.reserve((l_in+1) * nnl);
        while((p=txt.find('\n', p+1)) != str_t::npos) { // start from 1 is ok
          txt.replace(p, 1, ind);
          p += l_in-1;
        }
        txt.insert(0, ind);
      }
      os << nl << nl << "DESCRIPTION";
      os << nl << basic_cli_args_softwrap(txt, l_wi);
    }
 
    if(!arguments().empty()) {
      os << nl << nl << "ARGUMENTS" << nl << nl;
      help_arguments(os);
    }
 
    if(!return_values_help.empty()) {
      str_t txt = return_values_help;
      if(def_t::line_indent > 0) {
        str_t ind = str_t("\n") + str_t(l_in, ' ');
        typename str_t::size_type p = 0;
        typename std::string::size_type nnl=0;
        for(typename std::string::const_iterator it=txt.begin(); it!=txt.end(); ++it) {
          if(*it=='\n') nnl++;
        }
        txt.reserve((l_in+1) * nnl);
        while((p=txt.find('\n', p+1)) != str_t::npos) { // start from 1 is ok
          txt.replace(p, 1, ind);
          p += l_in-1;
        }
        txt.insert(0, ind);
      }
      os << "RETURN VALUES" << nl;
      os << basic_cli_args_softwrap(txt, l_wi) << nl << nl;
    }
 
    if(!version_author_copyright.empty()) {
      os << program_name() << " " << version_author_copyright << nl;
    }
    return *this;
  }
 
  /**
   * Exits on error or -h/--help, printing correspondent information.
   * @param const str_t& help_introduction=""
   * @param const str_t version_author_copyright=""
   * @param const str_t& positionals_help=""
   * @param const str_t& return_values_help=""
   * @param int exit_code=1
   * @param ostream &os=std::cerr
   * @return basic_cli_args&
   */
  basic_cli_args& handle_help_and_errors(
    const str_t& help_introduction="",
    const str_t version_author_copyright="",
    const str_t& positionals_help="",
    const str_t & return_values_help="",
    int exit_code=1,
    ostream_t& os=std::cerr)
  {
    if((argv_.size() >= 1 && argv_[0] == "--help") || (argv_.size() == 1 && argv_[0] == "-h")
    #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
       || (argv_.size() == 1 && argv_[0] == "/?")
    #endif
    ) {
      help(help_introduction, version_author_copyright, positionals_help, return_values_help, os);
      ::exit(exit_code);
    } else if(has_errors()) {
      print_errors(os);
      ::exit(exit_code);
    }
    return *this;
  }
 
protected:
 
  bool allow_unknown_opts_;
  str_t app_;
  str_vect_t argv_, errs_;
  defs_t defs_;
  args_t args_;
 
};
 
 
/**
 * ndef constant
 */
template <typename str_t>
const typename basic_cli_args<str_t>::def_t& basic_cli_args<str_t>::ndef
        = basic_cli_args<str_t>::def_t::ndef;
 
/**
 * narg constant
 */
template <typename str_t>
const typename basic_cli_args<str_t>::arg_t& basic_cli_args<str_t>::narg
        = basic_cli_args<str_t>::arg_t::narg;
 
/**
 * npos constant
 */
template <typename str_t>
const int basic_cli_args<str_t>::npos
        = basic_cli_args<str_t>::arg_t::npos;
 
 
template <typename str_t>
ostream_t& operator << (ostream_t& os, basic_cli_args<str_t>& a)
{
  os << "cli_args dump: {" << std::endl;
  if(a.errors().empty()) a.check_definitions();
  os << "  program: \"" << a.program_name() << "\"," << std::endl;
  os << "  all_ok: " << (a.has_errors() ? "false," : "true,") << std::endl;
  os << "  allow unknown options: "
     << (a.allow_unknown_options() ? "true," : "false,") << std::endl;
  os << "  definitions:" << " [";
  if(a.definitions().empty()) {
    os << "]," << std::endl;
  } else {
    int li = basic_cli_arg_def<str_t>::line_indent;
    basic_cli_arg_def<str_t>::line_indent = 4;
    for(typename basic_cli_args<str_t>::defs_t::const_iterator it=a.definitions().begin();
        it!=a.definitions().end(); ++it) {
      os << std::endl << *it;
    }
    os << std::endl << "  ]," << std::endl;
    basic_cli_arg_def<str_t>::line_indent = li;
  }
  os << "  arguments: [";
  if(a.arguments().empty()) {
    os << "]," << std::endl;
  } else {
    for(typename basic_cli_args<str_t>::args_t::const_iterator it=
        a.arguments().begin(); it != a.arguments().end(); ++it) {
      os << std::endl << "    " << *it;
    }
    os << std::endl << "  ]," << std::endl;
  }
  os << "  errors: [";
  if(a.errors().empty()) {
    os << "]" << std::endl;
  } else {
    unsigned i;
    for(i=0; i< a.errors().size()-1; ++i) {
      os << std::endl << "    \"" << a.errors()[i] << "\",";
    }
    os << std::endl << "    \"" << a.errors()[i] << "\"";
    os << std::endl << " ]" << std::endl;
  }
  os << "}" << std::endl;
  return os;
}
 
}}
// </editor-fold>
 
// <editor-fold desc="specialisation" defaultstate="collapsed">
namespace sw {
  /**
   * Standard specialisation with std::string
   * THAT'S THE ONE USUALLY USED
   */
  typedef detail::basic_cli_args<std::string> cli_args_t;
  cli_args_t cli_args;
}
// </editor-fold>
 
// <editor-fold desc="undefs" defaultstate="collapsed">
#undef ostream_t
#undef NaN
// </editor-fold>
 
#endif

