Last active
August 29, 2015 14:13
-
-
Save LeszekSwirski/e775f8340c25f95626cc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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