mirror of
https://github.com/Cameron-Reed1/Cpp-Argument-Parser.git
synced 2024-11-09 21:47:27 +00:00
Cleaned up code
You can now also put multiple short flags all after one '-'
This commit is contained in:
parent
30063522eb
commit
03db3c64a2
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace Cam
|
namespace Cam
|
||||||
{
|
{
|
||||||
@ -17,12 +18,13 @@ class Option
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Option(const char* name, const char* description, OPTTYPE type);
|
Option(const char* name, const char* description, OPTTYPE type);
|
||||||
Option(const char* name, const char* short_name, const char* description, OPTTYPE type);
|
Option(const char* name, const char short_name, const char* description, OPTTYPE type);
|
||||||
|
Option(const char short_name, const char* description, OPTTYPE type);
|
||||||
bool found();
|
bool found();
|
||||||
void* data;
|
void* data;
|
||||||
private:
|
private:
|
||||||
const char* m_name;
|
const char* m_name;
|
||||||
const char* m_short_name;
|
const char m_short_name;
|
||||||
const char* m_description;
|
const char* m_description;
|
||||||
OPTTYPE m_type;
|
OPTTYPE m_type;
|
||||||
bool m_found;
|
bool m_found;
|
||||||
@ -54,10 +56,22 @@ public:
|
|||||||
void add_positional_argument(PositionalArgument* arg);
|
void add_positional_argument(PositionalArgument* arg);
|
||||||
int parse(int argc, char** argv);
|
int parse(int argc, char** argv);
|
||||||
private:
|
private:
|
||||||
void print_help_message();
|
int print_help_message();
|
||||||
|
int handle_long_option(const char* option, const char* next_value);
|
||||||
|
int handle_short_option(const char option, const char* next_value);
|
||||||
|
int handle_positional_argument(const char* arg);
|
||||||
|
int get_option_data(Option* opt, const char* data_str);
|
||||||
|
int unknown_option(const char* option);
|
||||||
|
int unknown_option(const char option);
|
||||||
|
int incorrect_type(const char* option, const char* got);
|
||||||
|
int missing_argument(const char* option);
|
||||||
|
int missing_positional_argument(const char* arg);
|
||||||
private:
|
private:
|
||||||
const char* m_program_name;
|
const char* m_program_name;
|
||||||
const char* m_description;
|
const char* m_description;
|
||||||
|
const char** m_argv;
|
||||||
|
uint16_t m_opt_index;
|
||||||
|
uint16_t m_pos_index;
|
||||||
std::vector<Option*> m_options;
|
std::vector<Option*> m_options;
|
||||||
std::vector<PositionalArgument*> m_positional_args;
|
std::vector<PositionalArgument*> m_positional_args;
|
||||||
};
|
};
|
||||||
|
4
Makefile
4
Makefile
@ -46,11 +46,11 @@ $(BUILD_DIR):
|
|||||||
|
|
||||||
test: all
|
test: all
|
||||||
$(CXX) test.cpp $(CXXFLAGS) -l$(LIB_NAME) -L$(BUILD_DIR) -o $(BUILD_DIR)/test
|
$(CXX) test.cpp $(CXXFLAGS) -l$(LIB_NAME) -L$(BUILD_DIR) -o $(BUILD_DIR)/test
|
||||||
$(BUILD_DIR)/test
|
-$(BUILD_DIR)/test
|
||||||
|
|
||||||
test_installed:
|
test_installed:
|
||||||
$(CXX) test.cpp $(CXXFLAGS) -l$(LIB_NAME) -o $(BUILD_DIR)/test
|
$(CXX) test.cpp $(CXXFLAGS) -l$(LIB_NAME) -o $(BUILD_DIR)/test
|
||||||
$(BUILD_DIR)/test
|
-$(BUILD_DIR)/test
|
||||||
|
|
||||||
install:
|
install:
|
||||||
$(CP) $(BUILD_DIR)/$(LIB_FILE_NAME) $(INSTALL_DIR)
|
$(CP) $(BUILD_DIR)/$(LIB_FILE_NAME) $(INSTALL_DIR)
|
||||||
|
@ -22,11 +22,14 @@ namespace Arguments
|
|||||||
{
|
{
|
||||||
|
|
||||||
Option::Option(const char* name, const char* description, OPTTYPE type)
|
Option::Option(const char* name, const char* description, OPTTYPE type)
|
||||||
: m_name(name), m_short_name(nullptr), m_description(description), m_type(type), m_found(false) {}
|
: m_name(name), m_short_name(0), m_description(description), m_type(type), m_found(false) {}
|
||||||
|
|
||||||
Option::Option(const char* name, const char* short_name, const char* description, OPTTYPE type)
|
Option::Option(const char* name, const char short_name, const char* description, OPTTYPE type)
|
||||||
: m_name(name), m_short_name(short_name), m_description(description), m_type(type), m_found(false) {}
|
: m_name(name), m_short_name(short_name), m_description(description), m_type(type), m_found(false) {}
|
||||||
|
|
||||||
|
Option::Option(const char short_name, const char* description, OPTTYPE type)
|
||||||
|
: m_name(nullptr), m_short_name(short_name), m_description(description), m_type(type), m_found(false) {}
|
||||||
|
|
||||||
bool Option::found()
|
bool Option::found()
|
||||||
{
|
{
|
||||||
return m_found;
|
return m_found;
|
||||||
@ -67,119 +70,211 @@ void Parser::add_positional_argument(PositionalArgument* arg)
|
|||||||
|
|
||||||
int Parser::parse(int argc, char ** argv)
|
int Parser::parse(int argc, char ** argv)
|
||||||
{
|
{
|
||||||
uint8_t next_pos_arg = 0;
|
for (m_opt_index = 1; m_opt_index < argc; m_opt_index++)
|
||||||
bool all_pos_args_found = m_positional_args.size() == 0;
|
|
||||||
for (uint16_t i = 1; i < argc; i++)
|
|
||||||
{
|
{
|
||||||
if (argv[i][0] == '-') {
|
if (argv[m_opt_index][0] == '-' && argv[m_opt_index][1] == '-') {
|
||||||
bool match_found = false;
|
|
||||||
bool use_long_name = argv[i][1] == '-';
|
|
||||||
Option* matched_arg;
|
|
||||||
const char* option_name;
|
|
||||||
|
|
||||||
for (Option* arg: m_options)
|
int ret = handle_long_option(argv[m_opt_index] + 2, m_opt_index + 1 < argc ? argv[m_opt_index + 1] : nullptr);
|
||||||
{
|
if (ret)
|
||||||
if ((use_long_name ? strcmp(argv[i] + 2, arg->m_name) : strcmp(argv[i] + 1, arg->m_short_name)) == 0) {
|
return ret;
|
||||||
arg->m_found = true;
|
|
||||||
match_found = true;
|
|
||||||
option_name = (use_long_name ? arg->m_name : arg->m_short_name);
|
|
||||||
matched_arg = arg;
|
|
||||||
|
|
||||||
|
} else if (argv[m_opt_index][0] == '-' && argv[m_opt_index][1] != '-') {
|
||||||
|
|
||||||
|
for (uint8_t i = 1; argv[m_opt_index][i] != 0; i++) {
|
||||||
|
bool last = argv[m_opt_index][i + 1] == 0;
|
||||||
|
int ret = handle_short_option(argv[m_opt_index][i], m_opt_index + 1 < argc && last ? argv[m_opt_index + 1] : nullptr);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
if (last)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!match_found) {
|
|
||||||
if (use_long_name && strcmp(argv[i] + 2, "help") == 0) {
|
|
||||||
print_help_message();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
std::cout << m_program_name << ": invalid option: " << argv[i] << std::endl << std::endl;
|
|
||||||
std::cout << "See " << m_program_name << " --help" << std::endl;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
if (matched_arg->m_type != FLAG) {
|
|
||||||
i++;
|
|
||||||
if (i < argc && argv[i][0] != '-') {
|
|
||||||
if (matched_arg->m_type == STRING) {
|
|
||||||
matched_arg->data = argv[i];
|
|
||||||
} else if (matched_arg->m_type == INT) {
|
|
||||||
int* val = new int;
|
|
||||||
*val = stringToInt(argv[i]);
|
|
||||||
if (*val == -1) {
|
|
||||||
std::cout << m_program_name << ": option '" << option_name << "' expects an integer as an argument, got " << argv[i] << " instead" << std::endl;
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
matched_arg->data = val;
|
|
||||||
} else {
|
|
||||||
std::cout << "You appear to have forgotten to add a handler for this type of argument: " << matched_arg->m_type << std::endl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cout << m_program_name << ": Missing argument for option '" << option_name << '\'' << std::endl;
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!all_pos_args_found) {
|
|
||||||
PositionalArgument* arg = m_positional_args[next_pos_arg];
|
|
||||||
if (arg->m_type == INT) {
|
|
||||||
int* val = new int;
|
|
||||||
*val = stringToInt(argv[i]);
|
|
||||||
if (*val == -1) {
|
|
||||||
std::cout << m_program_name << ": argument '" << arg->m_name << "' expects an integer as an argument, got " << argv[i] << " instead" << std::endl;
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
arg->data = val;
|
|
||||||
} else {
|
|
||||||
arg->data = argv[i];
|
|
||||||
}
|
|
||||||
arg->m_found = true;
|
|
||||||
next_pos_arg++;
|
|
||||||
all_pos_args_found = m_positional_args.size() <= next_pos_arg;
|
|
||||||
} else {
|
} else {
|
||||||
std::cout << m_program_name << ": invalid option '" << argv[i] << '\'' << std::endl << std::endl;
|
|
||||||
std::cout << "See " << m_program_name << " --help" << std::endl;
|
|
||||||
|
|
||||||
return -2;
|
int ret = handle_positional_argument(argv[m_opt_index]);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!all_pos_args_found) {
|
if (m_pos_index < m_positional_args.size()) {
|
||||||
for (; next_pos_arg < m_positional_args.size(); next_pos_arg++) {
|
for (; m_pos_index < m_positional_args.size(); m_pos_index++) {
|
||||||
if (m_positional_args[next_pos_arg]->m_req) {
|
if (m_positional_args[m_pos_index]->m_req)
|
||||||
std::cout << m_program_name << ": Missing required positional argument '" << m_positional_args[next_pos_arg]->m_name << "'" << std::endl << std::endl;
|
return missing_positional_argument(m_positional_args[m_pos_index]->m_name);
|
||||||
std::cout << "See " << m_program_name << " --help" << std::endl;
|
|
||||||
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::print_help_message()
|
int Parser::handle_long_option(const char* option, const char* next_value)
|
||||||
|
{
|
||||||
|
Option* matched_option = nullptr;
|
||||||
|
|
||||||
|
for (Option* opt: m_options) {
|
||||||
|
if (strcmp(opt->m_name, option) == 0) {
|
||||||
|
matched_option = opt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched_option == nullptr) {
|
||||||
|
|
||||||
|
if (strcmp("help", option) == 0)
|
||||||
|
return print_help_message();
|
||||||
|
|
||||||
|
return unknown_option(option);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
matched_option->m_found = true;
|
||||||
|
return get_option_data(matched_option, next_value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Congratulations! You have reached an impossible state" << std::endl << std::endl;
|
||||||
|
std::cout << "Reality is broken :)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Parser::handle_short_option(const char option, const char* next_value)
|
||||||
|
{
|
||||||
|
Option* matched_option = nullptr;
|
||||||
|
|
||||||
|
for (Option* opt: m_options) {
|
||||||
|
if (opt->m_short_name == option) {
|
||||||
|
matched_option = opt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched_option == nullptr) {
|
||||||
|
|
||||||
|
if ('h' == option)
|
||||||
|
return print_help_message();
|
||||||
|
|
||||||
|
return unknown_option(option);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
matched_option->m_found = true;
|
||||||
|
return get_option_data(matched_option, next_value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Congratulations! You have reached impossible state #2" << std::endl << std::endl;
|
||||||
|
std::cout << "Reality is broken :)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Parser::handle_positional_argument(const char* arg)
|
||||||
|
{
|
||||||
|
if (m_pos_index >= m_positional_args.size())
|
||||||
|
return unknown_option(arg);
|
||||||
|
|
||||||
|
PositionalArgument* pos_arg = m_positional_args[m_pos_index];
|
||||||
|
|
||||||
|
if (pos_arg->m_type == INT) {
|
||||||
|
int* val = new int;
|
||||||
|
if ((*val = stringToInt(arg)) == -1)
|
||||||
|
return incorrect_type(pos_arg->m_name, arg);
|
||||||
|
|
||||||
|
pos_arg->data = val;
|
||||||
|
} else {
|
||||||
|
pos_arg->data = (void*) arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos_arg->m_found = true;
|
||||||
|
m_pos_index++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Parser::get_option_data(Option* opt, const char* data_str)
|
||||||
|
{
|
||||||
|
if (opt->m_type == FLAG)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (data_str == nullptr || data_str[0] == '-')
|
||||||
|
return missing_argument(opt->m_name);
|
||||||
|
|
||||||
|
if (opt->m_type == INT) {
|
||||||
|
int* val = new int;
|
||||||
|
if ((*val = stringToInt(data_str)) == -1)
|
||||||
|
return incorrect_type(opt->m_name, data_str);
|
||||||
|
opt->data = val;
|
||||||
|
} else {
|
||||||
|
opt->data = (void*) data_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_opt_index++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Parser::unknown_option(const char* option)
|
||||||
|
{
|
||||||
|
std::cout << m_program_name << ": invalid option '" << option << '\'' << std::endl << std::endl;
|
||||||
|
std::cout << "See " << m_program_name << " --help" << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Parser::unknown_option(const char option)
|
||||||
|
{
|
||||||
|
std::cout << m_program_name << ": invalid option: " << option << std::endl << std::endl;
|
||||||
|
std::cout << "See " << m_program_name << " --help" << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Parser::incorrect_type(const char* option, const char* got)
|
||||||
|
{
|
||||||
|
std::cout << m_program_name << ": argument '" << option << "' expects an integer as an argument, got " << got << " instead" << std::endl;
|
||||||
|
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Arguments::Parser::missing_argument(const char* option)
|
||||||
|
{
|
||||||
|
std::cout << m_program_name << ": Missing argument for option '" << option << '\'' << std::endl;
|
||||||
|
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Parser::missing_positional_argument(const char* arg)
|
||||||
|
{
|
||||||
|
std::cout << m_program_name << ": Missing required positional argument '" << arg << "'" << std::endl << std::endl;
|
||||||
|
std::cout << "See " << m_program_name << " --help" << std::endl;
|
||||||
|
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Parser::print_help_message()
|
||||||
{
|
{
|
||||||
std::cout << "Usage: " << m_program_name << (m_options.size() > 0 ? " [OPTIONS]" : "");
|
std::cout << "Usage: " << m_program_name << (m_options.size() > 0 ? " [OPTIONS]" : "");
|
||||||
for (PositionalArgument* arg: m_positional_args)
|
for (PositionalArgument* arg: m_positional_args)
|
||||||
{
|
{
|
||||||
std::cout << " " << arg->m_name;
|
std::cout << " " << arg->m_name;
|
||||||
}
|
}
|
||||||
std::cout << std::endl << std::endl << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
|
||||||
std::cout << "Options:" << std::endl;
|
if (m_options.size() > 0)
|
||||||
|
std::cout << std::endl << std::endl << "Options:" << std::endl;
|
||||||
for (Option* opt: m_options)
|
for (Option* opt: m_options)
|
||||||
{
|
{
|
||||||
std::cout << " -";
|
std::cout << " ";
|
||||||
if (opt->m_short_name != nullptr)
|
if (opt->m_short_name != 0)
|
||||||
std::cout << opt->m_short_name << ", -";
|
std::cout << '-' << opt->m_short_name;
|
||||||
|
|
||||||
std::cout << '-' << opt->m_name;
|
if (opt->m_short_name != 0 && opt->m_name != nullptr)
|
||||||
|
std::cout << ", ";
|
||||||
|
|
||||||
if (opt->m_short_name != nullptr) {
|
if (opt->m_name != nullptr)
|
||||||
for (uint8_t i = 0; i < 20 - strlen(opt->m_name) - strlen(opt->m_short_name); i++)
|
std::cout << "--" << opt->m_name;
|
||||||
|
|
||||||
|
if (opt->m_short_name != 0) {
|
||||||
|
for (uint8_t i = 0; i < 16 - strlen(opt->m_name); i++)
|
||||||
std::cout << ' ';
|
std::cout << ' ';
|
||||||
} else {
|
} else {
|
||||||
for (uint8_t i = 0; i < 20 - strlen(opt->m_name); i++)
|
for (uint8_t i = 0; i < 20 - strlen(opt->m_name); i++)
|
||||||
@ -189,7 +284,10 @@ void Parser::print_help_message()
|
|||||||
std::cout << opt->m_description << std::endl;
|
std::cout << opt->m_description << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << std::endl << std::endl << m_description << std::endl;
|
if (m_description)
|
||||||
|
std::cout << std::endl << std::endl << m_description << std::endl;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Arguments
|
} // namespace Arguments
|
||||||
|
8
test.cpp
8
test.cpp
@ -8,11 +8,11 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
parser.set_description("A test of argument parsing :)");
|
parser.set_description("A test of argument parsing :)");
|
||||||
|
|
||||||
Arguments::Option first("first", "f", "First arg", Arguments::STRING);
|
Arguments::Option first("first", 'f', "First arg", Arguments::STRING);
|
||||||
Arguments::Option second("second", "s", "Second arg", Arguments::INT);
|
Arguments::Option second("second", 's', "Second arg", Arguments::INT);
|
||||||
Arguments::Option third("third", "t", "Third arg", Arguments::FLAG);
|
Arguments::Option third("third", 't', "Third arg", Arguments::FLAG);
|
||||||
|
|
||||||
Arguments::PositionalArgument name("name", true, Arguments::STRING);
|
Arguments::PositionalArgument name("name", false, Arguments::STRING);
|
||||||
Arguments::PositionalArgument age("age", false, Arguments::INT);
|
Arguments::PositionalArgument age("age", false, Arguments::INT);
|
||||||
|
|
||||||
parser.add_option(&first);
|
parser.add_option(&first);
|
||||||
|
Loading…
Reference in New Issue
Block a user