Added command line parser to tl.

This commit is contained in:
Matthias Koefferlein 2017-08-13 22:57:53 +02:00
parent 5cfdc2fa64
commit 7280a56ee9
6 changed files with 1054 additions and 403 deletions

View File

@ -24,422 +24,28 @@
#include "dbReader.h" #include "dbReader.h"
#include "dbCIFWriter.h" #include "dbCIFWriter.h"
#include "tlLog.h" #include "tlLog.h"
#include "tlCommandLineParser.h"
#include <QFileInfo> #include <QFileInfo>
namespace bd
{
class ArgBase
{
public:
ArgBase (const char *option, const char *brief_doc, const char *long_doc)
: m_option (option), m_brief_doc (brief_doc), m_long_doc (long_doc)
{
// .. nothing yet ..
}
virtual ~ArgBase ()
{
// .. nothing yet ..
}
const std::string &option () const
{
return m_option;
}
const std::string &brief_doc () const
{
return m_brief_doc;
}
const std::string &long_doc () const
{
return m_long_doc;
}
virtual void take_value (tl::Extractor & /*ex*/)
{
// .. nothing yet ..
}
virtual ArgBase *clone () const
{
return new ArgBase (*this);
}
virtual bool wants_value () const
{
return false;
}
private:
std::string m_option;
std::string m_brief_doc;
std::string m_long_doc;
};
template <class T>
void extract (tl::Extractor &ex, T &t, bool /*for_list*/ = false)
{
ex.read (t);
}
void extract (tl::Extractor &ex, std::string &t, bool for_list = false)
{
if (*ex == '"' || *ex == '\'') {
ex.read_quoted (t);
} else if (for_list) {
ex.read (t, ",");
} else {
t = ex.get ();
}
}
template <class T>
void extract (tl::Extractor &ex, std::vector<T> &t, bool /*for_list*/ = false)
{
while (! ex.at_end ()) {
t.push_back (T ());
extract (ex, t.back (), true);
ex.test (",");
}
}
template <class T>
struct type_without_const_ref
{
typedef T inner_type;
};
template <class T>
struct type_without_const_ref<const T &>
{
typedef T inner_type;
};
template <class T>
struct wants_value_traits
{
bool operator() () const { return true; }
};
template <>
struct wants_value_traits<bool>
{
bool operator() () const { return false; }
};
template <class T>
class arg_direct_setter
: public ArgBase
{
public:
arg_direct_setter (const char *option, T *value, const char *brief_doc, const char *long_doc)
: ArgBase (option, brief_doc, long_doc), mp_value (value)
{
// .. nothing yet ..
}
virtual void take_value (tl::Extractor &ex)
{
extract (ex, *mp_value);
}
virtual ArgBase *clone () const
{
return new arg_direct_setter<T> (*this);
}
virtual bool wants_value () const
{
return wants_value_traits<T> () ();
}
private:
T *mp_value;
};
template <class C, class T>
class arg_method_setter
: public ArgBase
{
public:
arg_method_setter (const char *option, C *object, void (C::*setter)(T), const char *brief_doc, const char *long_doc)
: ArgBase (option, brief_doc, long_doc), mp_object (object), mp_setter (setter)
{
// .. nothing yet ..
}
virtual void take_value (tl::Extractor &ex)
{
typename type_without_const_ref<T>::innter_type t = T ();
extract (ex, t);
(mp_object->*mp_setter) (t);
}
virtual ArgBase *clone () const
{
return new arg_method_setter<C, T> (*this);
}
virtual bool wants_value () const
{
return wants_value_traits<T> () ();
}
private:
C *mp_object;
void (C::*mp_setter)(T);
};
template <class C, class T>
arg_method_setter<C, T> arg (const char *option, C *object, void (C::*setter)(T), const char *brief_doc, const char *long_doc = "")
{
return arg_method_setter<C, T> (option, object, setter, brief_doc, long_doc);
}
template <class T>
arg_direct_setter<T> arg (const char *option, T *value, const char *brief_doc, const char *long_doc = "")
{
return arg_direct_setter<T> (option, value, brief_doc, long_doc);
}
struct ParsedOption
{
ParsedOption (const std::string &option)
: optional (false)
{
tl::Extractor ex (option.c_str ());
while (! ex.at_end ()) {
if (ex.test ("--")) {
optional = true;
ex.read_word (long_option, "_-");
if (ex.test ("=")) {
ex.read_word (name);
}
} else if (ex.test ("-")) {
optional = true;
ex.read_word (short_option, "");
if (ex.test ("=")) {
ex.read_word (name);
}
} else {
optional = ex.test ("?");
ex.read_word (name);
}
ex.test("|");
}
}
bool optional;
std::string long_option, short_option, name;
};
class CommandLineOptions
{
public:
CommandLineOptions ()
{
// Populate with the built-in options
*this << ArgBase ("-h|--help", "Shows the usage", "");
}
~CommandLineOptions ()
{
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
delete *a;
}
m_args.clear ();
}
CommandLineOptions &operator<< (const ArgBase &a)
{
m_args.push_back (a.clone ());
return *this;
}
void brief (const std::string &text)
{
m_brief = text;
}
void parse (int argc, char *argv[])
{
for (int i = 0; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "-h" || arg == "--help") {
produce_help (tl::to_string (QFileInfo (tl::to_qstring (argv[0])).fileName ()));
throw tl::CancelException ();
}
}
// @@@ implement parsing
}
private:
std::string m_brief;
std::vector<ArgBase *> m_args;
void print_string_formatted (const std::string &indent, unsigned int columns, const std::string &text)
{
tl::info << indent << tl::noendl;
unsigned int c = 0;
const char *t = text.c_str ();
while (*t) {
const char *tt = t;
bool at_beginning = (c == 0);
while (*t && *t != ' ' && *t != '\n') {
++t;
++c;
if (c == columns && !at_beginning) {
tl::info << "";
tl::info << indent << tl::noendl;
c = 0;
}
}
tl::info << std::string (tt, 0, t - tt) << tl::noendl;
while (*t == ' ') {
++t;
}
if (*t == '\n') {
++t;
tl::info << tl::endl << indent << tl::noendl;
c = 0;
} else {
if (c + 1 == columns) {
tl::info << tl::endl << indent << tl::noendl;
c = 0;
} else {
tl::info << " " << tl::noendl;
c += 1;
}
}
while (*t == ' ') {
++t;
}
}
tl::info << "";
}
std::string pad_string (unsigned int columns, const std::string &text)
{
std::string s = text;
while (s.size () < size_t (columns)) {
s += " ";
}
return s;
}
void produce_help (const std::string &program_name)
{
int columns = 60;
tl::info << "Usage:" << tl::endl;
tl::info << " " << program_name << " [options]" << tl::noendl;
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
ParsedOption option ((*a)->option ());
if (! option.name.empty ()) {
if (option.optional) {
tl::info << " [<" << option.name << ">]" << tl::noendl;
} else {
tl::info << " <" << option.name << ">" << tl::noendl;
}
}
}
tl::info << tl::endl;
print_string_formatted (" ", columns, m_brief);
tl::info << tl::endl;
unsigned int short_option_width = 0;
unsigned int long_option_width = 0;
unsigned int name_width = 0;
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
ParsedOption option ((*a)->option ());
name_width = std::max (name_width, (unsigned int) option.name.size ());
short_option_width = std::max (short_option_width, (unsigned int) option.short_option.size ());
long_option_width = std::max (long_option_width, (unsigned int) option.long_option.size ());
}
tl::info << "Arguments:" << tl::endl;
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
ParsedOption option ((*a)->option ());
if (! option.short_option.empty () || ! option.long_option.empty ()) {
continue;
}
std::string n = "<" + option.name + ">";
if (option.optional) {
n += " (optional)";
}
tl::info << " " << pad_string (name_width + 13, n) << (*a)->brief_doc ();
tl::info << "";
if (! (*a)->long_doc ().empty ()) {
print_string_formatted (" ", columns, (*a)->long_doc ());
tl::info << "";
}
}
tl::info << "";
tl::info << "Options:" << tl::endl;
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
ParsedOption option ((*a)->option ());
if (option.short_option.empty () && option.long_option.empty ()) {
continue;
}
std::string name;
if ((*a)->wants_value ()) {
name = option.name;
if (name.empty ()) {
name = "value";
}
}
tl::info << " "
<< pad_string (short_option_width + 5, option.short_option.empty () ? "" : "-" + option.short_option) << " "
<< pad_string (long_option_width + 5, option.long_option.empty () ? "" : "--" + option.long_option) << " "
<< pad_string (name_width + 3, name) << " "
<< (*a)->brief_doc ();
tl::info << "";
if (! (*a)->long_doc ().empty ()) {
print_string_formatted (" ", columns, (*a)->long_doc ());
tl::info << "";
}
}
}
};
}
int int
main (int argc, char *argv []) main (int argc, char *argv [])
{ {
db::CIFWriterOptions cif_options; db::CIFWriterOptions cif_options;
std::string infile, outfile; std::string infile, outfile;
bd::CommandLineOptions cmd; tl::CommandLineOptions cmd;
cmd << bd::arg("-od|--dummy-calls", &cif_options.dummy_calls, "Produces dummy calls", cmd << tl::arg("-od|--dummy-calls", &cif_options.dummy_calls, "Produces dummy calls",
"If this option is given, the writer will produce dummy cell calls on global level for all top cells" "If this option is given, the writer will produce dummy cell calls on global level for all top cells"
) )
<< bd::arg("-ob|--blank-separator", &cif_options.blank_separator, "Uses blanks as x/y separators", << tl::arg("-ob|--blank-separator", &cif_options.blank_separator, "Uses blanks as x/y separators",
"If this option is given, blank characters will be used to separate x and y values. " "If this option is given, blank characters will be used to separate x and y values. "
"Otherwise comma characters will be used.\n" "Otherwise comma characters will be used.\n"
"Use this option if your CIF consumer cannot read comma characters as x/y separators." "Use this option if your CIF consumer cannot read comma characters as x/y separators."
) )
<< bd::arg("input", &infile, "The input file (any format, may be gzip compressed)") << tl::arg("input", &infile, "The input file (any format, may be gzip compressed)")
<< bd::arg("output", &outfile, "The output file") << tl::arg("output", &outfile, "The output file")
; ;
cmd.brief ("This program will convert the given file to a CIF file"); cmd.brief ("This program will convert the given file to a CIF file");

View File

@ -43,7 +43,8 @@ SOURCES = \
tlFileSystemWatcher.cc \ tlFileSystemWatcher.cc \
tlFileUtils.cc \ tlFileUtils.cc \
tlWebDAV.cc \ tlWebDAV.cc \
tlArch.cc tlArch.cc \
tlCommandLineParser.cc
HEADERS = \ HEADERS = \
tlAlgorithm.h \ tlAlgorithm.h \
@ -90,7 +91,8 @@ HEADERS = \
tlCpp.h \ tlCpp.h \
tlFileUtils.h \ tlFileUtils.h \
tlWebDAV.h \ tlWebDAV.h \
tlArch.h tlArch.h \
tlCommandLineParser.h
INCLUDEPATH = INCLUDEPATH =
DEPENDPATH = DEPENDPATH =

View File

@ -0,0 +1,336 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2017 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlLog.h"
#include "tlCommandLineParser.h"
#include <QFileInfo>
namespace tl
{
// ------------------------------------------------------------------------
// ArgBase implementation
ArgBase::ParsedOption::ParsedOption (const std::string &option)
: optional (false)
{
tl::Extractor ex (option.c_str ());
while (! ex.at_end ()) {
if (ex.test ("--")) {
optional = true;
ex.read_word (long_option, "_-");
if (ex.test ("=")) {
ex.read_word (name);
}
} else if (ex.test ("-")) {
optional = true;
ex.read_word (short_option, "");
if (ex.test ("=")) {
ex.read_word (name);
}
} else {
optional = ex.test ("?");
ex.read_word (name);
}
ex.test("|");
}
}
ArgBase::ArgBase (const char *option, const char *brief_doc, const char *long_doc)
: m_option (option), m_brief_doc (brief_doc), m_long_doc (long_doc)
{
// .. nothing yet ..
}
ArgBase::~ArgBase ()
{
// .. nothing yet ..
}
bool
ArgBase::is_option () const
{
return !m_option.short_option.empty () || !m_option.long_option.empty ();
}
// ------------------------------------------------------------------------
// CommandLineOptions implementation
CommandLineOptions::CommandLineOptions ()
{
// Populate with the built-in options
*this << ArgBase ("-h|--help", "Shows the usage", "");
}
CommandLineOptions::~CommandLineOptions ()
{
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
delete *a;
}
m_args.clear ();
}
CommandLineOptions &
CommandLineOptions::operator<< (const ArgBase &a)
{
m_args.push_back (a.clone ());
return *this;
}
static void
print_string_formatted (const std::string &indent, unsigned int columns, const std::string &text)
{
tl::info << indent << tl::noendl;
unsigned int c = 0;
const char *t = text.c_str ();
while (*t) {
const char *tt = t;
bool at_beginning = (c == 0);
while (*t && *t != ' ' && *t != '\n') {
++t;
++c;
if (c == columns && !at_beginning) {
tl::info << "";
tl::info << indent << tl::noendl;
c = 0;
}
}
tl::info << std::string (tt, 0, t - tt) << tl::noendl;
while (*t == ' ') {
++t;
}
if (*t == '\n') {
++t;
tl::info << tl::endl << indent << tl::noendl;
c = 0;
} else {
if (c + 1 == columns) {
tl::info << tl::endl << indent << tl::noendl;
c = 0;
} else {
tl::info << " " << tl::noendl;
c += 1;
}
}
while (*t == ' ') {
++t;
}
}
tl::info << "";
}
static std::string
pad_string (unsigned int columns, const std::string &text)
{
std::string s = text;
while (s.size () < size_t (columns)) {
s += " ";
}
return s;
}
void
CommandLineOptions::produce_help (const std::string &program_name)
{
int columns = 60;
tl::info << "Usage:" << tl::endl;
tl::info << " " << program_name << " [options]" << tl::noendl;
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
if (! (*a)->option ().name.empty ()) {
if ((*a)->option ().optional) {
tl::info << " [<" << (*a)->option ().name << ">]" << tl::noendl;
} else {
tl::info << " <" << (*a)->option ().name << ">" << tl::noendl;
}
}
}
tl::info << tl::endl;
print_string_formatted (" ", columns, m_brief);
tl::info << tl::endl;
unsigned int short_option_width = 0;
unsigned int long_option_width = 0;
unsigned int name_width = 0;
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
name_width = std::max (name_width, (unsigned int) (*a)->option ().name.size ());
short_option_width = std::max (short_option_width, (unsigned int) (*a)->option ().short_option.size ());
long_option_width = std::max (long_option_width, (unsigned int) (*a)->option ().long_option.size ());
}
tl::info << "Arguments:" << tl::endl;
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
if ((*a)->is_option ()) {
continue;
}
std::string n = "<" + (*a)->option ().name + ">";
if ((*a)->option ().optional) {
n += " (optional)";
}
tl::info << " " << pad_string (name_width + 13, n) << (*a)->brief_doc ();
tl::info << "";
if (! (*a)->long_doc ().empty ()) {
print_string_formatted (" ", columns, (*a)->long_doc ());
tl::info << "";
}
}
tl::info << "";
tl::info << "Options:" << tl::endl;
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end (); ++a) {
if (! (*a)->is_option ()) {
continue;
}
std::string name;
if ((*a)->wants_value ()) {
name = (*a)->option ().name;
if (name.empty ()) {
name = "value";
}
}
tl::info << " "
<< pad_string (short_option_width + 5, (*a)->option ().short_option.empty () ? "" : "-" + (*a)->option ().short_option) << " "
<< pad_string (long_option_width + 5, (*a)->option ().long_option.empty () ? "" : "--" + (*a)->option ().long_option) << " "
<< pad_string (name_width + 3, name) << " "
<< (*a)->brief_doc ();
tl::info << "";
if (! (*a)->long_doc ().empty ()) {
print_string_formatted (" ", columns, (*a)->long_doc ());
tl::info << "";
}
}
}
void
CommandLineOptions::parse (int argc, char *argv[])
{
for (int i = 0; i < argc; ++i) {
std::string arg_as_utf8 = tl::to_string (QString::fromLocal8Bit (argv [i]));
if (arg_as_utf8 == "-h" || arg_as_utf8 == "--help") {
produce_help (tl::to_string (QFileInfo (QString::fromLocal8Bit (argv [0])).fileName ()));
throw tl::CancelException ();
}
}
std::vector<ArgBase *>::const_iterator next_plain_arg = m_args.begin ();
while (next_plain_arg != m_args.end () && (*next_plain_arg)->is_option ()) {
++next_plain_arg;
}
for (int i = 1; i < argc; ++i) {
ArgBase *arg = 0;
std::string arg_as_utf8 = tl::to_string (QString::fromLocal8Bit (argv [i]));
tl::Extractor ex (arg_as_utf8.c_str ());
if (ex.test ("--")) {
std::string n;
ex.read_word (n);
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end () && !arg; ++a) {
if ((*a)->option ().long_option == n) {
arg = *a;
}
}
if (!arg) {
throw tl::Exception (tl::to_string (QObject::tr ("Unknown command line option --%1 (use -h for help)").arg (tl::to_qstring (n))));
}
} else if (ex.test ("-")) {
std::string n;
ex.read_word (n);
for (std::vector<ArgBase *>::const_iterator a = m_args.begin (); a != m_args.end () && !arg; ++a) {
if ((*a)->option ().short_option == n) {
arg = *a;
}
}
if (!arg) {
throw tl::Exception (tl::to_string (QObject::tr ("Unknown command line option -%1 (use -h for help)").arg (tl::to_qstring (n))));
}
} else {
if (next_plain_arg == m_args.end ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Unknown command line component %1 - no further plain argument expected (use -h for help)").arg (tl::to_qstring (arg_as_utf8))));
}
arg = *next_plain_arg++;
while (next_plain_arg != m_args.end () && (*next_plain_arg)->is_option ()) {
++next_plain_arg;
}
}
if (arg->wants_value ()) {
if (! arg->is_option () || ex.test ("=")) {
arg->take_value (ex);
} else {
if (! ex.at_end ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Syntax error in argument at \"..%1\" (use -h for help)").arg (tl::to_qstring (ex.get ()))));
}
++i;
if (i == argc) {
throw tl::Exception (tl::to_string (QObject::tr ("Value missing for last argument (use -h for help)")));
}
std::string arg_as_utf8 = tl::to_string (QString::fromLocal8Bit (argv [i]));
tl::Extractor ex_value (arg_as_utf8);
arg->take_value (ex_value);
}
} else {
if (ex.test ("=")) {
arg->take_value (ex);
} else {
arg->mark_present ();
}
}
}
if (next_plain_arg != m_args.end () && !(*next_plain_arg)->option ().optional) {
throw tl::Exception (tl::to_string (QObject::tr ("Additional arguments required (use -h for help)")));
}
}
}

