// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_CMD_LINE_PARSER_KERNEl_1_
#define DLIB_CMD_LINE_PARSER_KERNEl_1_
#include "cmd_line_parser_kernel_abstract.h"
#include "../algs.h"
#include <string>
#include <sstream>
#include "../interfaces/enumerable.h"
#include "../interfaces/cmd_line_parser_option.h"
#include "../assert.h"
#include "../string.h"
namespace dlib
{
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
class cmd_line_parser_kernel_1 : public enumerable<cmd_line_parser_option<charT> >
{
/*!
REQUIREMENTS ON map
is an implementation of map/map_kernel_abstract.h
is instantiated to map items of type std::basic_string<charT> to void*
REQUIREMENTS ON sequence
is an implementation of sequence/sequence_kernel_abstract.h and
is instantiated with std::basic_string<charT>
REQUIREMENTS ON sequence2
is an implementation of sequence/sequence_kernel_abstract.h and
is instantiated with std::basic_string<charT>*
INITIAL VALUE
options.size() == 0
argv.size() == 0
have_parsed_line == false
CONVENTION
have_parsed_line == parsed_line()
argv[index] == operator[](index)
argv.size() == number_of_arguments()
*((option_t*)options[name]) == option(name)
options.is_in_domain(name) == option_is_defined(name)
!*/
public:
typedef charT char_type;
typedef std::basic_string<charT> string_type;
typedef cmd_line_parser_option<charT> option_type;
// exception class
class cmd_line_parse_error : public dlib::error
{
void set_info_string (
)
{
std::ostringstream sout;
switch (type)
{
case EINVALID_OPTION:
sout << "Command line error: '" << narrow(item) << "' is not a valid option.";
break;
case ETOO_FEW_ARGS:
if (num > 1)
{
sout << "Command line error: The '" << narrow(item) << "' option requires " << num
<< " arguments.";
}
else
{
sout << "Command line error: The '" << narrow(item) << "' option requires " << num
<< " argument.";
}
break;
case ETOO_MANY_ARGS:
sout << "Command line error: The '" << narrow(item) << "' option does not take any arguments.\n";
break;
default:
sout << "Command line error.";
break;
}
const_cast<std::string&>(info) = wrap_string(sout.str(),0,0);
}
public:
cmd_line_parse_error(
error_type t,
const std::basic_string<charT>& _item
) :
dlib::error(t),
item(_item),
num(0)
{ set_info_string();}
cmd_line_parse_error(
error_type t,
const std::basic_string<charT>& _item,
unsigned long _num
) :
dlib::error(t),
item(_item),
num(_num)
{ set_info_string();}
cmd_line_parse_error(
) :
dlib::error(),
item(),
num(0)
{ set_info_string();}
~cmd_line_parse_error() throw() {}
const std::basic_string<charT> item;
const unsigned long num;
};
private:
class option_t : public cmd_line_parser_option<charT>
{
/*!
INITIAL VALUE
options.size() == 0
CONVENTION
name_ == name()
description_ == description()
number_of_arguments_ == number_of_arguments()
options[N][arg] == argument(arg,N)
num_present == count()
!*/
friend class cmd_line_parser_kernel_1<charT,map,sequence,sequence2>;
public:
const std::basic_string<charT>& name (
) const { return name_; }
const std::basic_string<charT>& group_name (
) const { return group_name_; }
const std::basic_string<charT>& description (
) const { return description_; }
unsigned long number_of_arguments(
) const { return number_of_arguments_; }
unsigned long count (
) const { return num_present; }
const std::basic_string<charT>& argument (
unsigned long arg,
unsigned long N
) const
{
// make sure requires clause is not broken
DLIB_CASSERT( N < count() && arg < number_of_arguments(),
"\tconst string_type& cmd_line_parser_option::argument(unsigned long,unsigned long)"
<< "\n\tInvalid arguments were given to this function."
<< "\n\tthis: " << this
<< "\n\tN: " << N
<< "\n\targ: " << arg
<< "\n\tname(): " << narrow(name())
<< "\n\tcount(): " << count()
<< "\n\tnumber_of_arguments(): " << number_of_arguments()
);
return options[N][arg];
}
protected:
option_t (
) :
num_present(0)
{}
~option_t()
{
clear();
}
private:
void clear()
/*!
ensures
- #count() == 0
- clears everything out of options and frees memory
!*/
{
for (unsigned long i = 0; i < options.size(); ++i)
{
delete [] options[i];
}
options.clear();
num_present = 0;
}
// data members
std::basic_string<charT> name_;
std::basic_string<charT> group_name_;
std::basic_string<charT> description_;
sequence2 options;
unsigned long number_of_arguments_;
unsigned long num_present;
// restricted functions
option_t(option_t&); // copy constructor
option_t& operator=(option_t&); // assignment operator
};
// --------------------------
public:
cmd_line_parser_kernel_1 (
);
virtual ~cmd_line_parser_kernel_1 (
);
void clear(
);
void parse (
int argc,
const charT** argv
);
void parse (
int argc,
charT** argv
)
{
parse(argc, const_cast<const charT**>(argv));
}
bool parsed_line(
) const;
bool option_is_defined (
const string_type& name
) const;
void add_option (
const string_type& name,
const string_type& description,
unsigned long number_of_arguments = 0
);
void set_group_name (
const string_type& group_name
);
string_type get_group_name (
) const { return group_name; }
const cmd_line_parser_option<charT>& option (
const string_type& name
) const;
unsigned long number_of_arguments(
) const;
const string_type& operator[] (
unsigned long index
) const;
void swap (
cmd_line_parser_kernel_1& item
);
// functions from the enumerable interface
bool at_start (
) const { return options.at_start(); }
void reset (
) const { options.reset(); }
bool current_element_valid (
) const { return options.current_element_valid(); }
const cmd_line_parser_option<charT>& element (
) const { return *static_cast<cmd_line_parser_option<charT>*>(options.element().value()); }
cmd_line_parser_option<charT>& element (
) { return *static_cast<cmd_line_parser_option<charT>*>(options.element().value()); }
bool move_next (
) const { return options.move_next(); }
size_t size (
) const { return options.size(); }
private:
// data members
map options;
sequence argv;
bool have_parsed_line;
string_type group_name;
// restricted functions
cmd_line_parser_kernel_1(cmd_line_parser_kernel_1&); // copy constructor
cmd_line_parser_kernel_1& operator=(cmd_line_parser_kernel_1&); // assignment operator
};
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
inline void swap (
cmd_line_parser_kernel_1<charT,map,sequence,sequence2>& a,
cmd_line_parser_kernel_1<charT,map,sequence,sequence2>& b
) { a.swap(b); }
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// member function definitions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
cmd_line_parser_kernel_1 (
) :
have_parsed_line(false)
{
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
~cmd_line_parser_kernel_1 (
)
{
// delete all option_t objects in options
options.reset();
while (options.move_next())
{
delete static_cast<option_t*>(options.element().value());
}
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
void cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
clear(
)
{
have_parsed_line = false;
argv.clear();
// delete all option_t objects in options
options.reset();
while (options.move_next())
{
delete static_cast<option_t*>(options.element().value());
}
options.clear();
reset();
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
void cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
parse (
int argc_,
const charT** argv
)
{
using namespace std;
// make sure there aren't any arguments hanging around from the last time
// parse was called
this->argv.clear();
// make sure that the options have been cleared of any arguments since
// the last time parse() was called
if (have_parsed_line)
{
options.reset();
while (options.move_next())
{
static_cast<option_t*>(options.element().value())->clear();
}
options.reset();
}
// this tells us if we have seen -- on the command line all by itself
// or not.
bool escape = false;
const unsigned long argc = static_cast<unsigned long>(argc_);
try
{
for (unsigned long i = 1; i < argc; ++i)
{
if (argv[i][0] == _dT(charT,'-') && !escape)
{
// we are looking at the start of an option
// --------------------------------------------------------------------
if (argv[i][1] == _dT(charT,'-'))
{
// we are looking at the start of a "long named" option
string_type temp = &argv[i][2];
string_type first_argument;
typename string_type::size_type pos = temp.find_first_of(_dT(charT,'='));
// This variable will be 1 if there is an argument supplied via the = sign
// and 0 otherwise.
unsigned long extra_argument = 0;
if (pos != string_type::npos)
{
// there should be an extra argument
extra_argument = 1;
first_argument = temp.substr(pos+1);
temp = temp.substr(0,pos);
}
// make sure this name is defined
if (!options.is_in_domain(temp))
{
// the long name is not a valid option
if (argv[i][2] == _dT(charT,'\0'))
{
// there was nothing after the -- on the command line
escape = true;
continue;
}
else
{
// there was something after the command line but it
// wasn't a valid option
throw cmd_line_parse_error(EINVALID_OPTION,temp);
}
}
option_t* o = static_cast<option_t*>(options[temp]);
// check the number of arguments after this option and make sure
// it is correct
if (argc + extra_argument <= o->number_of_arguments() + i)
{
// there are too few arguments
throw cmd_line_parse_error(ETOO_FEW_ARGS,temp,o->number_of_arguments());
}
if (extra_argument && first_argument.size() == 0 )
{
// if there would be exactly the right number of arguments if
// the first_argument wasn't empty
if (argc == o->number_of_arguments() + i)
throw cmd_line_parse_error(ETOO_FEW_ARGS,temp,o->number_of_arguments());
else
{
// in this case we just ignore the trailing = and parse everything
// the same.
extra_argument = 0;
}
}
// you can't force an option that doesn't have any arguments to take
// one by using the --option=arg syntax
if (extra_argument == 1 && o->number_of_arguments() == 0)
{
throw cmd_line_parse_error(ETOO_MANY_ARGS,temp);
}
// at this point we know that the option is ok and we should
// populate its options object
if (o->number_of_arguments() > 0)
{
string_type* stemp = new string_type[o->number_of_arguments()];
unsigned long j = 0;
// add the argument after the = sign if one is present
if (extra_argument)
{
stemp[0] = first_argument;
++j;
}
for (; j < o->number_of_arguments(); ++j)
{
stemp[j] = argv[i+j+1-extra_argument];
}
o->options.add(o->options.size(),stemp);
}
o->num_present += 1;
// adjust the value of i to account for the arguments to
// this option
i += o->number_of_arguments() - extra_argument;
}
// --------------------------------------------------------------------
else
{
// we are looking at the start of a list of a single char options
// make sure there is something in this string other than -
if (argv[i][1] == _dT(charT,'\0'))
{
throw cmd_line_parse_error();
}
string_type temp = &argv[i][1];
const typename string_type::size_type num = temp.size();
for (unsigned long k = 0; k < num; ++k)
{
string_type name;
// Doing this instead of name = temp[k] seems to avoid a bug in g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
// which results in name[0] having the wrong value.
name.resize(1);
name[0] = temp[k];
// make sure this name is defined
if (!options.is_in_domain(name))
{
// the name is not a valid option
throw cmd_line_parse_error(EINVALID_OPTION,name);
}
option_t* o = static_cast<option_t*>(options[name]);
// if there are chars immediately following this option
int delta = 0;
if (num != k+1)
{
delta = 1;
}
// check the number of arguments after this option and make sure
// it is correct
if (argc + delta <= o->number_of_arguments() + i)
{
// there are too few arguments
std::ostringstream sout;
throw cmd_line_parse_error(ETOO_FEW_ARGS,name,o->number_of_arguments());
}
o->num_present += 1;
// at this point we know that the option is ok and we should
// populate its options object
if (o->number_of_arguments() > 0)
{
string_type* stemp = new string_type[o->number_of_arguments()];
if (delta == 1)
{
temp = &argv[i][2+k];
k = (unsigned long)num; // this ensures that the argument to this
// option isn't going to be treated as a
// list of options
stemp[0] = temp;
}
for (unsigned long j = 0; j < o->number_of_arguments()-delta; ++j)
{
stemp[j+delta] = argv[i+j+1];
}
o->options.add(o->options.size(),stemp);
// adjust the value of i to account for the arguments to
// this option
i += o->number_of_arguments()-delta;
}
} // for (unsigned long k = 0; k < num; ++k)
}
// --------------------------------------------------------------------
}
else
{
// this is just a normal argument
string_type temp = argv[i];
this->argv.add(this->argv.size(),temp);
}
}
have_parsed_line = true;
}
catch (...)
{
have_parsed_line = false;
// clear all the option objects
options.reset();
while (options.move_next())
{
static_cast<option_t*>(options.element().value())->clear();
}
options.reset();
throw;
}
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
bool cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
parsed_line(
) const
{
return have_parsed_line;
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
bool cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
option_is_defined (
const string_type& name
) const
{
return options.is_in_domain(name);
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
void cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
set_group_name (
const string_type& group_name_
)
{
group_name = group_name_;
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
void cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
add_option (
const string_type& name,
const string_type& description,
unsigned long number_of_arguments
)
{
option_t* temp = new option_t;
try
{
temp->name_ = name;
temp->group_name_ = group_name;
temp->description_ = description;
temp->number_of_arguments_ = number_of_arguments;
void* t = temp;
string_type n(name);
options.add(n,t);
}catch (...) { delete temp; throw;}
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
const cmd_line_parser_option<charT>& cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
option (
const string_type& name
) const
{
return *static_cast<cmd_line_parser_option<charT>*>(options[name]);
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
unsigned long cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
number_of_arguments(
) const
{
return argv.size();
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
const std::basic_string<charT>& cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
operator[] (
unsigned long index
) const
{
return argv[index];
}
// ----------------------------------------------------------------------------------------
template <
typename charT,
typename map,
typename sequence,
typename sequence2
>
void cmd_line_parser_kernel_1<charT,map,sequence,sequence2>::
swap (
cmd_line_parser_kernel_1<charT,map,sequence,sequence2>& item
)
{
options.swap(item.options);
argv.swap(item.argv);
exchange(have_parsed_line,item.have_parsed_line);
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_CMD_LINE_PARSER_KERNEl_1_