Skip to content

Instantly share code, notes, and snippets.

@iwinux
Created June 15, 2013 10:25
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 iwinux/5787658 to your computer and use it in GitHub Desktop.
Save iwinux/5787658 to your computer and use it in GitHub Desktop.
A naive implementation of command argument parser in C++
#include <iostream>
#include <map>
#include <stdexcept>
#include <sstream>
#include <string>
#include <vector>
using std::cout;
using std::endl;
using std::istringstream;
using std::map;
using std::out_of_range;
using std::runtime_error;
using std::string;
using std::stringstream;
using std::vector;
struct Arg {
char flag;
string long_flag, value;
bool need_value;
};
class ArgParser {
public:
void add_switch(char flag, const string& long_flag, const string& key,
bool initial) {
add_optional(flag, long_flag, key, initial, false);
}
void add_positional(const string& key) {
Arg arg = { '\0', "", "", false };
args[key] = arg;
positionals.push_back(key);
}
template <typename T>
void add_optional(char flag, const string& long_flag, const string& key,
T initial, bool need_value = true) {
if (flag) { flags_to_keys[flag] = key; }
if (long_flag.size()) { long_flags_to_keys[long_flag] = key; }
string value;
to_string(initial, value);
Arg arg = { flag, long_flag, value, need_value };
args[key] = arg;
}
template <typename T> T get_arg(const string& key) {
if (!args.count(key)) {
throw out_of_range("ArgParser::get_arg - unknown arg " + key);
}
const Arg& arg = args.at(key);
T value;
istringstream in(arg.value);
in >> value;
return value;
}
void parse(int argc, char *argv[]) {
if (!args.size()) { return; }
int i;
string prev_key;
bool is_positional = false;
vector<string>::const_iterator pos = positionals.begin();
for (i = 1; i < argc; ++i) {
string current(argv[i]);
string key;
if (prev_key.size()) {
key = prev_key;
} else if (current.size() > 2 && current[0] == '-' &&
current[1] == '-') {
key = long_flags_to_keys[current.substr(2)];
} else if (current.size() > 1 && current[0] == '-') {
key = flags_to_keys[current[1]];
} else if (pos != positionals.end()) {
key = *pos++;
is_positional = true;
}
if (!key.size() || !args.count(key)) { break; }
Arg& arg = args[key];
if (arg.need_value) {
if (prev_key.size()) {
arg.value = current;
prev_key = "";
} else {
prev_key = key;
}
} else if (is_positional) {
arg.value = current;
is_positional = false;
} else {
negate_value(arg); // switch
}
}
if (i != argc || pos != positionals.end()) {
throw runtime_error("ArgParser::parse - invalid args");
}
}
private:
map<string, Arg> args;
map<char, string> flags_to_keys;
map<string, string> long_flags_to_keys;
vector<string> positionals;
template <typename T> void to_string(const T& value, string& s) {
stringstream ss;
ss << value;
ss >> s;
}
void negate_value(Arg& arg) {
bool value;
stringstream in(arg.value);
in >> value;
to_string(!value, arg.value);
}
};
int main(int argc, char *argv[]) {
ArgParser parser;
parser.add_switch('v', "verbose", "verbose", false);
parser.add_optional('n', "num", "num", 10);
parser.add_positional("name");
parser.parse(argc, argv);
bool is_verbose = parser.get_arg<bool>("verbose");
cout << "verbose: " << std::boolalpha << is_verbose << std::noboolalpha << endl;
int num = parser.get_arg<int>("num") + 17;
cout << "num: " << num << endl;
string name = parser.get_arg<string>("name");
cout << "name: " << name << endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment