From 766ef262f1413eaef755ea567f69c25a1d26351d Mon Sep 17 00:00:00 2001 From: Determinant Date: Mon, 16 Jul 2018 02:31:40 -0400 Subject: improve argument parsing --- include/salticidae/stream.h | 2 +- include/salticidae/util.h | 52 ++++++++++++++++++++++----------- src/util.cpp | 70 ++++++++++++++++++++++++++++----------------- 3 files changed, 80 insertions(+), 44 deletions(-) diff --git a/include/salticidae/stream.h b/include/salticidae/stream.h index 7273882..efca394 100644 --- a/include/salticidae/stream.h +++ b/include/salticidae/stream.h @@ -275,7 +275,7 @@ class _Bits { using _impl_type = T; static const uint32_t bit_per_datum = sizeof(_impl_type) * 8; static const uint32_t shift_per_datum = log2::value; - static_assert(bit_per_datum == 1 << shift_per_datum); + static_assert(bit_per_datum == 1 << shift_per_datum, "int type must have 2^n bits"); BoxObj<_impl_type[]> data; uint32_t nbits; uint32_t ndata; diff --git a/include/salticidae/util.h b/include/salticidae/util.h index ab052de..47a260a 100644 --- a/include/salticidae/util.h +++ b/include/salticidae/util.h @@ -63,19 +63,15 @@ class Logger { void write(const char *tag, const char *fmt, va_list ap); public: - Logger(const char *prefix): - output(stderr), opened(false), prefix(prefix) {} - Logger(const char *prefix, FILE *f): - output(f), opened(false), prefix(prefix) {} + Logger(const char *prefix, FILE *file = stderr): + output(file), opened(false), prefix(prefix) {} Logger(const char *prefix, const char *filename): opened(true), prefix(prefix) { if ((output = fopen(filename, "w")) == nullptr) - throw SalticidaeError("logger cannot open file"); + throw SalticidaeError("logger cannot open file %s", filename); } - ~Logger() { - if (opened) fclose(output); - } + ~Logger() { if (opened) fclose(output); } void debug(const char *fmt, ...); void info(const char *fmt, ...); @@ -237,27 +233,49 @@ class Config { }; private: + class OptValConf: public OptVal { + Config *config; + public: + template + static RcObj create(Args... args) { + return new OptValConf(args...); + } + OptValConf(Config *config): config(config) {} + void set_val(const std::string &fname) override { + if (config->load(fname)) + SALTICIDAE_LOG_INFO("loading extra configuration from %s", fname.c_str()); + else + SALTICIDAE_LOG_INFO("configuration file %s not found", fname.c_str()); + } + std::string &get() = delete; + }; + struct Opt { std::string optname; std::string optdoc; optval_t optval; Action action; struct option opt; + char short_opt; Opt(const std::string &optname, const std::string &optdoc, - const optval_t &optval, Action action, int idx); + const optval_t &optval, Action action, + char short_opt, + int idx); Opt(Opt &&other): optname(std::move(other.optname)), optdoc(std::move(other.optdoc)), optval(std::move(other.optval)), action(other.action), - opt(other.opt) { opt.name = this->optname.c_str(); } + opt(other.opt), + short_opt(other.short_opt) { + opt.name = this->optname.c_str(); + } }; - std::unordered_map conf; - std::vector getopt_order; + std::unordered_map conf; + std::vector> opts; std::string conf_fname; - RcObj opt_val_conf; - int conf_idx; + RcObj opt_val_conf; void update(const std::string &optname, const char *optval); void update(Opt &opt, const char *optval); @@ -265,14 +283,14 @@ class Config { public: Config(const std::string &conf_fname): conf_fname(conf_fname), - opt_val_conf(new OptValStr(this->conf_fname)) { - conf_idx = getopt_order.size(); - add_opt("conf", opt_val_conf, SET_VAL, "load options from a file"); + opt_val_conf(OptValConf::create(this)) { + add_opt("conf", opt_val_conf, SET_VAL, 'c', "load options from a file"); } ~Config() {} void add_opt(const std::string &optname, const optval_t &optval, Action action, + char short_opt = -1, const std::string &optdoc = ""); bool load(const std::string &fname); size_t parse(int argc, char **argv); diff --git a/src/util.cpp b/src/util.cpp index 580306c..2adb997 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -135,23 +135,30 @@ void ElapsedTime::stop(bool show_info) { } Config::Opt::Opt(const std::string &optname, const std::string &optdoc, - const optval_t &optval, Action action, int idx): - optname(optname), optdoc(optdoc), optval(optval), action(action) { + const optval_t &optval, Action action, + char short_opt, + int idx): + optname(optname), optdoc(optdoc), + optval(optval), action(action), + short_opt(short_opt) { opt.name = this->optname.c_str(); opt.has_arg = action == SWITCH_ON ? no_argument : required_argument; opt.flag = nullptr; - opt.val = idx; + opt.val = 0x100 + idx; } void Config::add_opt(const std::string &optname, const optval_t &optval, Action action, + char short_opt, const std::string &optdoc) { if (conf.count(optname)) throw SalticidaeError("option name already exists"); - auto it = conf.insert( - std::make_pair(optname, - Opt(optname, optdoc, - optval, action, getopt_order.size()))).first; - getopt_order.push_back(&it->second); + opts.push_back(new Opt(optname, optdoc, + optval, action, short_opt, + opts.size())); + auto opt = opts.back().get(); + conf.insert(std::make_pair(optname, opt)); + if (short_opt != -1) + conf.insert(std::make_pair(std::string(1, short_opt), opt)); } void Config::update(Opt &p, const char *optval) { @@ -167,7 +174,7 @@ void Config::update(Opt &p, const char *optval) { void Config::update(const std::string &optname, const char *optval) { assert(conf.count(optname)); - update(conf.find(optname)->second, optval); + update(*(conf.find(optname)->second), optval); } bool Config::load(const std::string &fname) { @@ -208,36 +215,47 @@ size_t Config::parse(int argc, char **argv) { if (load(conf_fname)) SALTICIDAE_LOG_INFO("loaded configuration from %s", conf_fname.c_str()); - size_t nopts = getopt_order.size(); + size_t nopts = opts.size(); struct option *longopts = (struct option *)malloc( sizeof(struct option) * (nopts + 1)); int ind; + std::string shortopts; for (size_t i = 0; i < nopts; i++) - longopts[i] = getopt_order[i]->opt; - longopts[nopts] = {0, 0, 0, 0}; - for (;;) { - int id = getopt_long(argc, argv, "", longopts, &ind); - if (id == -1 || id == '?') break; - update(*getopt_order[id], optarg); - if (id == conf_idx) + const auto &opt = opts[i]; + longopts[i] = opt->opt; + if (opt->short_opt != -1) { - auto &fname = opt_val_conf->get(); - if (load(fname)) - SALTICIDAE_LOG_INFO("loading extra configuration from %s", fname.c_str()); - else - SALTICIDAE_LOG_INFO("configuration file %s not found", fname.c_str()); + shortopts += opt->short_opt; + if (longopts[i].has_arg == required_argument) + shortopts += ":"; } } + longopts[nopts] = {0, 0, 0, 0}; + for (;;) + { + int id = getopt_long(argc, argv, shortopts.c_str(), longopts, &ind); + if (id == -1) + break; + if (id == '?') + throw SalticidaeError("invalid option format"); + if (id >= 0x100) + update(*(opts[id - 0x100]), optarg); + else + update(std::string(1, (char)id), optarg); + } return optind; } void Config::print_help(FILE *output) { - for (auto opt: getopt_order) + for (const auto &opt: opts) { - fprintf(output, "--%s\t\t%s\n", - opt->optname.c_str(), - opt->optdoc.c_str()); + fprintf(output, "--%s\t\t", opt->optname.c_str()); + if (opt->short_opt != -1) + fprintf(output, "-%c\t", opt->short_opt); + else + fprintf(output, "\t\t"); + fprintf(output, "%s\n", opt->optdoc.c_str()); } } -- cgit v1.2.3-70-g09d2