Created
May 26, 2016 00:37
-
-
Save ronchaine/3505f857c1d36e780e1879be16e3d2ce to your computer and use it in GitHub Desktop.
Easier way to parse command line arguments with c++
This file contains hidden or 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 <string> | |
#include <cstring> | |
#include <vector> | |
#include <unordered_map> | |
#include <tuple> | |
#include <iostream> | |
#include <iomanip> | |
namespace arguments | |
{ | |
constexpr int OPTIONAL = 0b00000001; | |
constexpr int BOOLEAN = 0b00000010; | |
constexpr int ALLOW_EXTRA = 0b00000100; | |
std::vector<std::string> extra_arguments; | |
std::string program_description; | |
namespace internal | |
{ | |
struct argument | |
{ | |
std::string value; | |
std::string key; | |
std::string help; | |
char32_t key_short; | |
int8_t flags; | |
}; | |
std::vector<argument> args; | |
std::unordered_map<std::string, std::string> arg_values; | |
void set_value(const std::string& key) | |
{ | |
// set value | |
if (internal::arg_values.count(key) != 0) | |
{ | |
int vl = stoi(internal::arg_values[key]); | |
internal::arg_values[key] = std::to_string(++vl); | |
} else { | |
internal::arg_values[key] = "1"; | |
} | |
} | |
void show_usage(const std::string& p, int8_t pflags) | |
{ | |
std::string all_short_keys; | |
std::string positional; | |
for (auto a : internal::args) | |
{ | |
if (a.key_short != 0xffffffff) | |
all_short_keys += a.key_short; | |
if (!(a.flags & OPTIONAL)) | |
{ | |
positional += a.key; | |
positional += " "; | |
} | |
} | |
std::cout << "usage: " << p << " [-" << all_short_keys << "] "; | |
std::cout << positional; | |
if (pflags & ALLOW_EXTRA) | |
{ | |
std::cout << "[...]"; | |
} | |
std::cout << "\n"; | |
} | |
void show_help(const std::string& p, int8_t pflags) | |
{ | |
show_usage(p, pflags); | |
if (!program_description.empty()) | |
std::cout << "\n" << program_description << "\n\n"; | |
std::cout << "positional arguments:\n"; | |
for (auto a : internal::args) | |
{ | |
if (!(a.flags & OPTIONAL)) | |
{ | |
std::string keys = " "; | |
keys += a.key; | |
if (a.key_short != 0xffffffff) | |
{ | |
keys += ", "; | |
keys += a.key_short; | |
} | |
std::cout << std::setw(28) << std::left << keys | |
<< std::setw(20) << std::left << a.help; | |
std::cout << "\n"; | |
} | |
} | |
std::cout << "\noptional arguments:\n"; | |
for (auto a : internal::args) | |
{ | |
if (a.flags & OPTIONAL) | |
{ | |
std::string key; | |
if (a.key_short != 0xffffffff) | |
{ | |
key += " -"; | |
key += a.key_short; | |
key += ", "; | |
} | |
key += "--"; | |
key += a.key; | |
std::cout << std::setw(28) << std::left << key | |
<< std::setw(20) << std::left << a.help; | |
std::cout << "\n"; | |
} | |
} | |
std::cout << "\n"; | |
} | |
} | |
inline void add(const std::string& key, char32_t key_short, int8_t flags, const std::string& help) | |
{ | |
internal::argument arg; | |
arg.key = key; | |
arg.key_short = key_short; | |
arg.flags = flags; | |
arg.help = help; | |
internal::args.push_back(arg); | |
} | |
inline void add(const std::string& key, int8_t flags, const std::string& help) | |
{ | |
add(key, 0xffffffff, flags, help); | |
} | |
inline void parse(int argc, char* argv[], int8_t pflags = 0) | |
{ | |
uint32_t pos_count = 0; | |
uint32_t pos_n = 0; | |
uint32_t pos_l = 0; | |
for (auto a : internal::args) | |
if (!(a.flags & OPTIONAL)) pos_count++; | |
// get arguments | |
for (int i = 1; i < argc; ++i) | |
{ | |
std::string parse_argument(argv[i]); | |
if (!strncmp(parse_argument.c_str(), "--", 2)) | |
{ | |
parse_argument = parse_argument.substr(2); | |
if (parse_argument == "help") | |
{ | |
internal::show_help(argv[0], pflags); | |
exit(0); | |
} | |
for (auto a : internal::args) | |
{ | |
if (a.key == parse_argument) | |
{ | |
if (a.flags & BOOLEAN) | |
{ | |
if (internal::arg_values.count(a.key) != 0) | |
{ | |
int vl = stoi(internal::arg_values[a.key]); | |
internal::arg_values[a.key] = std::to_string(++vl); | |
} else { | |
internal::arg_values[a.key] = "1"; | |
} | |
} else { | |
if (argc < i + 2) | |
exit(22); | |
std::string val = argv[i+1]; | |
i++; | |
internal::arg_values[a.key] = val; | |
} | |
} | |
} | |
} | |
else if (!strncmp(parse_argument.c_str(), "-", 1)) | |
{ | |
parse_argument = parse_argument.substr(1); | |
// check if this is a group of arguments instead of single one | |
if (parse_argument.size() > 1) | |
{ | |
// multiple arguments, parse each | |
for (auto c : parse_argument) | |
{ | |
if (c == 'h') | |
{ | |
internal::show_help(argv[0], pflags); | |
exit(0); | |
} | |
for (auto a : internal::args) | |
{ | |
if (a.key_short == c) | |
{ | |
// check that no grouped arguments have need for subarguments | |
if (!(a.flags & BOOLEAN)) | |
{ | |
internal::show_usage(argv[0], pflags); | |
exit(22); | |
} | |
// set value | |
if (internal::arg_values.count(a.key) != 0) | |
{ | |
int vl = stoi(internal::arg_values[a.key]); | |
internal::arg_values[a.key] = std::to_string(++vl); | |
} else { | |
internal::arg_values[a.key] = "1"; | |
} | |
} | |
} | |
} | |
} else { | |
// single argument | |
if (parse_argument[0] == 'h') | |
{ | |
internal::show_help(argv[0], pflags); | |
exit(0); | |
} | |
for (auto a : internal::args) | |
{ | |
if (a.key_short == parse_argument[0]) | |
{ | |
if (a.flags & BOOLEAN) | |
{ | |
if (internal::arg_values.count(a.key) != 0) | |
{ | |
int vl = stoi(internal::arg_values[a.key]); | |
internal::arg_values[a.key] = std::to_string(++vl); | |
} else { | |
internal::arg_values[a.key] = "1"; | |
} | |
} else { | |
if (argc < i + 2) | |
{ | |
internal::show_usage(argv[0], pflags); | |
exit(22); | |
} | |
std::string val = argv[i+1]; | |
i++; | |
internal::arg_values[a.key] = val; | |
} | |
} | |
} | |
} | |
} | |
else | |
{ | |
bool found = false; | |
for (int j = pos_l; j < internal::args.size(); ++j) | |
{ | |
// find non-optional | |
if (!(internal::args[j].flags & OPTIONAL)) | |
{ | |
internal::arg_values[internal::args[j].key] = parse_argument; | |
pos_l = j + 1; | |
pos_n++; | |
found = true; | |
break; | |
} | |
} | |
if (found) | |
continue; | |
extra_arguments.push_back(parse_argument); | |
} | |
} | |
if (pos_n != pos_count) | |
{ | |
internal::show_usage(argv[0], pflags); | |
exit(1); | |
} | |
if ((extra_arguments.size() != 0) && !(pflags & ALLOW_EXTRA)) | |
{ | |
internal::show_usage(argv[0], pflags); | |
exit(1); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment