Skip to content

Instantly share code, notes, and snippets.

@LeszekSwirski
Last active August 29, 2015 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LeszekSwirski/e775f8340c25f95626cc to your computer and use it in GitHub Desktop.
Save LeszekSwirski/e775f8340c25f95626cc to your computer and use it in GitHub Desktop.
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <sstream>
#include <cassert>
struct ArgumentBase {
explicit ArgumentBase(std::string name) : name(std::move(name)), is_set(false), _has_default(false), _has_implicit(false), _use_implicit(false) {
}
virtual ~ArgumentBase() {
}
virtual void parse(const char* str) = 0;
std::string name;
bool is_set;
bool _has_default;
bool _has_implicit;
bool _use_implicit;
};
template<typename T>
struct type_parser {
void parse(const std::string& in, T& out) {
std::istringstream iss(in);
iss >> out;
}
};
template<>
struct type_parser<std::string> {
void parse(const std::string& in, std::string& out) {
out = in;
}
};
template<typename T>
class Argument : public ArgumentBase {
public:
explicit Argument(std::string name) : ArgumentBase(std::move(name)) {
if (std::is_same<T,bool>::value) {
_has_default = true;
_value = false;
_has_implicit = true;
_implicit_value = true;
}
}
virtual void parse(const char* str) override {
std::string sarg(str);
if (sarg.size() == 0) {
assert(_has_implicit);
_value = std::move(_implicit_value);
} else {
std::istringstream iss(str);
iss >> _value;
}
}
void set_default(T default_value) {
_has_default = true;
_value = default_value;
}
void set_implicit(T default_value) {
_has_implicit = true;
_implicit_value = default_value;
}
const T& value() const {
assert(is_set || _has_default);
return _use_implicit ? _implicit_value : _value;
}
T _value;
T _implicit_value;
};
template<typename T>
std::ostream& operator<<(std::ostream& os, const Argument<T>& arg) {
if (arg.is_set)
return os << arg.name << " = " << arg.value();
else
return os << arg.name << " not set";
}
bool argmatch(const std::string& name, const std::string& sarg) {
const char* carg = &sarg[2];
size_t arglen = sarg.size() - 2;
if (name.size() > arglen)
return false;
if (arglen > name.size() && carg[name.size()] != '=')
return false;
for (size_t i = 0; i < name.size(); ++i) {
if (name[i] != carg[i])
return false;
}
return true;
}
class ArgParser {
public:
template<typename T>
Argument<T>& add_argument(std::string name) {
args.push_back(std::unique_ptr<Argument<T>>(new Argument<T>(std::move(name))));
std::unique_ptr<ArgumentBase>& base = args.back();
ArgumentBase* pbase = base.get();
return *dynamic_cast<Argument<T>*>(pbase);
}
Argument<bool>& add_flag(std::string name) {
return add_argument<bool>(std::move(name));
}
void parse(int argc, char** argv) {
int i = 1;
while (i < argc) {
std::cout << i << std::endl;
std::string sarg(argv[i]);
if (sarg.size() >= 3 && sarg[0] == '-' && sarg[1] == '-') {
std::cout << sarg << std::endl;
for (std::unique_ptr<ArgumentBase>& parg : args) {
ArgumentBase& arg = *parg;
if (argmatch(arg.name, sarg)) {
arg.is_set = true;
std::cout << arg.name << std::endl;
const char* cval = 0;
if (sarg.size() - 2 > arg.name.size()) {
std::cout << "Use =" << std::endl;
// Argument value is after equals sign
assert(sarg[2 + arg.name.size()] == '=');
cval = &sarg[2 + arg.name.size() + 1];
} else {
std::cout << "Use next" << std::endl;
// Argument value is next arg
if (i+1 >= argc) {
std::cout << "Use implicit" << std::endl;
// No next arg, use implicit value
assert(arg._has_implicit);
arg._use_implicit = true;
}
else {
assert(i+1 < argc);
const char* cargval = argv[i+1];
if (cargval[0] == '-') {
std::cout << "Use implicit" << std::endl;
// Next arg is a new option, use implicit value
assert(arg._has_implicit);
arg._use_implicit = true;
} else {
cval = cargval;
i++;
}
}
}
if (cval) {
std::cout << "Parse '" << std::string(cval) << "'" << std::endl;
arg.parse(cval);
}
break;
}
}
}
i++;
}
}
private:
std::vector<std::unique_ptr<ArgumentBase>> args;
};
int main(int argc, char** argv) {
ArgParser ap;
auto& arg_help = ap.add_flag("help");
auto& arg_file = ap.add_argument<std::string>("file");
ap.parse(argc, argv);
std::cout << arg_file << std::endl;
std::cout << arg_help << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment