diff --git a/jngen.h b/jngen.h index b043856..ae2d58d 100644 --- a/jngen.h +++ b/jngen.h @@ -114,55 +114,212 @@ class Dsu { +#include #include #include #include namespace jngen { -namespace detail { +struct VariableMap { + std::vector positional; + std::map named; -template -void readVariable(T& var, const std::string& value) { - if (value.empty()) { - return; + int count(size_t pos) const { + return pos < positional.size(); + } + + int count(const std::string& name) const { + return named.count(name); + } + + std::string operator[](size_t pos) const { + if (!count(pos)) { + return ""; + } + return positional[pos]; + } + + std::string operator[](const std::string& name) const { + if (!count(name)) { + return name; + } + return named.at(name); + } + + bool initialized = false; +}; + +// TODO: think about seed as a last argument +inline VariableMap parseArguments(const std::vector& args) { + VariableMap result; + + for (const std::string& s: args) { + if (s == "-") { + continue; + } + if (s == "--") { + break; + } + + if (s[0] != '-') { + result.positional.push_back(s); + continue; + } + + std::string name; + std::string value; + bool foundEq = false; + for (char c: s.substr(1)) { + if (!foundEq && c == '=') { + foundEq = true; + } else { + if (foundEq) { + value += c; + } else { + name += c; + } + } + } + if (foundEq) { + ensure( + !result.named.count(value), + "Named arguments must have distinct names"); + result.named[name] = value; + } else { + result.positional.push_back(name); + } } + result.initialized = true; + return result; +} + +VariableMap vmap; + +template +bool readVariable(const std::string& value, T& var) { std::istringstream ss(value); - ss >> var; - ensure(ss, "Failed to parse a value from a command line argument"); + T t; + if (ss >> t) { + var = t; + return true; + } + return false; } -typedef std::vector::const_iterator OptionIterator; +template +bool getOpt(size_t index, T& var) { + ensure( + vmap.initialized, + "parseArgs(args, argv) must be called before getOpt(...)"); + if (!vmap.count(index)) { + return false; + } + return readVariable(vmap[index], var); +} -void getopts(OptionIterator) -{ } +template +bool getOpt(const std::string& name, T& var) { + ensure( + vmap.initialized, + "parseArgs(args, argv) must be called before getOpt(...)"); + if (!vmap.count(name)) { + return false; + } + return readVariable(vmap[name], var); +} + +inline void parseArgs(int argc, char *argv[]) { + vmap = parseArguments(std::vector(argv + 1, argv + argc)); +} + +namespace detail { + +inline std::vector splitByComma(std::string s) { + auto strip = [](std::string s) { + size_t l = 0; + while (l < s.size() && s[l] == ' ') { + ++l; + } + s = s.substr(l); + while (!s.empty() && s.back() == ' ') { + s.pop_back(); + } + return s; + }; + + std::vector result; + s += ','; + std::string cur; + + for (char c: s) { + if (c == ',') { + result.push_back(strip(cur)); + cur.clear(); + } else { + cur += c; + } + } + + return result; +} + +inline int getNamedImpl(std::vector::const_iterator) { return 0; } + +template +int getNamedImpl( + std::vector::const_iterator it, T& var, Args&... args) +{ + int res = getOpt(*it, var); + res += getNamedImpl(++it, args...); + return res; +} + +inline int getPositionalImpl(size_t) { return 0; } template -void getopts(OptionIterator iter, T& var, Args& ...args) { - readVariable(var, *iter); - getopts(++iter, args...); +int getPositionalImpl(size_t index, T& var, Args&... args) { + int res = getOpt(index, var); + res += getPositionalImpl(index + 1, args...); + return res; } } // namespace detail template -void getopts(std::vector options, Args& ...args) { - if (options.size() < sizeof...(args)) { - options.resize(sizeof...(args)); - } - detail::getopts(options.cbegin(), args...); +int doGetNamed(const std::string& names, Args&... args) { + ensure( + vmap.initialized, + "parseArgs(args, argv) must be called before getNamed(...)"); + + auto namesSplit = detail::splitByComma(names); + + ensure( + namesSplit.size() == sizeof...(args), + "Number of names is not equal to number of variables"); + + return detail::getNamedImpl(namesSplit.begin(), args...); } template -void getopts(int argc, char *argv[], Args& ...args) { - return getopts(std::vector(argv + 1, argv + argc), args...); +int getPositional(Args&... args) { + ensure( + vmap.initialized, + "parseArgs(args, argv) must be called before getPositional(...)"); + + return detail::getPositionalImpl(0, args...); } } // namespace jngen -using jngen::getopts; +using jngen::parseArgs; +using jngen::getOpt; + +using jngen::getPositional; + +#define getNamed(...) jngen::doGetNamed(#__VA_ARGS__, __VA_ARGS__) #include diff --git a/options.h b/options.h index 9c80024..015eb79 100644 --- a/options.h +++ b/options.h @@ -2,52 +2,209 @@ #include "common.h" +#include #include #include #include namespace jngen { -namespace detail { +struct VariableMap { + std::vector positional; + std::map named; -template -void readVariable(T& var, const std::string& value) { - if (value.empty()) { - return; + int count(size_t pos) const { + return pos < positional.size(); + } + + int count(const std::string& name) const { + return named.count(name); + } + + std::string operator[](size_t pos) const { + if (!count(pos)) { + return ""; + } + return positional[pos]; + } + + std::string operator[](const std::string& name) const { + if (!count(name)) { + return name; + } + return named.at(name); + } + + bool initialized = false; +}; + +// TODO: think about seed as a last argument +inline VariableMap parseArguments(const std::vector& args) { + VariableMap result; + + for (const std::string& s: args) { + if (s == "-") { + continue; + } + if (s == "--") { + break; + } + + if (s[0] != '-') { + result.positional.push_back(s); + continue; + } + + std::string name; + std::string value; + bool foundEq = false; + for (char c: s.substr(1)) { + if (!foundEq && c == '=') { + foundEq = true; + } else { + if (foundEq) { + value += c; + } else { + name += c; + } + } + } + if (foundEq) { + ensure( + !result.named.count(value), + "Named arguments must have distinct names"); + result.named[name] = value; + } else { + result.positional.push_back(name); + } } + result.initialized = true; + return result; +} + +VariableMap vmap; + +template +bool readVariable(const std::string& value, T& var) { std::istringstream ss(value); - ss >> var; - ensure(ss, "Failed to parse a value from a command line argument"); + T t; + if (ss >> t) { + var = t; + return true; + } + return false; } -typedef std::vector::const_iterator OptionIterator; +template +bool getOpt(size_t index, T& var) { + ensure( + vmap.initialized, + "parseArgs(args, argv) must be called before getOpt(...)"); + if (!vmap.count(index)) { + return false; + } + return readVariable(vmap[index], var); +} -void getopts(OptionIterator) -{ } +template +bool getOpt(const std::string& name, T& var) { + ensure( + vmap.initialized, + "parseArgs(args, argv) must be called before getOpt(...)"); + if (!vmap.count(name)) { + return false; + } + return readVariable(vmap[name], var); +} + +inline void parseArgs(int argc, char *argv[]) { + vmap = parseArguments(std::vector(argv + 1, argv + argc)); +} + +namespace detail { + +inline std::vector splitByComma(std::string s) { + auto strip = [](std::string s) { + size_t l = 0; + while (l < s.size() && s[l] == ' ') { + ++l; + } + s = s.substr(l); + while (!s.empty() && s.back() == ' ') { + s.pop_back(); + } + return s; + }; + + std::vector result; + s += ','; + std::string cur; + + for (char c: s) { + if (c == ',') { + result.push_back(strip(cur)); + cur.clear(); + } else { + cur += c; + } + } + + return result; +} + +inline int getNamedImpl(std::vector::const_iterator) { return 0; } template -void getopts(OptionIterator iter, T& var, Args& ...args) { - readVariable(var, *iter); - getopts(++iter, args...); +int getNamedImpl( + std::vector::const_iterator it, T& var, Args&... args) +{ + int res = getOpt(*it, var); + res += getNamedImpl(++it, args...); + return res; +} + +inline int getPositionalImpl(size_t) { return 0; } + +template +int getPositionalImpl(size_t index, T& var, Args&... args) { + int res = getOpt(index, var); + res += getPositionalImpl(index + 1, args...); + return res; } } // namespace detail template -void getopts(std::vector options, Args& ...args) { - if (options.size() < sizeof...(args)) { - options.resize(sizeof...(args)); - } - detail::getopts(options.cbegin(), args...); +int doGetNamed(const std::string& names, Args&... args) { + ensure( + vmap.initialized, + "parseArgs(args, argv) must be called before getNamed(...)"); + + auto namesSplit = detail::splitByComma(names); + + ensure( + namesSplit.size() == sizeof...(args), + "Number of names is not equal to number of variables"); + + return detail::getNamedImpl(namesSplit.begin(), args...); } template -void getopts(int argc, char *argv[], Args& ...args) { - return getopts(std::vector(argv + 1, argv + argc), args...); +int getPositional(Args&... args) { + ensure( + vmap.initialized, + "parseArgs(args, argv) must be called before getPositional(...)"); + + return detail::getPositionalImpl(0, args...); } } // namespace jngen -using jngen::getopts; +using jngen::parseArgs; +using jngen::getOpt; + +using jngen::getPositional; + +#define getNamed(...) jngen::doGetNamed(#__VA_ARGS__, __VA_ARGS__)