View File

@ -0,0 +1,420 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2017 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(HDR_tlCommandLineParser_h)
#define HDR_tlCommandLineParser_h
#include "tlCommon.h"
#include "tlLog.h"
#include <QFileInfo>
namespace tl
{
/**
* @brief A base class for argument getters
*
* The argument getters are both declarations (delivering names and doc)
* as well as providers for the translation methods. Argument getters
* are attached to the CommandLineOptions object to form the
* command line declaration.
*/
class TL_PUBLIC ArgBase
{
public:
/**
* @brief A parsed version of the option string
*/
struct ParsedOption
{
/**
* @brief Constructor
* This constructor populates the optional flag and
* the long_option, short_option and name values.
*/
ParsedOption (const std::string &option);
bool optional;
std::string long_option, short_option, name;
};
/**
* @brief Constructor
*
* @param option The option descriptor
* @param brief_doc A short documentation
* @param long_doc A long documentation (or empty to skip this)
*
* The option descriptor is either
* "name" - A mandatory input parameter with name "name"
* "?name" - An optional input parameter with name "name"
* "-o" - A short option without a parameter (boolean)
* "-o=value" - A short option with a value named "value"
* "--long-option" - A long option
* "--long-option=value" - A long option with a value
* "-o|--long-option" - A short/long option
* "-o|--long-option=value" - A short/long option with a value
*/
ArgBase (const char *option, const char *brief_doc, const char *long_doc);
/**
* @brief Destructor
*/
virtual ~ArgBase ();
/**
* @brief The option descriptor (see constructor)
*/
const ParsedOption &option () const
{
return m_option;
}
/**
* @brief The short documentation
*/
const std::string &brief_doc () const
{
return m_brief_doc;
}
/**
* @brief The long documentation
*/
const std::string &long_doc () const
{
return m_long_doc;
}
/**
* @brief Returns true, if the argument is an option
*/
bool is_option () const;
/**
* @brief Gets a value from the extractor into the target of the argument
*/
virtual void take_value (tl::Extractor & /*ex*/)
{
// .. nothing yet ..
}
/**
* @brief Marks an option to be present (for boolean options)
*/
virtual void mark_present ()
{
// .. nothing yet ..
}
/**
* @brief Clones this method
*/
virtual ArgBase *clone () const
{
return new ArgBase (*this);
}
/**
* @brief Returns a value indicating whether the argument wants a value
* This flag indicates that a value shall be assigned. For boolean types
* no value needs to be supplied. For other types a value is required.
*/
virtual bool wants_value () const
{
return false;
}
private:
ParsedOption m_option;
std::string m_brief_doc;
std::string m_long_doc;
};
/**
* @brief A helper for extracting values by type
*/
template <class T>
void extract (tl::Extractor &ex, T &t, bool /*for_list*/ = false)
{
ex.read (t);
}
/**
* @brief A specialization for the string type
*/
void extract (tl::Extractor &ex, std::string &t, bool for_list = false)
{
if (*ex == '"' || *ex == '\'') {
ex.read_quoted (t);
} else if (for_list) {
ex.read (t, ",");
} else {
t = ex.get ();
}
}
/**
* @brief A specialization for a list of any type (vector)
*/
template <class T>
void extract (tl::Extractor &ex, std::vector<T> &t, bool /*for_list*/ = false)
{
while (! ex.at_end ()) {
t.push_back (T ());
extract (ex, t.back (), true);
ex.test (",");
}
}
/**
* @brief A helper to mark "presence"
*/
template <class T>
void mark_presence (T &)
{
// .. the default implementation does nothing ..
}
void mark_presence (bool &t)
{
t = true;
}
template <class C, class T>
void mark_presence_setter (C *, void (C::*) (T))
{
// .. the default implementation does nothing ..
}
template <class C>
void mark_presence_setter (C *c, void (C::*ptr) (bool))
{
(c->*ptr) (true);
}
/**
* @brief A helper template to extract the actual type from (T) or (const T &)
*/
template <class T>
struct type_without_const_ref
{
typedef T inner_type;
};
template <class T>
struct type_without_const_ref<const T &>
{
typedef T inner_type;
};
/**
* @brief A helper template to determine types that don't need a value
*/
template <class T>
struct wants_value_traits
{
bool operator() () const { return true; }
};
template <>
struct wants_value_traits<bool>
{
bool operator() () const { return false; }
};
/**
* @brief A specialization of the ArgBase class for a setter with direct access to the value
*/
template <class T>
class arg_direct_setter
: public ArgBase
{
public:
arg_direct_setter (const char *option, T *value, const char *brief_doc, const char *long_doc)
: ArgBase (option, brief_doc, long_doc), mp_value (value)
{
// .. nothing yet ..
}
virtual void take_value (tl::Extractor &ex)
{
extract (ex, *mp_value);
}
virtual void mark_present ()
{
mark_presence (*mp_value);
}
virtual ArgBase *clone () const
{
return new arg_direct_setter<T> (*this);
}
virtual bool wants_value () const
{
return wants_value_traits<T> () ();
}
private:
T *mp_value;
};
/**
* @brief A specialization of the ArgBase class for a setter that is a method of a class
*/
template <class C, class T>
class arg_method_setter
: public ArgBase
{
public:
arg_method_setter (const char *option, C *object, void (C::*setter)(T), const char *brief_doc, const char *long_doc)
: ArgBase (option, brief_doc, long_doc), mp_object (object), mp_setter (setter)
{
// .. nothing yet ..
}
virtual void take_value (tl::Extractor &ex)
{
typedef typename type_without_const_ref<T>::inner_type inner_type;
inner_type t = inner_type ();
extract (ex, t);
(mp_object->*mp_setter) (t);
}
virtual void mark_present ()
{
mark_presence_setter (mp_object, mp_setter);
}
virtual ArgBase *clone () const
{
return new arg_method_setter<C, T> (*this);
}
virtual bool wants_value () const
{
return wants_value_traits<T> () ();
}
private:
C *mp_object;
void (C::*mp_setter)(T);
};
/**
* @brief Polymorphic production methods for the argument getters
*/
template <class C, class T>
arg_method_setter<C, T> arg (const char *option, C *object, void (C::*setter)(T), const char *brief_doc, const char *long_doc = "")
{
return arg_method_setter<C, T> (option, object, setter, brief_doc, long_doc);
}
template <class T>
arg_direct_setter<T> arg (const char *option, T *value, const char *brief_doc, const char *long_doc = "")
{
return arg_direct_setter<T> (option, value, brief_doc, long_doc);
}
/**
* @brief The command line parser class
*
* To establish a command line parser use code like this:
*
* @code
* int
* main (int argc, char *argv [])
* {
* bool has_x = false;
* int int_value = 0;
*
* CommandLineOptions cmd;
* cmd << arg("-x|--long-option", &has_x, "X Option", "This is a long documentation for X option")
* << arg("int_value", &int_value, "A mandatory integer value");
* ;
*
* try {
* cmd.parse (argc, argv);
* ... use has_x, int_value ..
* } catch (tl::CancelException &ex) {
* return 1;
* } catch (std::exception &ex) {
* tl::error << ex.what ();
* return 1;
* } catch (tl::Exception &ex) {
* tl::error << ex.msg ();
* return 1;
* } catch (...) {
* tl::error << "ERROR: unspecific error";
* }
* }
* @endcode
*/
class TL_PUBLIC CommandLineOptions
{
public:
/**
* @brief Constructor
*/
CommandLineOptions ();
/**
* @brief Destructor
*/
~CommandLineOptions ();
/**
* @brief Adds the argument declaration & getter
*/
CommandLineOptions &operator<< (const ArgBase &a);
/**
* @brief Adds the brief documentation
*/
void brief (const std::string &text)
{
m_brief = text;
}
/**
* @brief Parses the command line and executes the getters
*
* This method will throw a tl::CancelException if the help function was used.
* It will throw other exceptions on syntax errors or if mandatory arguments are
* missing.
*/
void parse (int argc, char *argv[]);
private:
std::string m_brief;
std::vector<ArgBase *> m_args;
void produce_help (const std::string &program_name);
};
}
#endif

