Created
June 15, 2013 10:25
-
-
Save iwinux/5787658 to your computer and use it in GitHub Desktop.
A naive implementation of command argument parser in C++
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 <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