#ifndef CMP_Options_h #define CMP_Options_h #include #include #include #include #include "cmp/utils/IntTypes.h" #include "cmp/utils/Vec.h" #include "cmp/utils/ParseUtils.h" namespace CMP { //================================================================================================== // Top-level option parse/help functions: extern void parseOptions (int& argc, char** argv, bool strict = false); extern void printUsageAndExit(int argc, char** argv, bool verbose = false); extern void setUsageHelp (const char* str); extern void setHelpPrefixStr (const char* str); //================================================================================================== // Options is an abstract class that gives the interface for all types options: class Option { protected: const char* name; const char* description; const char* category; const char* type_name; static vec& getOptionList () { static vec options; return options; } static const char*& getUsageString() { static const char* usage_str; return usage_str; } static const char*& getHelpPrefixString() { static const char* help_prefix_str = ""; return help_prefix_str; } struct OptionLt { bool operator()(const Option* x, const Option* y) { int test1 = strcmp(x->category, y->category); return test1 < 0 || test1 == 0 && strcmp(x->type_name, y->type_name) < 0; } }; Option(const char* name_, const char* desc_, const char* cate_, const char* type_) : name (name_) , description(desc_) , category (cate_) , type_name (type_) { getOptionList().push(this); } public: virtual ~Option() {} virtual bool parse (const char* str) = 0; virtual void help (bool verbose = false) = 0; friend void parseOptions (int& argc, char** argv, bool strict); friend void printUsageAndExit (int argc, char** argv, bool verbose); friend void setUsageHelp (const char* str); friend void setHelpPrefixStr (const char* str); }; //================================================================================================== // Range classes with specialization for floating types: struct IntRange { int begin; int end; IntRange(int b, int e) : begin(b), end(e) {} }; struct Int64Range { int64_t begin; int64_t end; Int64Range(int64_t b, int64_t e) : begin(b), end(e) {} }; struct DoubleRange { double begin; double end; bool begin_inclusive; bool end_inclusive; DoubleRange(double b, bool binc, double e, bool einc) : begin(b), end(e), begin_inclusive(binc), end_inclusive(einc) {} }; //================================================================================================== // Double options: class DoubleOption : public Option { protected: DoubleRange range; double value; public: DoubleOption(const char* c, const char* n, const char* d, double def = double(), DoubleRange r = DoubleRange(-HUGE_VAL, false, HUGE_VAL, false)) : Option(n, d, c, ""), range(r), value(def) { // FIXME: set LC_NUMERIC to "C" to make sure that strtof/strtod parses decimal point correctly. } operator double (void) const { return value; } operator double& (void) { return value; } DoubleOption& operator=(double x) { value = x; return *this; } virtual bool parse(const char* str){ const char* span = str; if (!match(span, "-") || !match(span, name) || !match(span, "=")) return false; char* end; double tmp = strtod(span, &end); if (end == NULL) return false; else if (tmp >= range.end && (!range.end_inclusive || tmp != range.end)){ fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); exit(1); }else if (tmp <= range.begin && (!range.begin_inclusive || tmp != range.begin)){ fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); exit(1); } value = tmp; // fprintf(stderr, "READ VALUE: %g\n", value); return true; } virtual void help (bool verbose = false){ fprintf(stderr, " -%-12s = %-8s %c%4.2g .. %4.2g%c (default: %g)\n", name, type_name, range.begin_inclusive ? '[' : '(', range.begin, range.end, range.end_inclusive ? ']' : ')', value); if (verbose){ fprintf(stderr, "\n %s\n", description); fprintf(stderr, "\n"); } } }; //================================================================================================== // Int options: class IntOption : public Option { protected: IntRange range; int32_t value; public: IntOption(const char* c, const char* n, const char* d, int32_t def = int32_t(), IntRange r = IntRange(INT32_MIN, INT32_MAX)) : Option(n, d, c, ""), range(r), value(def) {} operator int32_t (void) const { return value; } operator int32_t& (void) { return value; } IntOption& operator= (int32_t x) { value = x; return *this; } virtual bool parse(const char* str){ const char* span = str; if (!match(span, "-") || !match(span, name) || !match(span, "=")) return false; char* end; int32_t tmp = strtol(span, &end, 10); if (end == NULL) return false; else if (tmp > range.end){ fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); exit(1); }else if (tmp < range.begin){ fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); exit(1); } value = tmp; return true; } virtual void help (bool verbose = false){ fprintf(stderr, " -%-12s = %-8s [", name, type_name); if (range.begin == INT32_MIN) fprintf(stderr, "imin"); else fprintf(stderr, "%4d", range.begin); fprintf(stderr, " .. "); if (range.end == INT32_MAX) fprintf(stderr, "imax"); else fprintf(stderr, "%4d", range.end); fprintf(stderr, "] (default: %d)\n", value); if (verbose){ fprintf(stderr, "\n %s\n", description); fprintf(stderr, "\n"); } } }; // Leave this out for visual C++ until Microsoft implements C99 and gets support for strtoll. #ifndef _MSC_VER class Int64Option : public Option { protected: Int64Range range; int64_t value; public: Int64Option(const char* c, const char* n, const char* d, int64_t def = int64_t(), Int64Range r = Int64Range(INT64_MIN, INT64_MAX)) : Option(n, d, c, ""), range(r), value(def) {} operator int64_t (void) const { return value; } operator int64_t& (void) { return value; } Int64Option& operator= (int64_t x) { value = x; return *this; } virtual bool parse(const char* str){ const char* span = str; if (!match(span, "-") || !match(span, name) || !match(span, "=")) return false; char* end; int64_t tmp = strtoll(span, &end, 10); if (end == NULL) return false; else if (tmp > range.end){ fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); exit(1); }else if (tmp < range.begin){ fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); exit(1); } value = tmp; return true; } virtual void help (bool verbose = false){ fprintf(stderr, " -%-12s = %-8s [", name, type_name); if (range.begin == INT64_MIN) fprintf(stderr, "imin"); else fprintf(stderr, "%4" PRIi64, range.begin); fprintf(stderr, " .. "); if (range.end == INT64_MAX) fprintf(stderr, "imax"); else fprintf(stderr, "%4" PRIi64, range.end); fprintf(stderr, "] (default: %" PRIi64")\n", value); if (verbose){ fprintf(stderr, "\n %s\n", description); fprintf(stderr, "\n"); } } }; #endif //================================================================================================== // String option: class StringOption : public Option { const char* value; public: StringOption(const char* c, const char* n, const char* d, const char* def = NULL) : Option(n, d, c, ""), value(def) {} operator const char* (void) const { return value; } operator const char*& (void) { return value; } StringOption& operator= (const char* x) { value = x; return *this; } virtual bool parse(const char* str){ const char* span = str; if (!match(span, "-") || !match(span, name) || !match(span, "=")) return false; value = span; return true; } virtual void help (bool verbose = false){ fprintf(stderr, " -%-10s = %8s\n", name, type_name); if (verbose){ fprintf(stderr, "\n %s\n", description); fprintf(stderr, "\n"); } } }; //================================================================================================== // Bool option: class BoolOption : public Option { bool value; public: BoolOption(const char* c, const char* n, const char* d, bool v) : Option(n, d, c, ""), value(v) {} operator bool (void) const { return value; } operator bool& (void) { return value; } BoolOption& operator=(bool b) { value = b; return *this; } virtual bool parse(const char* str){ const char* span = str; if (match(span, "-")){ bool b = !match(span, "no-"); if (strcmp(span, name) == 0){ value = b; return true; } } return false; } virtual void help (bool verbose = false){ fprintf(stderr, " -%s, -no-%s", name, name); for (uint32_t i = 0; i < 32 - strlen(name)*2; i++) fprintf(stderr, " "); fprintf(stderr, " "); fprintf(stderr, "(default: %s)\n", value ? "on" : "off"); if (verbose){ fprintf(stderr, "\n %s\n", description); fprintf(stderr, "\n"); } } }; //================================================================================================= } #endif