pdf2htmlEX/pdf2htmlEX/src/ArgParser.h

220 lines
5.8 KiB
C++

/*
* A wrapper of getopt
*
* by WangLu
* 2012.09.10
*/
#ifndef ARGPARSER_H__
#define ARGPARSER_H__
#include <string>
#include <vector>
#include <ostream>
#include <sstream>
#include <memory>
#ifndef nullptr
#define nullptr (NULL)
#endif
namespace pdf2htmlEX {
//helper
template<class T>
bool read_value(const char * arg, T * location)
{
std::istringstream sin(arg);
return ((sin >> (*location)) && (sin.eof()));
}
extern bool read_value(const char * arg, char * location);
extern bool read_value(const char * arg, std::string * location);
template<class T>
void dump_value(std::ostream & out, const T & v)
{
out << v;
}
extern void dump_value(std::ostream & out, const std::string & v);
class ArgParser
{
public:
typedef void (*ArgParserCallBack) (const char * arg);
/*
* The 1st is for arguments with callbacks(i.e. flags)
* The 2nd is for arguments linked to variables
*
* optname:
* - if not nullptr, it should be the name of the arg, should be in the format of "<long name>[,<short char>]", e.g. "help,h"
* - if nullptr, it denotes an optional arg, and description will be ignored
* description:
* - if description is nullptr or "", the argument won't be shown in show_usage()
*
* location:
* - if not nullptr, the argument for this arg is stored there
* - if nullptr, this arg does not need arguments
*/
ArgParser & add(const char * optname, const char * description, ArgParserCallBack callback, bool need_arg = false);
template <class T, class Tv>
ArgParser & add(const char * optname, T * location, const Tv & default_value, const char * description, bool dont_show_default = false);
void parse(int argc, char ** argv) const;
void show_usage(std::ostream & out) const;
private:
// type names helper
template<class>
static const char * get_type_name(void) { return "unknown"; }
struct ArgEntryBase
{
/* name or description cannot be nullptr */
ArgEntryBase(const char * name, const char * description, bool need_arg);
virtual ~ArgEntryBase() { }
char shortname;
std::string name;
std::string description;
bool need_arg;
virtual void parse (const char * arg) const = 0;
virtual void show_usage (std::ostream & out) const = 0;
};
template <class T, class Tv>
struct ArgEntry : public ArgEntryBase
{
ArgEntry(const char * name,
const char * description,
ArgParserCallBack callback,
bool need_arg);
ArgEntry(const char * name,
T * location, const Tv & default_value,
const char * description, bool dont_show_default);
virtual void parse (const char * arg) const;
virtual void show_usage (std::ostream & out) const;
private:
T * location;
T default_value;
ArgParserCallBack callback;
bool dont_show_default;
};
std::vector<std::unique_ptr<ArgEntryBase>> arg_entries, optional_arg_entries;
static const int arg_col_width;
};
template<class T, class Tv>
ArgParser & ArgParser::add(const char * optname, T * location, const Tv & default_value, const char * description, bool dont_show_default)
{
// ArgEntry does not accept nullptr as optname nor description
if((!optname) || (!optname[0]))
{
// when optname is nullptr or "", it's optional, and description is dropped
optional_arg_entries.emplace_back(new ArgEntry<T, Tv>("", location, default_value, "", dont_show_default));
}
else
{
arg_entries.emplace_back(new ArgEntry<T, Tv>(optname, location, default_value, (description ? description : ""), dont_show_default));
}
return *this;
}
// Known types
template<> const char * ArgParser::get_type_name<int> (void);
template<> const char * ArgParser::get_type_name<double> (void);
template<> const char * ArgParser::get_type_name<std::string> (void);
template<class T, class Tv>
ArgParser::ArgEntry<T, Tv>::ArgEntry(const char * name, const char * description, ArgParserCallBack callback, bool need_arg)
: ArgEntryBase(name, description, need_arg)
, location(nullptr)
, default_value()
, callback(callback)
, dont_show_default(true)
{
}
template<class T, class Tv>
ArgParser::ArgEntry<T, Tv>::ArgEntry(const char * name, T * location, const Tv & default_value, const char * description, bool dont_show_default)
: ArgEntryBase(name, description, (location != nullptr))
, location(location)
, default_value(default_value)
, callback(nullptr)
, dont_show_default(dont_show_default)
{
if(need_arg)
*location = T(default_value);
}
template<class T, class Tv>
void ArgParser::ArgEntry<T, Tv>::parse(const char * arg) const
{
if(need_arg)
{
if(!arg)
throw std::string("Missing argument of option: --") + name;
if((location != nullptr) && (!read_value(arg, location)))
throw std::string("Invalid argument: ") + arg;
}
if(callback)
(*callback)(arg);
}
template<class T, class Tv>
void ArgParser::ArgEntry<T, Tv>::show_usage(std::ostream & out) const
{
if(description.empty())
return;
std::ostringstream sout;
sout << " ";
if(shortname != 0)
{
sout << "-" << shortname;
}
if(name != "")
{
if(shortname != 0)
sout << ",";
sout << "--" << name;
}
if(need_arg)
{
sout << " <" << get_type_name<T>() << ">";
}
std::string s = sout.str();
out << s;
for(int i = s.size(); i < arg_col_width; ++i)
out << ' ';
out << " " << description;
if(need_arg && !dont_show_default)
{
out << " (default: ";
dump_value(out, default_value);
out << ")";
}
out << std::endl;
}
} // namespace ArgParser
#endif //ARGPARSER_H__