using namespace std; #include "arg_parser.h" #include #include using namespace std; // Convert a string to this-or-that representation. // Specialization is not the same as instantiation! // Atox and Atox need to come early, // because they get used by later things. template struct cvt2 { static double doit(string const sss){ stringstream parse; parse.str(sss); T rslt; parse >> rslt; if (parse.fail()) throw invalid_argument("'" + sss + "' isn't a proper " + myTraits::name); if (parse.eof()) return rslt; do { char ch; parse >> ch; if (parse.fail()) break; if (ch == 'k') rslt *= 1024; else if (ch == 'M') rslt *= 1048576; else break; parse >> ws; // without this, we wouldn't know whether we were at EoF if (!parse.eof()) break; return rslt; // whew! good. } while (0); throw invalid_argument("'" + sss + "' has extraneous verbiage"); return 0; /* should never happen */ } }; template struct helper{ static T convert(string const &arg); }; template<> struct helper { static string convert(string const &arg){ return arg; } }; template<> struct helper { static int convert(string const &arg){ return cvt2::doit(arg.c_str()); } }; template<> struct helper { static double convert(string const &arg){ if (tolower(arg) == "inf") return Inf; if (tolower(arg) == "-inf") return -Inf; return cvt2::doit(arg.c_str()); } }; // convert to double, then downgrade to float: template<> struct helper { static float convert(string const &arg){ return helper::convert(arg); } }; template struct helper > { static vector convert(string const &_arg){ string arg(_arg); vector rslt; while (arg.length()) { string::size_type where = arg.find(','); string word; if (where == arg.npos) { word = arg; arg = ""; } else { word = arg.substr(0, where); arg = arg.substr(1+where); } rslt.push_back(helper::convert(word)); } return rslt; } }; template string num_from_list(const string arg, B& rslt){ if (arg.length()) { string::size_type where = arg.find_first_of(", \t"); string word; if (where == arg.npos) { rslt = helper::convert(arg); return ""; } else { rslt = helper::convert(arg.substr(0, where)); return arg.substr(1+where); } } // should never get here return arg; } template struct helper > { static complex convert(string const &_arg){ string arg(_arg); B rp(0), ip(0); arg = num_from_list(arg, rp); arg = num_from_list(arg, ip); return complex(rp, ip); } }; // This is the interface that ordinary users use. // The point of all this is that you can perform // "partial specification" (what I might call re-specialization) // on classes, but not on functions. template T Atox(std::string const &str) { // Just delegate. return helper::convert(str); } ////////////////////////////////// // Various getters. //+++ fileGetter .... //ordinary constructor: fileGetter:: fileGetter(const std::string fname){ file.open(fname.c_str()); } std::string fileGetter::get(){ std::string str; for (;;) { file >> str; // try to read one word if (file.fail()) return ""; // failed if (str[0] != '#') return str; // got a comment; string junk; getline(file, junk); //throw it away // go around the loop, read next line } } // beware that neither bad() nor fail() is the opposite of good() int fileGetter::fail() { return file.fail(); } //+++ cmdGetter .... //ordinary constructor: cmdGetter::cmdGetter(int const _argc, char const * const * _argv) : argc(_argc), argv(_argv), wasgood(argc) {} std::string cmdGetter::get(){ if (argc <= 0) { wasgood = 0; return ""; } wasgood = argc--; return *argv++; } int cmdGetter::fail() { return !wasgood; } // That's all the getters for now. ////////////////////// string tolower(const string& arg){ string dupe(arg); for (string::iterator foo = dupe.begin(); foo != dupe.end(); foo++){ *foo = tolower(*foo); } return dupe; } string arg_parser::xcase(const string& arg){ if (!fold_case) return arg; return tolower(arg); } // Constructor, for reading words from command line: arg_parser::arg_parser(int const _argc, char const * const * _argv) : fold_case(0) { std::shared_ptr tmp (new cmdGetter(_argc, _argv)); getsome.push_front(tmp); } // Constructor, for reading words from file: arg_parser::arg_parser(const std::string fname) : fold_case(0), keyword("(?" "?)"), narg(0) { push(fname); } void arg_parser::push(const std::string fname){ std::shared_ptr tmp (new fileGetter(fname)); getsome.push_front(tmp); } int arg_parser::fail(){ if (!getsome.size()) return 1; // is this really correct? return getsome.front()->fail(); } // see if cur arg is a prefix of patt */ int arg_parser::prefix(const string& kwpatt){ if ((cur.length()==0 && kwpatt.length()!=0)) return 0; // disallow "", unless exact match if (xcase(cur) != xcase(kwpatt).substr(0, cur.length())) return 0; keyword = kwpatt; narg = 0; return 1; } template T arg_parser::nextRaw(){ cur = ""; for (;;) { if (!getsome.size()) { break; } cur += getsome.front()->get(); if (fail()) { // here if read failed; pop the context getsome.pop_front(); // if read failed late; return what we have if (cur.length()) return Atox(cur); // if read failed early, try again in parent context continue; } size_t len = cur.length(); if (len == 0) break; // zero length, can't have a comma if (cur[len-1] != ',') break; // else has a comma, keep reading } return Atox(cur); } // return next item, assuming it is an argument: template T arg_parser::nextArg(){ if (0) cerr << "next called with narg: " << narg <<" kw: " << keyword << endl; T rslt = nextRaw(); narg++; if (fail()) { ostringstream buf; buf << "Keyword " << keyword << " requires argument #" << narg << " of type " << myTraits::name; throw invalid_argument(buf.str()); } return rslt; } template arg_parser& arg_parser::operator>>(T& val){ val = nextArg(); return *this; } // from .h file // template T Atox(std::string const &foo); // specializations and explicit instantiations: // The first two lines of this macro help us figure // out the /name/ of a type. #define setup(T) \ template <> const char* myTraits::name = #T; /* specialization */ \ template struct myTraits; /* instantiation */ \ template arg_parser& arg_parser::operator>>(T& val); \ template T Atox(std::string const &arg); \ template T arg_parser::nextRaw(); // Last but not least, instantiate the things we are going to need. // I don't know why the compiler can't do this automatically. setup(int); setup(double); setup(float); setup(string); setup(vector); setup(vector); setup(complex);