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