/** * Copyright (c) 2018 Cornell University. * * Author: Ted Yin * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _SALTICIDAE_COMMON_H #define _SALTICIDAE_COMMON_H #include #include #include #include #include #include #include #include #include "salticidae/config.h" #include "salticidae/ref.h" namespace salticidae { void sec2tv(double t, struct timeval &tv); void event_add_with_timeout(struct event *ev, double timeout); double gen_rand_timeout(double base_timeout, double alpha = 0.5); std::string trim(const std::string &s, const std::string &space = "\t\r\n "); std::vector split(const std::string &s, const std::string &delim); std::vector trim_all(const std::vector &ss); class SalticidaeError: public std::exception { std::string msg; public: SalticidaeError(); template SalticidaeError(const std::string &fmt, Args... args) { int guessed_size = 128; std::string buff; for (;;) { buff.resize(guessed_size); int nwrote = snprintf((char *)buff.data(), guessed_size, fmt.c_str(), args...); if (nwrote < 0 || nwrote == guessed_size) { guessed_size <<= 1; continue; } buff.resize(nwrote); msg = std::move(buff); break; } } operator std::string() const { return msg; } const char *what() const throw() override { return msg.c_str(); } }; class Logger { protected: FILE *output; bool opened; const char *prefix; void write(const char *tag, const char *fmt, va_list ap); public: 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 %s", filename); } ~Logger() { if (opened) fclose(output); } void debug(const char *fmt, ...); void info(const char *fmt, ...); void warning(const char *fmt, ...); void error(const char *fmt, ...); }; extern Logger logger; #ifdef SALTICIDAE_DEBUG_LOG #define SALTICIDAE_NORMAL_LOG #define SALTICIDAE_ENABLE_LOG_DEBUG #endif #ifdef SALTICIDAE_NORMAL_LOG #define SALTICIDAE_ENABLE_LOG_INFO #define SALTICIDAE_ENABLE_LOG_WARN #endif #ifdef SALTICIDAE_ENABLE_LOG_INFO #define SALTICIDAE_LOG_INFO(...) salticidae::logger.info(__VA_ARGS__) #else #define SALTICIDAE_LOG_INFO(...) ((void)0) #endif #ifdef SALTICIDAE_ENABLE_LOG_DEBUG #define SALTICIDAE_LOG_DEBUG(...) salticidae::logger.debug(__VA_ARGS__) #else #define SALTICIDAE_LOG_DEBUG(...) ((void)0) #endif #ifdef SALTICIDAE_ENABLE_LOG_WARN #define SALTICIDAE_LOG_WARN(...) salticidae::logger.warning(__VA_ARGS__) #else #define SALTICIDAE_LOG_WARN(...) ((void)0) #endif #define SALTICIDAE_LOG_ERROR(...) salticidae::logger.error(__VA_ARGS__) class ElapsedTime { struct timeval t0; clock_t cpu_t0; public: double elapsed_sec; double cpu_elapsed_sec; void start(); void stop(bool show_info = false); }; class Config { public: enum Action { SWITCH_ON, SET_VAL, APPEND }; class OptVal { public: virtual void switch_on() { throw SalticidaeError("undefined OptVal behavior: set_val"); } virtual void set_val(const std::string &) { throw SalticidaeError("undefined OptVal behavior: set_val"); } virtual void append(const std::string &) { throw SalticidaeError("undefined OptVal behavior: append"); } virtual ~OptVal() = default; }; using optval_t = RcObj; class OptValFlag: public OptVal { bool val; public: template static RcObj create(Args... args) { return new OptValFlag(args...); } OptValFlag() = default; OptValFlag(bool val): val(val) {} void switch_on() override { val = true; } bool &get() { return val; } }; class OptValStr: public OptVal { std::string val; public: template static RcObj create(Args... args) { return new OptValStr(args...); } OptValStr() = default; OptValStr(const std::string &val): val(val) {} void set_val(const std::string &strval) override { val = strval; } std::string &get() { return val; } }; class OptValInt: public OptVal { int val; public: template static RcObj create(Args... args) { return new OptValInt(args...); } OptValInt() = default; OptValInt(int val): val(val) {} void set_val(const std::string &strval) override { size_t idx; try { val = stoi(strval, &idx); } catch (std::invalid_argument &) { throw SalticidaeError("invalid integer"); } } int &get() { return val; } }; class OptValDouble: public OptVal { double val; public: template static RcObj create(Args... args) { return new OptValDouble(args...); } OptValDouble() = default; OptValDouble(double val): val(val) {} void set_val(const std::string &strval) override { size_t idx; try { val = stod(strval, &idx); } catch (std::invalid_argument &) { throw SalticidaeError("invalid double"); } } double &get() { return val; } }; class OptValStrVec: public OptVal { using strvec_t = std::vector; strvec_t val; public: template static RcObj create(Args... args) { return new OptValStrVec(args...); } OptValStrVec() = default; OptValStrVec(const strvec_t &val): val(val) {} void append(const std::string &strval) override { val.push_back(strval); } strvec_t &get() { return val; } }; 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, 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), short_opt(other.short_opt) { opt.name = this->optname.c_str(); } }; std::unordered_map conf; std::vector> opts; std::string conf_fname; RcObj opt_val_conf; void update(const std::string &optname, const char *optval); void update(Opt &opt, const char *optval); public: Config() {} Config(const std::string &conf_fname): conf_fname(conf_fname), 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); void print_help(FILE *output = stderr); }; } #endif