/******************************************************************** ** Image Component Library (ICL) ** ** ** ** Copyright (C) 2006-2010 CITEC, University of Bielefeld ** ** Neuroinformatics Group ** ** Website: www.iclcv.org and ** ** http://opensource.cit-ec.de/projects/icl ** ** ** ** File : include/ICLUtils/ProgArg.h ** ** Module : ICLUtils ** ** Authors: Christof Elbrechter, Andre Justus ** ** ** ** ** ** Commercial License ** ** ICL can be used commercially, please refer to our website ** ** www.iclcv.org for more details. ** ** ** ** GNU General Public License Usage ** ** Alternatively, this file may be used under the terms of the ** ** GNU General Public License version 3.0 as published by the ** ** Free Software Foundation and appearing in the file LICENSE.GPL ** ** included in the packaging of this file. Please review the ** ** following information to ensure the GNU General Public License ** ** version 3.0 requirements will be met: ** ** http://www.gnu.org/copyleft/gpl.html. ** ** ** ** The development of this software was supported by the ** ** Excellence Cluster EXC 277 Cognitive Interaction Technology. ** ** The Excellence Cluster EXC 277 is a grant of the Deutsche ** ** Forschungsgemeinschaft (DFG) in the context of the German ** ** Excellence Initiative. ** ** ** *********************************************************************/ #ifndef ICL_PROGARG_H #define ICL_PROGARG_H #include #include #include namespace icl{ /// Programm argument environment exception type \ingroup PA \ingroup EXCEPT struct ProgArgException : public ICLException{ ProgArgException(const std::string &func, const std::string &what): ICLException(func+":"+what){} }; #define THROW_ProgArgException(X) throw ProgArgException(__FUNCTION__,(X)) /** \cond */ // internally used programm argument data type class ProgArgData{ protected: friend const std::string &pasubarg_internal(const ProgArgData &pa) throw (ProgArgException); friend bool padefined_internal(const ProgArgData &pa) throw (ProgArgException); std::string id; int subargidx; bool danglingOnly; inline ProgArgData(const std::string &id, int subargidx): id(id),subargidx((int)subargidx){ } inline ProgArgData(unsigned int idx, bool danglingOnly): subargidx(idx),danglingOnly(danglingOnly){ } }; /** \endcond */ /** \cond */ // internal function for program argument explanation void paex_internal(const std::string &pa, const std::string &ex); // internal sub-argument access function const std::string &pasubarg_internal(const ProgArgData &pa) throw (ProgArgException); // another internal helper function bool padefined_internal(const ProgArgData &pa) throw (ProgArgException); /** \endcond */ /// Programm argument utility class \ingroup PA /** @see icl::pa(const std::string&,unsigned int) */ class ProgArg : public ProgArgData{ /// private constructor /** Use the functions icl::pa(const std::string&,unsigned int) and icl::pa(unsigned int,bool) to create an instance of this class in order to access program arguments*/ inline ProgArg(const std::string &id, unsigned int subargidx): ProgArgData(id,subargidx){ } inline ProgArg(unsigned int idx, bool danglingOnly): ProgArgData(idx,danglingOnly){ } public: /** \cond */ // undocumented friend friend const ProgArg pa(const std::string &,unsigned int) throw (ProgArgException); // undocumented friend friend const ProgArg pa(unsigned int,bool); // yet another one friend bool padefined_internal(const ProgArgData &pa) throw (ProgArgException); /** \endcond */ /// returns the count of actually given sub arguments /** If this argument was not given, this function returns 0.*/ int n() const throw (ProgArgException); /// this is the main conversion function. It returns the associated sub argument as given T /** If T is bool, this operator returns whether the arg was given rather than its value */ template inline operator T() const throw (ProgArgException){ return parse(pasubarg_internal(*this)); } /// this template function can be used to explicitly cast a program argument into a given type template inline T as() const throw (ProgArgException){ return parse(pasubarg_internal(*this)); } /// important convenience operator for using a ProgArg instance as string /** *pa("-x") is the same as pa("-x").as(). However, the first version is much shorter.*/ inline std::string operator*() const throw (ProgArgException){ return pasubarg_internal(*this); } }; /// just puts the referenced argument value as string into the lvalue-stream inline std::ostream &operator<<(std::ostream &s,const ProgArg &pa){ return s << pa.as(); } /** \cond */ // explicit specialization for bool types (returns whether the arg was actually given) template<> inline ProgArg::operator bool() const{ return padefined_internal(*this); } // explicit specialization for bool types (returns whether the arg was actually given) template<> inline bool ProgArg::as() const throw (ProgArgException){ return padefined_internal(*this); } /** \endcond */ /// this allows to check if two progargs are defined \ingroup PA /** this allows you to write: \code if(pa("-size") && pa("-scale")){ ... } \endcode */ inline bool operator&&(const ProgArg &a, const ProgArg &b){ return a.as() && b.as(); } /// allows to check more than two ProgArg instances at once \ingroup PA /** Example: \code if(pa("-size") && pa("-scale") && pa("-format")){ ... } \endcode */ inline bool operator&&(const ProgArg &a, bool b){ return b && a.as(); } /// allows to check more than two ProgArg instances at once \ingroup PA /** Example: \code if(pa("-size") && pa("-scale") && pa("-format")){ ... } \endcode */ inline bool operator&&(bool &b, const ProgArg &a){ return b && a.as(); } /// this allows to check if either of two progargs are defined \ingroup PA /** this allows you to write: \code if(pa("-size") || pa("-scale")){ ... } \endcode */ inline bool operator||(const ProgArg &a, const ProgArg &b){ return a.as() || b.as(); } /// allows to check if either of more than two ProgArg instances is defined \ingroup PA /** Example: \code if(pa("-size") || pa("-scale") || pa("-format")){ ... } \endcode */ inline bool operator||(const ProgArg &a, bool b){ return b || a.as(); } /// allows to check if either of more than two ProgArg instances is defined \ingroup PA /** Example: \code if(pa("-size") || pa("-scale") || pa("-format")){ ... } \endcode */ inline bool operator||(bool &b, const ProgArg &a){ return b || a.as(); } /// returns given program argument \ingroup PA /** The pa-function is the main interface for extracting information about given program arguments and/or their default values at run-time. The returned icl::ProgArg instance is always automatically parsed from its internal string representation into the expressions lvalue-type (this can easily be implemented with a templated version of the implicit cast operator of a class). Here are some examples: \code painit(n,ppc,"-size|-s(Size=VGA) -index(int) -files(...)"); // extract the first sub-argument of argument // '-index' and convert it into an int value int i = pa("-index"); // extract the first sub-argument of argument '-size' // if '-s' is the shortcut for '-size' Size s = pa("-s); // extract the 2nd sub-argument of argument '-input' // if '-s' is the shortcut for '-size' int = pa("-input",1); // check if argument -size was actually given if(pa("-size")){ ... } // read a list of files from the arbitrary sub-argument // arg '-files'. Note: it is not recommended to use // pa("-files") within a loop, as internally the argument hash- // map must always be searched int nFiles = pa("-files").n(); for(int i=0;i int | std::string Test t((std::string)pa("-x")); // also ambiguous // due to different available std::string constructors Test t(pa("-x").as()); // works, but long code Test t(*pa("-x")); // much shorter, but only for using // a program argument as std::string \endcode @see icl::painit(int,char**,const std::string&,bool) */ inline const ProgArg pa(const std::string &id, unsigned int subargidx=0) throw (ProgArgException){ if(!id.length()) THROW_ProgArgException("invalid programm argument id ''"); return ProgArg(id,subargidx); } /// returns given program argument at given index \ingroup PA /** @see icl::pa(const std::string&,unsigned int) */ inline const ProgArg pa(unsigned int idx, bool danglingOnly=true){ return ProgArg(idx,danglingOnly); } /// utility function that allows to use a default value, if given argument was not defined \ingroup PA template inline const T padef(const std::string &id, unsigned int subargidx, const T &def) throw (ProgArgException){ const ProgArg p = pa(id,subargidx); return p ? parse(p) : def; } /// utility function that allows to use a default value, if given argument was not defined \ingroup PA template inline const T padef(const std::string &id, const T &def) throw (ProgArgException){ return padef(id,0,def); } /// returns number of actually given args given \ingroup PA /** @see icl::pa(const std::string&,unsigned int) */ unsigned int pacount(bool danglingOnly=true); /// returns application name (full command line) /** @param fullpath if this is set to true, the complete first argument of main is returned, which may be something like /usr/bin/icl-create. If fullpath is false, which is default, just the program name is returned. @see icl::pa(const std::string&,unsigned int) */ const std::string &paprogname(bool fullpath=false); /// shows current available programm arguments \ingroup PA void pausage(const std::string &msg=""); /** \cond */ // utility class which allows the user to call the paex-function in a 'stacked' manner struct PAEX{ PAEX operator()(const std::string &pa, const std::string &ex); }; /** \endcond */ /// This function can be used to provide additional information for certain program arguments \ingroup PA /** Due to the use of a special but hidden utility structure called icl::PAEX, this function can be called in a 'stacked' manner. This mean, that the function can be called several times without repeating the function name. Example: \code paex("-size","defines the input image size") ("-input","defines input device id and parameters") ("-format","define input image format"); \endcode @see icl::pa(const std::string&,unsigned int) */ inline PAEX paex(const std::string &pa, const std::string &ex){ paex_internal(pa,ex); return PAEX(); } /** \cond */ // deferred implementation of stacking operator inline PAEX PAEX::operator()(const std::string &pa, const std::string &ex){ return paex(pa,ex); } /** \endcond */ /// initialization function for ICL's program argument evaluation framework \ingroup PA /** painit always receives your program's main-functions arguments as n and ppc. The actual definition of the underlying program argument evaluation framework is given by the init- argument.\n The following rules define the syntax for the init-string:\n - The init string consists of space-separated tokens of single program argument definitions. Necessary space-characters within these tokens must be escaped using a back-slash character. - Each single argument definition token consists of a pipe-separated list of argument name alternatives (e.g. -size|-s followed by an optional parameter list in round braces. - Each argument is allowed to have 0, N or unlimited sub-arguments. - If the argument has no sub arguments it is a flag, which implicitly has the value true if it was given and false otherwise. Zero sub- arguments can be forced by using no parameter list, i.e., no round braces, an empty parameter list () or a parameter list with a zero argument count (0). - An arbitrary sub-argument count can be reached by using the special parameter list (...). - Otherwise, the parameter-list is a comma separated list of type, type=default-value, or argument-count tokens. Examples: - (int,int) defines two integer sub-arguments - (5) defines 5 sub-arguments with no type information - (float,2,int) defines 4 sub-arguments of type float, any, any and int - (int=4,Size=VGA) defines two sub-arguments of type int and Size with given default values 4 and VGA Note: if an int-type is used to define several arguments together, no defaults can be given. If you don't want to define the type, but only the default values, the special type string * should be used e.g., (*=foo,*=bar) Furthermore, it is worth to mention, that the defined types are always just hints for the user. Internally all parameters are treated as strings. Here are some further complete example calls for painit. \code int main(int n, char **ppc){ painit(n,ppc,"-size|-s(Size=VGA) -format|-f(format-string)"); } \endcode \code int main(int n, char **ppc){ painit(n,ppc,"-input|-i(device-type=dc,device-params=0)"); } \endcode \section Dangling Arguments Sometimes, certain arguments shall be used directly without defining arguments and sub-arguments. E.g. if you have a converter application that gets an input and an output file only (e.g., program call: myConvert image.ppm converted-image.ppm rather than something like myConvert -i image.ppm -o converted-image.ppm). Dangling arguments are these arguments that do not match defined arguments or sub-arguments of these. Usually, given arguments that do not match primary arguments or sub-arguments lead to a ProgArgException. You can explicitly allow these dangling arguments by setting allowDanglingArgs to 'true'. \n Dangling arguments can then be obtained simply using the functions icl::pacount and icl::pa(unsigned int,bool). To list all dangling arguments, the followed code can be used: \code for(unsigned int i=0;i