parent
93696ef645
commit
eb6daf08ba
13 changed files with 1104 additions and 42 deletions
@ -0,0 +1,686 @@ |
|||||||
|
#include <mrw/exception.hpp> |
||||||
|
#include <mrw/stacktrace.hpp> |
||||||
|
#include <mrw/smartpointer.hpp> |
||||||
|
#include <mrw/simpletrace.hpp> |
||||||
|
#include <stdlib.h> // exit |
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
#include <set> |
||||||
|
#include <list> |
||||||
|
#include <exception> |
||||||
|
#include <stdexcept> |
||||||
|
#include <sstream> |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
namespace mrw { |
||||||
|
|
||||||
|
/** @defgroup arguments C++ Evaluation of Command Line Arguments
|
||||||
|
|
||||||
|
@brief These classes do simple and easy command line argment evaluation |
||||||
|
in C++. |
||||||
|
|
||||||
|
Features: |
||||||
|
- every argument has a long and a short option |
||||||
|
- all arguments are optional and provide a default value |
||||||
|
- the order of options is not important |
||||||
|
- every option can take any (fixed) number of additional parameter of |
||||||
|
type |
||||||
|
- string |
||||||
|
- integer |
||||||
|
- boolean (@c "yes", @c "on", @c "true" evaluates to @c true) |
||||||
|
- short options can be combined, instead of |
||||||
|
@c -a @c -b @c -c @c 15 you can simply write @c -abc @c 15 |
||||||
|
- automated help display (support for option @c -h) |
||||||
|
|
||||||
|
@c mrw::Args is the main user interface class that represents |
||||||
|
all command line options with their arguments. It is implemented |
||||||
|
as singleton, so the same instance can be accessed from |
||||||
|
everywhere in the code. It mst be setup just in the beginning of |
||||||
|
the @c main() function. |
||||||
|
|
||||||
|
The other important class for the end user is @c mrw::Opt, one |
||||||
|
possible option with additional parameter. The end user needs @c |
||||||
|
mrw::Opt to setup all allowed command line options in the |
||||||
|
beginning, bevore evaluation of the user given command line is |
||||||
|
done (before @c argc and @c argv is shifted into @c mrw::Args. |
||||||
|
|
||||||
|
The third class a user should know is @c mrw::Param. It |
||||||
|
represents the arguments to one option. Every instance of @c |
||||||
|
mrw::Opt owns one instance of @c mrw::Param that is either empty |
||||||
|
or list of (mandatory) arguments of type @c std::string, @c int |
||||||
|
or @c bool. |
||||||
|
|
||||||
|
The classes are normally used this way: |
||||||
|
|
||||||
|
@code |
||||||
|
// this program may be called e.g. with the following arguments:
|
||||||
|
// ./a.out --coordinates 13 1 -vo out.txt -n MyName
|
||||||
|
int main(int argv, const char * const * const argv) { |
||||||
|
try { |
||||||
|
mrw::Args::instance() |
||||||
|
// setup the possible options
|
||||||
|
<<mrw::Opt('h', "--help", "Show this help text") |
||||||
|
<<mrw::Opt('v', "--verbose", "print more information") |
||||||
|
<<mrw::Opt('q', "--quiet", "be quiet") |
||||||
|
<<mrw::Opt('n', "--name", mrw::Param()<<"MRW", "name of the user") |
||||||
|
<<mrw::Opt('o', "--output-file", mrw::Param()<<"", "file to load") |
||||||
|
<<mrw::Opt('c', "--coordinates", mrw::Param()<<0<<0, "X, Y coordinate") |
||||||
|
// set a description text for help
|
||||||
|
<<"This is a testprogram for argument evaluation in C++" |
||||||
|
// define the help option
|
||||||
|
<<'h' |
||||||
|
// shift in the command line arguments
|
||||||
|
<<argc<<argv; |
||||||
|
... |
||||||
|
// example usage of simple option
|
||||||
|
if (mrw::Args::instance().find('v')) // be verbose here
|
||||||
|
... |
||||||
|
// example usage of option with (one) parameter
|
||||||
|
ifstream file(mrw::Args::instance().find('o').toString().c_str()); |
||||||
|
... |
||||||
|
return 0 |
||||||
|
} |
||||||
|
} catch (mrw::exception& x) { |
||||||
|
// trace error, print help or mention option -h
|
||||||
|
} |
||||||
|
} |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
//@{
|
||||||
|
|
||||||
|
/** @brief List of additional (mandatory) parameter to one command
|
||||||
|
line argument. |
||||||
|
@pre #include<mrw/arg.hpp> |
||||||
|
|
||||||
|
A new mandatory parameter is added to the list of parameter, by |
||||||
|
shifting the default value into the instance of @c |
||||||
|
mrw::Param. E.g. add a string, that defaults to @c "noname", an |
||||||
|
integer, that defaults to @c 4, another integer that defaults to |
||||||
|
@2 and a boolean that defaults to @c "true": |
||||||
|
|
||||||
|
@code |
||||||
|
// if you need the instance as variable:
|
||||||
|
mrw::Param p(); |
||||||
|
p<<"noname"<<4<<2<<true; |
||||||
|
// or in an expression:
|
||||||
|
mrw::Opt o('e', "--example", mrw::Param()<<"noname"<<4<<2<<true, ""); |
||||||
|
@endcode |
||||||
|
|
||||||
|
To access a value at a given position, simply use @c |
||||||
|
operator[]. Then use @c mrw::Value::toString, @c |
||||||
|
mrw::Value::toInt or @c mrw::Value::toBool to get the value of |
||||||
|
that parameter. Of course yo must know the correct type of a |
||||||
|
parameter at a given position, but since you are the programmer |
||||||
|
you know it, or you can get it by running your program with the |
||||||
|
help option, mostly @c -h. To retrieve the parameters setup in |
||||||
|
the example above (connected to option @c -e or @c --example), |
||||||
|
either the default value, or the value overwritten by the user, |
||||||
|
simply type: |
||||||
|
|
||||||
|
@code |
||||||
|
mrw::Args& args = mrw::Args::instance(); |
||||||
|
std::string theString = args[0]->toString(); |
||||||
|
int firstInteger = args[1]->toInt(); |
||||||
|
int secondInteger = args[2]->toInt(); |
||||||
|
bool theBoolean = args[3]->toBool(); |
||||||
|
@endcode |
||||||
|
|
||||||
|
@section argParts Setup Command Line from Different Program Parts |
||||||
|
|
||||||
|
If your software is large and splitted into different parts (or |
||||||
|
sub projects or modules, ...), all with their own parameter, you |
||||||
|
can use the following trick: Statical variables are initialized |
||||||
|
before the @c main() function is called. |
||||||
|
|
||||||
|
In part Abc write in a code file (not in a header file): |
||||||
|
|
||||||
|
@code |
||||||
|
class AbcArguments { |
||||||
|
public: |
||||||
|
AbcArguments() { |
||||||
|
mrw::Args::instance() |
||||||
|
<<mrw::Opt('n', "--name", mrw::Param()<<"MRW", "name of the user") |
||||||
|
<<mrw::Opt('o', "--output-file", mrw::Param()<<"", "file to load") |
||||||
|
<<mrw::Opt('c', "--coordinates", mrw::Param()<<0<<0, |
||||||
|
"X, Y coordinate") |
||||||
|
<<"Description text for part Abc, will be added to the\n" |
||||||
|
"overall documentation"; |
||||||
|
} |
||||||
|
}; |
||||||
|
static AbcArgument abcArgumentInitializer; |
||||||
|
@endcode |
||||||
|
|
||||||
|
Do the same for all other parts Then the @c main() function reduces to: |
||||||
|
|
||||||
|
@code |
||||||
|
int main(int argc, const char * const * const argv) { |
||||||
|
// set the help and evaluate the user given arguments
|
||||||
|
mrw::Args::instance() |
||||||
|
<<mrw::Opt('h', "--help", "Show this help text") |
||||||
|
<<'h'<<argc<<argv; |
||||||
|
... |
||||||
|
} |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
class Param { |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
/** @brief Abstract base class to represent one single parameter value.
|
||||||
|
@pre #include<mrw/arg.hpp> |
||||||
|
*/ |
||||||
|
class Value { |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
virtual ~Value() {} |
||||||
|
|
||||||
|
/** @brief If the instance is a @c std::string, return that
|
||||||
|
string, otherwise throw an exception. |
||||||
|
@throw mrw::bad_cast if the instance is not a string |
||||||
|
@return the string, if the instance is a string |
||||||
|
*/ |
||||||
|
virtual const std::string& toString() const throw(mrw::exception) { |
||||||
|
throw mrw::bad_cast(); |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief If the instance is an @c int, return that integer,
|
||||||
|
otherwise throw an exception. |
||||||
|
@throw mrw::bad_cast if the instance is not a integer |
||||||
|
@return the integer, if the instance is a integer |
||||||
|
*/ |
||||||
|
virtual int toInt() const throw(mrw::exception) { |
||||||
|
throw mrw::bad_cast(); |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief If the instance is an @c bool, return that boolean,
|
||||||
|
otherwise throw an exception. |
||||||
|
@note the following typings are converted to @c true: |
||||||
|
- true |
||||||
|
- yes |
||||||
|
- on |
||||||
|
Everything else is converted to @c false. |
||||||
|
@throw mrw::bad_cast if the instance is not a boolean |
||||||
|
@return the boolean, if the instance is a boolean |
||||||
|
*/ |
||||||
|
virtual bool toBool() const throw(mrw::exception) { |
||||||
|
throw mrw::bad_cast(); |
||||||
|
} |
||||||
|
|
||||||
|
/// @brief returns a printable representation of the value
|
||||||
|
virtual std::string printable() const throw(mrw::bad_exception) = 0; |
||||||
|
|
||||||
|
/// @brief returns a printable typename of the value
|
||||||
|
virtual const std::string& typestr() const throw(mrw::bad_exception)=0; |
||||||
|
|
||||||
|
protected: |
||||||
|
friend class Args; // allow assign for Param
|
||||||
|
virtual void operator=(const std::string&) throw(mrw::exception) = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
class StringValue: public Value { |
||||||
|
public: |
||||||
|
virtual ~StringValue() {} |
||||||
|
StringValue(const std::string& s) throw(mrw::bad_exception): _s(s) { |
||||||
|
} |
||||||
|
virtual const std::string& toString() const throw(mrw::exception) { |
||||||
|
return _s; |
||||||
|
} |
||||||
|
virtual const std::string& typestr() const throw(mrw::bad_exception) { |
||||||
|
static std::string name("string"); |
||||||
|
return name; |
||||||
|
} |
||||||
|
virtual std::string printable() const throw(mrw::bad_exception) { |
||||||
|
return _s; |
||||||
|
} |
||||||
|
protected: |
||||||
|
virtual void operator=(const std::string& s) throw(mrw::exception) { |
||||||
|
_s = s; |
||||||
|
} |
||||||
|
private: |
||||||
|
std::string _s; |
||||||
|
}; |
||||||
|
|
||||||
|
class IntValue: public Value { |
||||||
|
public: |
||||||
|
virtual ~IntValue() {} |
||||||
|
IntValue(int i) throw(mrw::bad_exception): _i(i) { |
||||||
|
} |
||||||
|
virtual int toInt() const throw(mrw::exception) { |
||||||
|
return _i; |
||||||
|
} |
||||||
|
virtual const std::string& typestr() const throw(mrw::bad_exception) { |
||||||
|
static std::string name("integer"); |
||||||
|
return name; |
||||||
|
} |
||||||
|
virtual std::string printable() const throw(mrw::bad_exception) { |
||||||
|
return ((std::stringstream&)(std::stringstream()<<_i)).str(); |
||||||
|
} |
||||||
|
protected: |
||||||
|
virtual void operator=(const std::string& s) throw(mrw::exception) { |
||||||
|
if (!(std::stringstream(s)>>_i)) throw mrw::bad_cast(); |
||||||
|
} |
||||||
|
private: |
||||||
|
int _i; |
||||||
|
}; |
||||||
|
|
||||||
|
class BoolValue: public Value { |
||||||
|
public: |
||||||
|
virtual ~BoolValue() {} |
||||||
|
BoolValue(bool b) throw(mrw::bad_exception): _b(b) { |
||||||
|
} |
||||||
|
virtual bool toBool() const throw(mrw::exception) { |
||||||
|
return _b; |
||||||
|
} |
||||||
|
virtual const std::string& typestr() const throw(mrw::bad_exception) { |
||||||
|
static std::string name("boolean (\"yes\" or \"no\")"); |
||||||
|
return name; |
||||||
|
} |
||||||
|
virtual std::string printable() const throw(mrw::bad_exception) { |
||||||
|
return _b?"yes":"no"; |
||||||
|
} |
||||||
|
protected: |
||||||
|
virtual void operator=(const std::string& s) throw(mrw::exception) { |
||||||
|
_b = s=="true" || s=="yes" || s=="on"; |
||||||
|
} |
||||||
|
private: |
||||||
|
bool _b; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef std::vector< mrw::SmartPointer<Value> > Params; |
||||||
|
Params _params; |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
/// @brief returns the number of (mandatory) parameter
|
||||||
|
int size() const throw(std::bad_exception) { |
||||||
|
return _params.size(); |
||||||
|
} |
||||||
|
|
||||||
|
/// @brief add one more mandatory string parameter
|
||||||
|
Param& operator<<(const char* const s) throw(mrw::bad_exception) { |
||||||
|
_params.push_back(new StringValue(s)); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
/// @brief add one more mandatory string parameter
|
||||||
|
Param& operator<<(const std::string& s) throw(mrw::bad_exception) { |
||||||
|
_params.push_back(new StringValue(s)); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
/// @brief add one more mandatory integer parameter
|
||||||
|
Param& operator<<(int i) throw(mrw::bad_exception) { |
||||||
|
_params.push_back(new IntValue(i)); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
// @brief add one more mandatory boolean parameter
|
||||||
|
Param& operator<<(bool b) throw(mrw::bad_exception) { |
||||||
|
_params.push_back(new BoolValue(b)); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief get parameter number @i
|
||||||
|
@throw mrw::out_of_range if @c i is too big */ |
||||||
|
const mrw::SmartPointer<Value>& operator[](unsigned int i) const |
||||||
|
throw(mrw::exception) { |
||||||
|
if (i<_params.size()) return _params[i]; |
||||||
|
throw mrw::out_of_range |
||||||
|
(((std::stringstream&) |
||||||
|
(std::stringstream()<<"check failed: " |
||||||
|
<<i<<'<'<<_params.size())).str()); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
friend class Args; // allow set
|
||||||
|
mrw::SmartPointer<Value>& setable(unsigned int i) |
||||||
|
throw(mrw::exception) { |
||||||
|
if (i<_params.size()) return _params[i]; |
||||||
|
throw mrw::out_of_range |
||||||
|
(((std::stringstream&) |
||||||
|
(std::stringstream()<<"check failed: " |
||||||
|
<<i<<'<'<<_params.size())).str()); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
/** @brief this class represents one command line option
|
||||||
|
@pre #include<mrw/arg.hpp> |
||||||
|
|
||||||
|
The library user needs this class when setting up the list of |
||||||
|
supported command line ooptions: Simply shift one instance of @c |
||||||
|
mrw::Opt per supported command line option into @c |
||||||
|
mrw::Args::instance(), e.g.: |
||||||
|
|
||||||
|
@code |
||||||
|
mrw::Args::instance() |
||||||
|
<<mrw::Opt('h', "--help", "Show this help text"); |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
class Opt { |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
/** @brief create an @c mrw::Opt with additional parameter
|
||||||
|
@param shortname short name of the option |
||||||
|
@param longname long name of the option, must start with "--" |
||||||
|
@param param the additional parameter |
||||||
|
@param help the help string for this option |
||||||
|
*/ |
||||||
|
Opt::Opt(const char shortname, const std::string& longname, |
||||||
|
const Param& param, const std::string& help) |
||||||
|
throw(mrw::bad_exception): |
||||||
|
_set(false), _shortname(shortname), _longname(longname), |
||||||
|
_param(param), _help(help) { |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief create a simple @c mrw::Opt
|
||||||
|
|
||||||
|
This option is either set or not set, there are no additional |
||||||
|
parameter. |
||||||
|
|
||||||
|
@param shortname short name of the option |
||||||
|
@param longname long name of the option, must start with "--" |
||||||
|
@param help the help string for this option |
||||||
|
*/ |
||||||
|
Opt::Opt(const char shortname, const std::string& longname, |
||||||
|
const std::string& help) |
||||||
|
throw(mrw::bad_exception): |
||||||
|
_set(false), _shortname(shortname), _longname(longname), |
||||||
|
_help(help) { |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief get the help text for this option */ |
||||||
|
const std::string& help() const throw(mrw::bad_exception) { |
||||||
|
return _help; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief find out, whether this option was set by the user
|
||||||
|
|
||||||
|
Example: Check whether the user has set the @c -v option for |
||||||
|
verbose output: |
||||||
|
|
||||||
|
@code |
||||||
|
if (mrw::Args::instance().find('v')) // -v is set
|
||||||
|
@endcode |
||||||
|
|
||||||
|
@return |
||||||
|
- @c true if the user has started the program with this option |
||||||
|
- @c false if the user has not set this option |
||||||
|
*/ |
||||||
|
operator bool() const throw(std::bad_exception) {return _set;} |
||||||
|
|
||||||
|
/** @brief get one of the additional parameter
|
||||||
|
|
||||||
|
If this option has additional parameter, get the @c i-th of them. |
||||||
|
|
||||||
|
@throw mrw::out_of_range if @c i is too big |
||||||
|
@param i number of the additional parameter to get (starting with @c 0) |
||||||
|
@return a smart pointer to the value (default or given by the user) |
||||||
|
*/ |
||||||
|
const mrw::SmartPointer<Param::Value>& operator[](unsigned int i) const |
||||||
|
throw(mrw::exception) { |
||||||
|
return _param[i]; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
friend class Args; // is allowed to set values
|
||||||
|
void set() const throw(mrw::bad_exception) { |
||||||
|
_set = true; |
||||||
|
} |
||||||
|
Param& args() const throw(mrw::bad_exception) { |
||||||
|
return _param; |
||||||
|
} |
||||||
|
mutable bool _set; |
||||||
|
char _shortname; |
||||||
|
std::string _longname; |
||||||
|
mutable Param _param; |
||||||
|
std::string _help; |
||||||
|
}; |
||||||
|
|
||||||
|
/** @brief handle command line arguments
|
||||||
|
@pre #include<mrw/arg.hpp> |
||||||
|
|
||||||
|
This class handles command line arguments. It is a |
||||||
|
singleton. Get the one and only instance of this class with @c |
||||||
|
mrw::Args::instance(). It is setup by shifting values into this |
||||||
|
class. The order is important, be sure that you shift in @c argc |
||||||
|
and @c argv as last parameters. |
||||||
|
|
||||||
|
Example setup: |
||||||
|
|
||||||
|
@code |
||||||
|
mrw::Args::instance() |
||||||
|
<<mrw::Opt('h', "--help", "Show this help text") |
||||||
|
<<mrw::Opt('v', "--verbose", "print more information") |
||||||
|
<<"This is a testprogram for argument evaluation in C++" |
||||||
|
<<'h'<<argc<<argv; |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
class Args { |
||||||
|
|
||||||
|
public: |
||||||
|
|
||||||
|
typedef std::list<std::string> OtherArgs; |
||||||
|
|
||||||
|
/// @brief get the one and only instance
|
||||||
|
static Args& instance() throw(mrw::bad_exception) { // singleton
|
||||||
|
static Args _instance; |
||||||
|
return _instance; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief setup an acceptable option
|
||||||
|
|
||||||
|
Setup an acceptable user option. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
@code |
||||||
|
mrw::Args::instance() |
||||||
|
<<mrw::Opt('v', "--verbose", "print more information"); |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
Args& operator<<(const mrw::Opt& opt) throw(mrw::invalid_argument) { |
||||||
|
// twice the same, but other sort order
|
||||||
|
Options::iterator it(_options.insert(_options.end(), opt)); |
||||||
|
if (!_shortopts.insert(ShortOpts::value_type(opt._shortname, it)).second) |
||||||
|
throw mrw::invalid_argument(std::string(1, opt._shortname)); |
||||||
|
if (!_longopts.insert(LongOpts::value_type(opt._longname, it)).second) |
||||||
|
throw mrw::invalid_argument(opt._longname); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief setup the number of arguments
|
||||||
|
|
||||||
|
Setup the number of arguments. |
||||||
|
This must be done before @c argv is shifted in. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
@code |
||||||
|
int main(int argv, const char * const * const argv) { |
||||||
|
mrw::Args::instance()<<argc<<argv; |
||||||
|
... |
||||||
|
} |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
Args& operator<<(int argc) throw(mrw::bad_exception) { |
||||||
|
_argc = argc; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief setup the C array of command line arguments
|
||||||
|
|
||||||
|
Setup the C array of command line arguments. This must be the |
||||||
|
very last thing shifted in. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
@code |
||||||
|
int main(int argv, const char * const * const argv) { |
||||||
|
mrw::Args::instance()<<argc<<argv; |
||||||
|
... |
||||||
|
} |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
Args& operator<<(const char *const*const argv) throw(mrw::exception) { |
||||||
|
if (_argc<0) |
||||||
|
throw mrw::invalid_argument("argc was not set when shifting argv"); |
||||||
|
return parse(_argc, argv); |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief add a description text
|
||||||
|
|
||||||
|
Add a description text. This description text is shown in the |
||||||
|
@c DESCRIPTION section of the help display. If the description |
||||||
|
text is shifted in more then once, the different sections are |
||||||
|
appended with new line and an empty line between. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
@code |
||||||
|
mrw::Args::instance()<<"this is a description for --help"; |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
Args& operator<<(const std::string& description) throw(mrw::exception) { |
||||||
|
if (_description=="") |
||||||
|
_description = description; |
||||||
|
else |
||||||
|
_description += "\n\n"+description; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief set the help option
|
||||||
|
|
||||||
|
Define which option prints the help text. There is no code |
||||||
|
needed for printing the help text: if the help option has been |
||||||
|
shifted in, help is printed automatically at user request, |
||||||
|
then the program is terminated. Only specify the short option |
||||||
|
name, the long option name is known. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
@code |
||||||
|
mrw::Args::instance()<<'h'; |
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
Args& operator<<(char help) throw(mrw::exception) { |
||||||
|
_help = help; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief get an option, given the short option name
|
||||||
|
@throw mrw::out_of_range if the option does not exist |
||||||
|
*/ |
||||||
|
const Opt& find(char c) const throw(mrw::exception) { |
||||||
|
ShortOpts::const_iterator it(_shortopts.find(c)); |
||||||
|
if (it==_shortopts.end()) throw mrw::out_of_range(std::string(1, c)); |
||||||
|
return *it->second; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief get an option, given the long option name
|
||||||
|
@throw mrw::out_of_range if the option does not exist |
||||||
|
*/ |
||||||
|
const Opt& find(const std::string& s) const throw(mrw::exception) { |
||||||
|
LongOpts::const_iterator it(_longopts.find(s)); |
||||||
|
if (it==_longopts.end()) throw mrw::out_of_range(s); |
||||||
|
return *it->second; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief get all non interpreted options
|
||||||
|
|
||||||
|
All user options that don't fit the defined and interpreted |
||||||
|
options. The meaning for this is, that a user may append, |
||||||
|
e.g. a list of file names. |
||||||
|
*/ |
||||||
|
const OtherArgs& otherArgs() { |
||||||
|
return _otherargs; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief get the file name of the executable, that's @c argv[0] */ |
||||||
|
const std::string& filename() throw(mrw::bad_exception) { |
||||||
|
return _filename; |
||||||
|
} |
||||||
|
|
||||||
|
/** @brief print the help text, then exit */ |
||||||
|
void help() { |
||||||
|
std::cout<<"USAGE: "<<std::endl |
||||||
|
<<" "<<_filename<<" [ OPTIONS ]"<<std::endl |
||||||
|
<<"OPTIONS:"<<std::endl; |
||||||
|
for (Options::iterator it(_options.begin()); it!=_options.end(); ++it) { |
||||||
|
std::cout<<" -"<<it->_shortname<<" | "<<it->_longname; |
||||||
|
for (int i(0); i<it->_param.size(); ++i) |
||||||
|
std::cout<<" <"<<(*it)[i]->typestr()<<">"; |
||||||
|
if (it->_param.size()>0) std::cout<<" (default: "; |
||||||
|
for (int i(0); i<it->_param.size()-1; ++i) |
||||||
|
std::cout<<(*it)[i]->printable()<<" "; |
||||||
|
if (it->_param.size()>0) |
||||||
|
std::cout<<(*it)[it->_param.size()-1]->printable()<<")"; |
||||||
|
std::cout<<std::endl<<" "<<it->help()<<std::endl; |
||||||
|
} |
||||||
|
if (_description.size()>0) |
||||||
|
std::cout<<"DESCRIPTION:"<<std::endl |
||||||
|
<<_description<<std::endl; |
||||||
|
exit(0); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
Args& parse(int argc, const char*const*const argv) throw(mrw::exception) { |
||||||
|
if (argc>0) _filename = argv[0]; |
||||||
|
for (int i(1); i<argc; ++i) { |
||||||
|
std::string arg(argv[i]); |
||||||
|
if (arg.find("--")==0 && arg!="--") { // long arguments
|
||||||
|
LongOpts::iterator it(_longopts.find(arg)); |
||||||
|
if (it!=_longopts.end() || i+it->second->args().size()>=argc) |
||||||
|
throw mrw::invalid_argument(arg); |
||||||
|
it->second->set(); |
||||||
|
for (int j(0), l(it->second->args().size()); j<l; ++j) { |
||||||
|
*(it->second->args().setable(j)) = argv[++i]; |
||||||
|
} |
||||||
|
} else if (arg.find("-")==0) { // short arguments
|
||||||
|
// first check all, then set all
|
||||||
|
for (int j(1), l(arg.size()); j<l; ++j) { |
||||||
|
ShortOpts::iterator it(_shortopts.find(arg[j])); |
||||||
|
if (it==_shortopts.end() || it->second->args().size()>0 && |
||||||
|
(j+1!=l || i+it->second->args().size()>=argc)) |
||||||
|
throw mrw::invalid_argument(arg); |
||||||
|
} |
||||||
|
for (int j(1), l(arg.size()); j<l; ++j) { |
||||||
|
ShortOpts::iterator it(_shortopts.find(arg[j])); |
||||||
|
it->second->set(); |
||||||
|
if (j+1==l && it->second->args().size()>0) { |
||||||
|
for (int k(0); k < it->second->args().size(); ++k) { |
||||||
|
*(it->second->args().setable(k)) = argv[++i]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (arg!="--") _otherargs.push_back(arg); |
||||||
|
while (++i<argc) _otherargs.push_back(argv[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
if (_help && find(_help)) help(); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
typedef std::list<Opt> Options; |
||||||
|
typedef std::map<char, Options::iterator> ShortOpts; |
||||||
|
typedef std::map<std::string, Options::iterator> LongOpts; |
||||||
|
Args(): _argc(-1), _help(0) {} // singleton
|
||||||
|
Args& operator=(const Args&); // singleton, not implemented
|
||||||
|
std::string _filename; |
||||||
|
Options _options; |
||||||
|
ShortOpts _shortopts; |
||||||
|
LongOpts _longopts; |
||||||
|
OtherArgs _otherargs; |
||||||
|
int _argc; |
||||||
|
char _help; |
||||||
|
std::string _description; |
||||||
|
}; |
||||||
|
|
||||||
|
//@}
|
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
#include <mrw/smartpointer.hpp> |
||||||
|
|
||||||
|
class A { |
||||||
|
public: int a; |
||||||
|
protected: virtual void fn() {} // dynamic_cast requires a virtual function
|
||||||
|
}; |
||||||
|
|
||||||
|
class B: virtual public A { |
||||||
|
public: int b; |
||||||
|
}; |
||||||
|
|
||||||
|
int main(int, char**) { |
||||||
|
utl::SmartPointer<int> i1 = new int; |
||||||
|
*i1 = 13; |
||||||
|
utl::SmartPointer<int> i2 = i1; |
||||||
|
utl::SmartPointer<int> i3; |
||||||
|
if (!i3) i3 = i2; |
||||||
|
*i2 = 666; // *i1 is now 666 too
|
||||||
|
utl::SmartPointer<A> b1 = new B; |
||||||
|
utl::SmartPointer<B> b2 = b1; // b1 and b2 point to the same instance
|
||||||
|
b1->a = 0; // b1->b does not compile
|
||||||
|
b2->a = 1; |
||||||
|
b2->b = 2; |
||||||
|
return 0; |
||||||
|
} // memory is automatically freed
|
@ -0,0 +1,72 @@ |
|||||||
|
#ifndef __MRW_SIMPLETRACE_HPP__ |
||||||
|
#define __MRW_SIMPLETRACE_HPP__ |
||||||
|
#include <iostream> |
||||||
|
#include <iomanip> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
// GENERIC TRACER FOR TESTS ---------------------------------------------------
|
||||||
|
// (without file/line)
|
||||||
|
#ifndef __GNUG__ |
||||||
|
#define METHOD(name) mrw::FnTrace fnTrace(this, name) |
||||||
|
#define FUNCTION(name) mrw::FnTrace fnTrace(0, name) |
||||||
|
#else |
||||||
|
#define METHOD mrw::FnTrace fnTrace(this, __FUNCTION__) |
||||||
|
#define FUNCTION mrw::FnTrace fnTrace(0, __FUNCTION__) |
||||||
|
#endif |
||||||
|
#define CALL(name) fnTrace.call(name) |
||||||
|
#define TRACE(name) fnTrace.trace(name) |
||||||
|
#define TRACE_OFF mrw::FnTrace::off() |
||||||
|
#define TRACE_ON mrw::FnTrace::on() |
||||||
|
#define NO_TRACE mrw::NoTrace noTrace; |
||||||
|
|
||||||
|
namespace mrw { |
||||||
|
class FnTrace { |
||||||
|
public: |
||||||
|
FnTrace(const void* addr, const std::string& name): |
||||||
|
_addr(addr), _name(name) { |
||||||
|
if (_off==0) |
||||||
|
std::cout<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec |
||||||
|
<<std::setw(2+_level)<<std::setfill(' ')<<"\\ " |
||||||
|
<<_name<<std::endl; |
||||||
|
++_level; |
||||||
|
} |
||||||
|
~FnTrace() { |
||||||
|
--_level; |
||||||
|
if (_off==0) |
||||||
|
std::cout<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec |
||||||
|
<<std::setw(2+_level)<<std::setfill(' ')<<"/ "<<_name |
||||||
|
<<std::endl; |
||||||
|
} |
||||||
|
void call(const std::string& name) { |
||||||
|
if (_off==0) |
||||||
|
std::cout<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec |
||||||
|
<<std::setw(4+_level)<<std::setfill(' ')<<" -> "<<name |
||||||
|
<<std::endl; |
||||||
|
} |
||||||
|
void trace(const std::string& name) { |
||||||
|
if (_off==0) |
||||||
|
std::cout<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec |
||||||
|
<<std::setw(4+_level)<<std::setfill(' ')<<" **** "<<name |
||||||
|
<<" **** "<<std::endl; |
||||||
|
} |
||||||
|
static void off() { |
||||||
|
++_off; |
||||||
|
} |
||||||
|
static void on() { |
||||||
|
if (_off>0) --_off; |
||||||
|
} |
||||||
|
private: |
||||||
|
const void* _addr; |
||||||
|
const std::string _name; |
||||||
|
static unsigned int _level; |
||||||
|
static unsigned int _off; |
||||||
|
}; |
||||||
|
unsigned int FnTrace::_level(0); |
||||||
|
unsigned int FnTrace::_off(0); |
||||||
|
class NoTrace { |
||||||
|
public: |
||||||
|
NoTrace() {TRACE_OFF;} |
||||||
|
~NoTrace() {TRACE_ON;} |
||||||
|
}; |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,137 @@ |
|||||||
|
#ifndef __MRW__SMARTPOINTER_HPP__ |
||||||
|
#define __MRW__SMARTPOINTER_HPP__ |
||||||
|
|
||||||
|
namespace mrw { |
||||||
|
|
||||||
|
class PointerCounter { |
||||||
|
private: |
||||||
|
unsigned int _cnt; |
||||||
|
public: |
||||||
|
PointerCounter(): |
||||||
|
_cnt(1) { |
||||||
|
} |
||||||
|
PointerCounter* incr() { |
||||||
|
++_cnt; |
||||||
|
return this; |
||||||
|
} |
||||||
|
int decr() { |
||||||
|
return --_cnt; |
||||||
|
} |
||||||
|
int get() { |
||||||
|
return _cnt; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
class SmartPointerParent { |
||||||
|
protected: |
||||||
|
template<class TYPE> |
||||||
|
PointerCounter* getCounter(TYPE& sp) { |
||||||
|
return sp._cnt; |
||||||
|
} |
||||||
|
template<class TYPE> |
||||||
|
typename TYPE::Pointer getPointer(TYPE& sp) { |
||||||
|
return sp._ptr; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/** @addtogroup AutoTools */ |
||||||
|
//@{
|
||||||
|
|
||||||
|
/** @brief Smart Pointer Implementation
|
||||||
|
@pre #include <mrw/smartpointer.hpp> |
||||||
|
|
||||||
|
This is a smart pointer that can be casted withing the |
||||||
|
inheritance of the pointer it is storing. |
||||||
|
|
||||||
|
A smart pointer is a pointer that is automatically cleaned up |
||||||
|
when it is no more referenced. Therefore you only allocate |
||||||
|
memory, but you never free it, just as in Java, cleaning up is |
||||||
|
done behind the scenes. If you assign a smart pointer to another |
||||||
|
smart pointer, both point to the same memory. A counter counts |
||||||
|
the number of references to the object and frees it as soon as |
||||||
|
the last smart pointer pointing to the same memory has been |
||||||
|
destroyed. |
||||||
|
|
||||||
|
A smart pointer is used just as a normal pointer, except that |
||||||
|
you can assign it only to other smart pointers, not to ordinary |
||||||
|
pointers, and you don't have to delete the memory allocated. Any |
||||||
|
memory assigned to a smart pointer is consumed and mustn't be |
||||||
|
used any more. Never allocate a smart pointer with |
||||||
|
<code>new</code>, but only the pointer that is stored in the |
||||||
|
smart pointer! |
||||||
|
|
||||||
|
@note memory assigned to a smart pointer is consumed |
||||||
|
*/
|
||||||
|
template<class TYPE> class SmartPointer: public SmartPointerParent { |
||||||
|
private: |
||||||
|
typedef TYPE* Pointer; |
||||||
|
PointerCounter* _cnt; |
||||||
|
TYPE* _ptr; |
||||||
|
private: |
||||||
|
void drop() { |
||||||
|
if (_cnt && !_cnt->decr()) { |
||||||
|
delete _cnt; _cnt=0; |
||||||
|
delete _ptr; _ptr=0; |
||||||
|
} |
||||||
|
} |
||||||
|
private: |
||||||
|
friend class SmartPointerParent; |
||||||
|
friend class SmartPointerTest; |
||||||
|
public: |
||||||
|
SmartPointer(): |
||||||
|
_cnt(0), _ptr(0) { |
||||||
|
} |
||||||
|
SmartPointer(const SmartPointer<TYPE>& o): |
||||||
|
_cnt(o._cnt?o._cnt->incr():0), _ptr(o._ptr) { |
||||||
|
} |
||||||
|
SmartPointer(TYPE* ptr): |
||||||
|
_cnt(ptr ? new PointerCounter : 0), _ptr(ptr) { |
||||||
|
} |
||||||
|
template<class OTHER> SmartPointer(const SmartPointer<OTHER>& o): |
||||||
|
_cnt(0), _ptr(dynamic_cast<TYPE*>(getPointer(o))) { |
||||||
|
if (_ptr) _cnt = getCounter(o)->incr(); |
||||||
|
} |
||||||
|
~SmartPointer() { |
||||||
|
drop(); |
||||||
|
} |
||||||
|
SmartPointer& operator=(const SmartPointer<TYPE>& o) { |
||||||
|
if (o._ptr==_ptr) return *this; |
||||||
|
drop(); |
||||||
|
_cnt = o._cnt ? o._cnt->incr() : 0; |
||||||
|
_ptr = o._ptr; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
SmartPointer& operator=(TYPE* ptr) { |
||||||
|
if (ptr==_ptr) return *this; |
||||||
|
drop(); |
||||||
|
_cnt = ptr ? new PointerCounter : 0; |
||||||
|
_ptr = ptr; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
template<class OTHER> |
||||||
|
SmartPointer& operator=(const SmartPointer<OTHER>& o) { |
||||||
|
if (getPointer(o)==_ptr) return *this; |
||||||
|
drop(); |
||||||
|
_ptr = dynamic_cast<TYPE*>(getPointer(o)); |
||||||
|
_cnt = _ptr ? getCounter(o)->incr() : 0; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
TYPE& operator*() { |
||||||
|
return *_ptr; |
||||||
|
} |
||||||
|
const TYPE& operator*() const { |
||||||
|
return *_ptr; |
||||||
|
} |
||||||
|
TYPE* const operator->() { |
||||||
|
return _ptr; |
||||||
|
} |
||||||
|
const TYPE* const operator->() const { |
||||||
|
return _ptr; |
||||||
|
} |
||||||
|
operator bool() { |
||||||
|
return _ptr!=0; |
||||||
|
} |
||||||
|
}; |
||||||
|
//@}
|
||||||
|
} |
||||||
|
#endif |
Loading…
Reference in new issue