源代码转载处:(https://github.com/tanakh/cmdline)
源代码中有部分存在问题无法编译,修改后在win11+vs2022可顺利编译
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace cmdline {
namespace detail {
template <typename Target, typename Source, bool Same>
class lexical_cast_t {
public:
//格式转换
static Target cast(const Source &arg) {
Target ret;
std::stringstream ss;
//将arg放入流ss中,从ss流中抽取数据放入ret中
if (!(ss << arg && ss >> ret && ss.eof()))
throw std::bad_cast();
return ret;
}
};
template <typename Target, typename Source>
class lexical_cast_t<Target, Source, true> {
public:
static Target cast(const Source &arg) { return arg; }
};
template <typename Source>
class lexical_cast_t<std::string, Source, false> {
public:
static std::string cast(const Source &arg) {
std::ostringstream ss;
ss << arg;
return ss.str();
}
};
//将传入的string转换为Target类型后返回
template <typename Target>
class lexical_cast_t<Target, std::string, false> {
public:
static Target cast(const std::string &arg) {
Target ret;
// istringstream类用于执行C++风格的串流的输入操作
std::istringstream ss(arg);
if (!(ss >> ret && ss.eof())) throw std::bad_cast();
return ret;
}
};
template <typename T1, typename T2>
struct is_same {
static const bool value = false;
};
template <typename T>
struct is_same<T, T> {
static const bool value = true;
};
//类型转换
template <typename Target, typename Source>
Target lexical_cast(const Source &arg) {
return lexical_cast_t<Target, Source,
detail::is_same<Target, Source>::value>::cast(arg);
}
//识别C++编译以后的函数名的过程,叫demangle
static inline std::string demangle(const std::string &name) {
#ifdef _MSC_VER
return name; // 为MSVC编译器时直接返回name
#elif
// defined(__GNUC__)
// 为gcc编译器时还调用原来的代码
int status = 0;
char *p = abi::__cxa_demangle(name.c_str(), 0, 0, &status);
std::string ret(p);
free(p);
#endif
}
//返回类型名称
template <class T>
std::string readable_typename() {
// typeid().name()获取目标类型
return demangle(typeid(T).name());
}
//将传入参数转为string类型
template <class T>
std::string default_value(T def) {
return detail::lexical_cast<std::string>(def);
}
//返回字符串"string"
template <>
inline std::string readable_typename<std::string>() {
return "string";
}
}// ===================================================namespace detail
class cmdline_error : public std::exception {
public:
cmdline_error(const std::string &msg) : msg(msg) {}
~cmdline_error() throw() {}
//返回字符串内容
const char *what() const throw() { return msg.c_str(); }
private:
std::string msg;
};
//将传入字符串str转换类型
template <class T>
struct default_reader {
T operator()(const std::string &str) { return detail::lexical_cast<T>(str); }
};
template <class T>
struct range_reader {
range_reader(const T &low, const T &high) : low(low), high(high) {}
//判断传入字符串是否在对应范围内
T operator()(const std::string &s) const {
T ret = default_reader<T>()(s);
if (!(ret >= low && ret <= high))
throw cmdline::cmdline_error("range_error");
return ret;
}
private:
T low, high;
};
//设置范围
template <class T>
range_reader<T> range(const T &low, const T &high) {
return range_reader<T>(low, high);
}
//参数容器结构体
template <class T>
struct oneof_reader {
//在参数容器中查找字符串并返回
T operator()(const std::string &s) {
T ret = default_reader<T>()(s);
//在alt中没有找到ret,抛出命令行错误(空字符串)
if (std::find(alt.begin(), alt.end(), ret) == alt.end())
throw cmdline_error("");
return ret;
}
//向参数容器中添加内容
void add(const T &v) { alt.push_back(v); }
private:
std::vector<T> alt;
};
//将传入的1个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1) {
oneof_reader<T> ret;
ret.add(a1);
return ret;
}
//将传入的2个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1, T a2) {
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
return ret;
}
//将传入的3个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3) {
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
return ret;
}
//将传入的4个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4) {
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
return ret;
}
//将传入的5个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5) {
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
return ret;
}
//将传入的6个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6) {
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
return ret;
}
//将传入的7个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7) {
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
ret.add(a7);
return ret;
}
//将传入的8个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8) {
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
ret.add(a7);
ret.add(a8);
return ret;
}
//将传入的9个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9) {
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
ret.add(a7);
ret.add(a8);
ret.add(a9);
return ret;
}
//将传入的10个参数添加到一个oneof_reader结构体并返回
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9,
T a10) {
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
ret.add(a7);
ret.add(a8);
ret.add(a9);
ret.add(a10);
return ret;
}
//解析器
class parser {
public:
parser() {}
~parser() {
for (std::map<std::string, option_base *>::iterator p = options.begin();
p != options.end(); p++)
delete p->second;
}
void add(const std::string &name, char short_name = 0,const std::string &desc = "") {
//如果name存在
if (options.count(name))
throw cmdline_error("multiple definition: " + name);
options[name] = new option_without_value(name, short_name, desc);
ordered.push_back(options[name]);
}
template <class T>
void add(const std::string &name, char short_name = 0,const std::string &desc = "", bool need = true, const T def = T()) {
add(name, short_name, desc, need, def, default_reader<T>());
}
template <class T, class F>
void add(const std::string &name, char short_name = 0,
const std::string &desc = "", bool need = true, const T def = T(),
F reader = F()) {
//如果name存在于option
if (options.count(name))
throw cmdline_error("multiple definition: " + name);
options[name] = new option_with_value_with_reader<T, F>(
name, short_name, need, def, desc, reader);
ordered.push_back(options[name]);
}
void footer(const std::string &f) { ftr = f; }
void set_program_name(const std::string &name) { prog_name = name; }
bool exist(const std::string &name) const {
//如果name不存在
if (options.count(name) == 0)
throw cmdline_error("there is no flag: --" + name);
return options.find(name)->second->has_set();
}
//
template <class T>
const T &get(const std::string &name) const {
//如果name不存在
if (options.count(name) == 0)
throw cmdline_error("there is no flag: --" + name);
//指针类型转换
const option_with_value<T> *p =
dynamic_cast<const option_with_value<T> *>(options.find(name)->second);
if (p == NULL) throw cmdline_error("type mismatch flag '" + name + "'");
return p->get();
}
//返回others
const std::vector<std::string> &rest() const { return others; }
//接收字符串的解析函数:负责清除字符串中的双引号和空格
bool parse(const std::string &arg) {
std::vector<std::string> args;
std::string buf;
bool in_quote = false;
for (std::string::size_type i = 0; i < arg.length(); i++) {
//字符串中有奇数个",将in_quote设置为真
if (arg[i] == '\"') {
in_quote = !in_quote;
continue;
}
//字符串中有空格且空格字符之前有偶数个”字符
//args中放入空字符串(buf未初始化),占用一个位置
if (arg[i] == ' ' && !in_quote) {
args.push_back(buf);
buf = "";
continue;
}
//在字符串结尾为"\"
if (arg[i] == '\\') {
i++;
if (i >= arg.length()) {
errors.push_back("unexpected occurrence of '\\' at end of string");
return false;
}
}
//将字符串各字符拼接到buf中,除双引号和空格符
buf += arg[i];
}
//字符串中有奇数个双引号
if (in_quote) {
errors.push_back("quote is not closed");
return false;
}
//将非空buf放入args
if (buf.length() > 0) args.push_back(buf);
//打印args中字符串内容
for (size_t i = 0; i < args.size(); i++)
std::cout << "\"" << args[i] << "\"" << std::endl;
//使用解析函数2
return parse(args);
}
//接收字符串容器的解析函数2
bool parse(const std::vector<std::string> &args) {
int argc = static_cast<int>(args.size());
std::vector<const char *> argv(argc);
for (int i = 0; i < argc; i++) argv[i] = args[i].c_str();
//使用解析函数3
return parse(argc, &argv[0]);
}
//接收标准程序入口参数的解析函数3
bool parse(int argc, const char *const argv[]) {
errors.clear();
others.clear();
if (argc < 1) {
errors.push_back("argument number must be longer than 0");
return false;
}
if (prog_name == "") prog_name = argv[0];
std::map<char, std::string> lookup;
for (std::map<std::string, option_base *>::iterator p = options.begin();p != options.end(); p++) {
if (p->first.length() == 0) continue;
char initial = p->second->short_name();
if (initial) {
//如果lookup中存在initial
if (lookup.count(initial) > 0) {
lookup[initial] = "";
errors.push_back(std::string("short option '") + initial +
"' is ambiguous");
return false;
} else
lookup[initial] = p->first;
}
}
for (int i = 1; i < argc; i++) {
//如果argv[i]前两个字符为--
if (strncmp(argv[i], "--", 2) == 0) {
//查找字符"=",找到的话返回第一次出现的位置
const char *p = strchr(argv[i] + 2, '=');
//如果找到
if (p) {
std::string name(argv[i] + 2, p);
std::string val(p + 1);
set_option(name, val);
} else {
//argv[i]前两个字符不是--
std::string name(argv[i] + 2);
//如果option中没有name
if (options.count(name) == 0) {
errors.push_back("undefined option: --" + name);
continue;
}
if (options[name]->has_value()) {
if (i + 1 >= argc) {
errors.push_back("option needs value: --" + name);
continue;
} else {
i++;
set_option(name, argv[i]);
}
} else {
set_option(name);
}
}
} else if (strncmp(argv[i], "-", 1) == 0) {
if (!argv[i][1]) continue;
char last = argv[i][1];
for (int j = 2; argv[i][j]; j++) {
last = argv[i][j];
if (lookup.count(argv[i][j - 1]) == 0) {
errors.push_back(std::string("undefined short option: -") +
argv[i][j - 1]);
continue;
}
if (lookup[argv[i][j - 1]] == "") {
errors.push_back(std::string("ambiguous short option: -") +
argv[i][j - 1]);
continue;
}
set_option(lookup[argv[i][j - 1]]);
}
if (lookup.count(last) == 0) {
errors.push_back(std::string("undefined short option: -") + last);
continue;
}
if (lookup[last] == "") {
errors.push_back(std::string("ambiguous short option: -") + last);
continue;
}
if (i + 1 < argc && options[lookup[last]]->has_value()) {
set_option(lookup[last], argv[i + 1]);
i++;
} else {
set_option(lookup[last]);
}
} else {
others.push_back(argv[i]);
}
}
for (std::map<std::string, option_base *>::iterator p = options.begin();
p != options.end(); p++)
if (!p->second->valid())
errors.push_back("need option: --" + std::string(p->first));
return errors.size() == 0;
}
void parse_check(const std::string &arg) {
if (!options.count("help")) add("help", '?', "print this message");
check(0, parse(arg));
}
void parse_check(const std::vector<std::string> &args) {
if (!options.count("help")) add("help", '?', "print this message");
check(args.size(), parse(args));
}
void parse_check(int argc, char *argv[]) {
if (!options.count("help")) add("help", '?', "print this message");
check(argc, parse(argc, argv));
}
std::string error() const { return errors.size() > 0 ? errors[0] : ""; }
std::string error_full() const {
std::ostringstream oss;
for (size_t i = 0; i < errors.size(); i++) oss << errors[i] << std::endl;
return oss.str();
}
std::string usage() const {
std::ostringstream oss;
oss << "usage: " << prog_name << " ";
for (size_t i = 0; i < ordered.size(); i++) {
if (ordered[i]->must()) oss << ordered[i]->short_description() << " ";
}
oss << "[options] ... " << ftr << std::endl;
oss << "options:" << std::endl;
size_t max_width = 0;
for (size_t i = 0; i < ordered.size(); i++) {
max_width = std::max(max_width, ordered[i]->name().length());
}
for (size_t i = 0; i < ordered.size(); i++) {
if (ordered[i]->short_name()) {
oss << " -" << ordered[i]->short_name() << ", ";
} else {
oss << " ";
}
oss << "--" << ordered[i]->name();
for (size_t j = ordered[i]->name().length(); j < max_width + 4; j++)
oss << ' ';
oss << ordered[i]->description() << std::endl;
}
return oss.str();
}
private:
void check(int argc, bool ok) {
if ((argc == 1 && !ok) || exist("help")) {
std::cerr << usage();
exit(0);
}
if (!ok) {
std::cerr << error() << std::endl << usage();
exit(1);
}
}
void set_option(const std::string &name) {
if (options.count(name) == 0) {
errors.push_back("undefined option: --" + name);
return;
}
if (!options[name]->set()) {
errors.push_back("option needs value: --" + name);
return;
}
}
void set_option(const std::string &name, const std::string &value) {
if (options.count(name) == 0) {
errors.push_back("undefined option: --" + name);
return;
}
if (!options[name]->set(value)) {
errors.push_back("option value is invalid: --" + name + "=" + value);
return;
}
}
//基础选项类(抽象类)
class option_base {
public:
virtual ~option_base() {}
virtual bool has_value() const = 0;
virtual bool set() = 0;
virtual bool set(const std::string &value) = 0;
virtual bool has_set() const = 0;
virtual bool valid() const = 0;
virtual bool must() const = 0;
virtual const std::string &name() const = 0;
virtual char short_name() const = 0;
virtual const std::string &description() const = 0;
virtual std::string short_description() const = 0;
};
//无值选项类
class option_without_value : public option_base {
public:
option_without_value(const std::string &name, char short_name,
const std::string &desc)
: nam(name), snam(short_name), desc(desc), has(false) {}
~option_without_value() {}
//无值选项类,返回false
bool has_value() const { return false; }
//令has为真,返回true
bool set() {
has = true;
return true;
}
//返回false
bool set(const std::string &) { return false; }
//返回has
bool has_set() const { return has; }
//返回true
bool valid() const { return true; }
//返回false
bool must() const { return false; }
//返回nam(string)
const std::string &name() const { return nam; }
//返回snam
char short_name() const { return snam; }
//返回desc
const std::string &description() const { return desc; }
//返回--nam
std::string short_description() const { return "--" + nam; }
private:
std::string nam;
char snam;
std::string desc;
bool has;
};
//有值选项类
template <class T>
class option_with_value : public option_base {
public:
option_with_value(const std::string &name, char short_name, bool need,
const T &def, const std::string &desc)
: nam(name),
snam(short_name),
need(need),
has(false),
def(def),
actual(def) {
this->desc = full_description(desc);
}
~option_with_value() {}
//返回真实值
const T &get() const { return actual; }
//返回true
bool has_value() const { return true; }
//返回false
bool set() { return false; }
bool set(const std::string &value) {
try {
actual = read(value);
has = true;
} catch (const std::exception &e) {
return false;
}
return true;
}
//返回has
bool has_set() const { return has; }
bool valid() const {
if (need && !has) return false;
return true;
}
//返回need
bool must() const { return need; }
//返回nam
const std::string &name() const { return nam; }
//返回snam
char short_name() const { return snam; }
//返回desc
const std::string &description() const { return desc; }
//返回简短描述
std::string short_description() const {
return "--" + nam + "=" + detail::readable_typename<T>();
}
protected:
//完整描述
std::string full_description(const std::string &desc) {
return desc + " (" + detail::readable_typename<T>() +
(need ? "" : " [=" + detail::default_value<T>(def) + "]") + ")";
}
virtual T read(const std::string &s) = 0;
std::string nam;
char snam;
bool need;
std::string desc;
bool has;
//默认值
T def;
//真实值
T actual;
};
template <class T, class F>
class option_with_value_with_reader : public option_with_value<T> {
public:
option_with_value_with_reader(const std::string &name, char short_name,
bool need, const T def,
const std::string &desc, F reader)
: option_with_value<T>(name, short_name, need, def, desc),
reader(reader) {}
private:
//将传入字符串赋予reader并返回
T read(const std::string &s) { return reader(s); }
F reader;
};
//选项
std::map<std::string, option_base *> options;
std::vector<option_base *> ordered;
std::string ftr;
//程序名称
std::string prog_name;
std::vector<std::string> others;
//错误容器(string)
std::vector<std::string> errors;
}; //解析器
} // namespace cmdline