View File

@ -0,0 +1,286 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2017 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlCommandLineParser.h"
#include "utHead.h"
TEST(1)
{
std::string a;
int b = 0;
bool c = false;
double d = 1;
bool e = false;
std::string f;
tl::CommandLineOptions cmd;
cmd << tl::arg ("a", &a, "")
<< tl::arg ("?b", &b, "")
<< tl::arg ("-c", &c, "")
<< tl::arg ("--dlong|-d", &d, "")
<< tl::arg ("--elong", &e, "")
<< tl::arg ("-f|--flong=value", &f, "");
{
char *argv[] = { "x", "y" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (a, "y");
EXPECT_EQ (b, 0);
{
char *argv[] = { "x", "z", "17" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (a, "z");
EXPECT_EQ (b, 17);
b = 0;
{
char *argv[] = { "x", "u", "-c" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (a, "u");
EXPECT_EQ (b, 0);
EXPECT_EQ (c, true);
b = 0;
c = false;
{
char *argv[] = { "x", "u", "-c", "-d=21" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (a, "u");
EXPECT_EQ (b, 0);
EXPECT_EQ (c, true);
EXPECT_EQ (d, 21);
b = 0;
c = false;
{
char *argv[] = { "x", "u", "-d", "22", "-c" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (a, "u");
EXPECT_EQ (b, 0);
EXPECT_EQ (c, true);
EXPECT_EQ (d, 22);
e = false;
{
char *argv[] = { "x", "u", "--dlong", "23" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (a, "u");
EXPECT_EQ (d, 23);
EXPECT_EQ (e, false);
{
char *argv[] = { "x", "u", "--dlong=24", "--elong" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (a, "u");
EXPECT_EQ (d, 24);
EXPECT_EQ (e, true);
{
char *argv[] = { "x", "u", "-c", "-f=foo" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (a, "u");
EXPECT_EQ (f, "foo");
{
char *argv[] = { "x", "u", "--flong", "bar" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (a, "u");
EXPECT_EQ (f, "bar");
}
struct Values
{
Values ()
{
b = 0;
d = 1;
c = e = false;
}
void set_a (const std::string &x) { a = x; }
void set_b (int x) { b = x; }
void set_c (bool x) { c = x; }
void set_d (const double &x) { d = x; }
void set_e (bool x) { e = x; }
void set_f (std::string x) { f = x; }
std::string a;
int b;
bool c;
double d;
bool e;
std::string f;
};
TEST(2)
{
Values v;
tl::CommandLineOptions cmd;
cmd << tl::arg ("a", &v, &Values::set_a, "")
<< tl::arg ("?b", &v, &Values::set_b, "")
<< tl::arg ("-c", &v, &Values::set_c, "")
<< tl::arg ("--dlong|-d", &v, &Values::set_d, "")
<< tl::arg ("--elong", &v, &Values::set_e, "")
<< tl::arg ("-f|--flong=value", &v, &Values::set_f, "");
{
char *argv[] = { "x", "y" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (v.a, "y");
EXPECT_EQ (v.b, 0);
{
char *argv[] = { "x", "z", "17" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (v.a, "z");
EXPECT_EQ (v.b, 17);
v.b = 0;
{
char *argv[] = { "x", "u", "-c" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (v.a, "u");
EXPECT_EQ (v.b, 0);
EXPECT_EQ (v.c, true);
v.b = 0;
v.c = false;
{
char *argv[] = { "x", "u", "-c", "-d=21" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (v.a, "u");
EXPECT_EQ (v.b, 0);
EXPECT_EQ (v.c, true);
EXPECT_EQ (v.d, 21);
v.b = 0;
v.c = false;
{
char *argv[] = { "x", "u", "-d", "22", "-c" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (v.a, "u");
EXPECT_EQ (v.b, 0);
EXPECT_EQ (v.c, true);
EXPECT_EQ (v.d, 22);
v.e = false;
{
char *argv[] = { "x", "u", "--dlong", "23" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (v.a, "u");
EXPECT_EQ (v.d, 23);
EXPECT_EQ (v.e, false);
{
char *argv[] = { "x", "u", "--dlong=24", "--elong" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (v.a, "u");
EXPECT_EQ (v.d, 24);
EXPECT_EQ (v.e, true);
{
char *argv[] = { "x", "u", "-c", "-f=foo" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (v.a, "u");
EXPECT_EQ (v.f, "foo");
{
char *argv[] = { "x", "u", "--flong", "bar" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (v.a, "u");
EXPECT_EQ (v.f, "bar");
}
TEST(3)
{
std::vector<std::string> a;
std::vector<int> b;
tl::CommandLineOptions cmd;
cmd << tl::arg ("a", &a, "")
<< tl::arg ("-b", &b, "");
{
char *argv[] = { "x", "r,u,v" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (int (a.size ()), 3);
EXPECT_EQ (a[0], "r");
EXPECT_EQ (a[1], "u");
EXPECT_EQ (a[2], "v");
EXPECT_EQ (b.empty (), true);
a.clear ();
{
char *argv[] = { "x", "\"r,u\",v" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (int (a.size ()), 2);
EXPECT_EQ (a[0], "r,u");
EXPECT_EQ (a[1], "v");
EXPECT_EQ (b.empty (), true);
a.clear ();
{
char *argv[] = { "x", "'\"'", "-b=1,5,-13" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (int (a.size ()), 1);
EXPECT_EQ (a[0], "\"");
EXPECT_EQ (int (b.size ()), 3);
EXPECT_EQ (b[0], 1);
EXPECT_EQ (b[1], 5);
EXPECT_EQ (b[2], -13);
a.clear ();
b.clear ();
{
char *argv[] = { "x", "", "-b", "-13,21" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (int (a.size ()), 0);
EXPECT_EQ (int (b.size ()), 2);
EXPECT_EQ (b[0], -13);
EXPECT_EQ (b[1], 21);
}

View File

@ -101,7 +101,8 @@ SOURCES = \
tlFileUtils.cc \ tlFileUtils.cc \
tlHttpStream.cc \ tlHttpStream.cc \
tlWebDAV.cc \ tlWebDAV.cc \
laySnap.cc laySnap.cc \
tlCommandLineParser.cc
# main components: # main components:
SOURCES += \ SOURCES += \