Skip to content

Instantly share code, notes, and snippets.

@ronchaine
Created May 26, 2016 00:37
Show Gist options
  • Save ronchaine/3505f857c1d36e780e1879be16e3d2ce to your computer and use it in GitHub Desktop.
Save ronchaine/3505f857c1d36e780e1879be16e3d2ce to your computer and use it in GitHub Desktop.
Easier way to parse command line arguments with c++
#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