mirror of
https://github.com/sametersoylu/argument-parser.git
synced 2026-04-12 19:31:18 +00:00
feat: introduce help. auto add help through parser generation like another action.
This commit is contained in:
@@ -1,23 +1,72 @@
|
||||
#pragma once
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#ifndef ARGUMENT_PARSER_HPP
|
||||
#define ARGUMENT_PARSER_HPP
|
||||
|
||||
#include <any>
|
||||
#include <atomic>
|
||||
#include <base_convention.hpp>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <traits.hpp>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace argument_parser {
|
||||
namespace internal::atomic {
|
||||
template <typename T> class copyable_atomic {
|
||||
public:
|
||||
copyable_atomic() : value(std::make_shared<std::atomic<T>>()) {}
|
||||
copyable_atomic(T desired) : value(std::make_shared<std::atomic<T>>(desired)) {}
|
||||
|
||||
copyable_atomic(const copyable_atomic &other) : value(other.value) {}
|
||||
copyable_atomic &operator=(const copyable_atomic &other) {
|
||||
if (this != &other) {
|
||||
value = other.value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
copyable_atomic(copyable_atomic &&other) noexcept = default;
|
||||
copyable_atomic &operator=(copyable_atomic &&other) noexcept = default;
|
||||
~copyable_atomic() = default;
|
||||
|
||||
T operator=(T desired) noexcept {
|
||||
store(desired);
|
||||
return desired;
|
||||
}
|
||||
|
||||
operator T() const noexcept {
|
||||
return load();
|
||||
}
|
||||
|
||||
void store(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept {
|
||||
if (value) {
|
||||
value->store(desired, order);
|
||||
}
|
||||
}
|
||||
|
||||
T load(std::memory_order order = std::memory_order_seq_cst) const noexcept {
|
||||
return value ? value->load(order) : T{};
|
||||
}
|
||||
|
||||
T exchange(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept {
|
||||
return value ? value->exchange(desired, order) : T{};
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::atomic<T>> value;
|
||||
};
|
||||
} // namespace internal::atomic
|
||||
|
||||
class action_base {
|
||||
public:
|
||||
virtual ~action_base() = default;
|
||||
@@ -127,6 +176,13 @@ namespace argument_parser {
|
||||
}
|
||||
} // namespace helpers
|
||||
|
||||
/**
|
||||
* @brief Base class for parsing arguments from the command line.
|
||||
*
|
||||
* Note: This class and its methods are NOT thread-safe.
|
||||
* It must be instantiated and used from a single thread (typically the main thread),
|
||||
* as operations such as argument processing and checking rely on thread-local or instance-specific state.
|
||||
*/
|
||||
class base_parser {
|
||||
public:
|
||||
template <typename T>
|
||||
@@ -177,6 +233,18 @@ namespace argument_parser {
|
||||
std::string program_name;
|
||||
std::vector<std::string> parsed_arguments;
|
||||
|
||||
void reset_current_conventions() {
|
||||
_current_conventions = {};
|
||||
}
|
||||
|
||||
void current_conventions(std::initializer_list<conventions::convention const *const> convention_types) {
|
||||
_current_conventions = convention_types;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::initializer_list<conventions::convention const *const> current_conventions() const {
|
||||
return _current_conventions;
|
||||
}
|
||||
|
||||
private:
|
||||
void assert_argument_not_exist(std::string const &short_arg, std::string const &long_arg) const;
|
||||
static void set_argument_status(bool is_required, std::string const &help_text, argument &arg);
|
||||
@@ -224,6 +292,9 @@ namespace argument_parser {
|
||||
std::unordered_map<std::string, int> long_arguments;
|
||||
std::unordered_map<int, std::string> reverse_long_arguments;
|
||||
|
||||
std::initializer_list<conventions::convention const *const> _current_conventions;
|
||||
internal::atomic::copyable_atomic<std::thread::id> creation_thread_id = std::this_thread::get_id();
|
||||
|
||||
std::list<std::function<void(base_parser const &)>> on_complete_events;
|
||||
|
||||
friend class linux_parser;
|
||||
|
||||
@@ -77,9 +77,8 @@ namespace argument_parser::v2 {
|
||||
return base::get_optional<T>(arg);
|
||||
}
|
||||
|
||||
void on_complete(std::function<void(argument_parser::base_parser const &)> const &action) {
|
||||
base::on_complete(action);
|
||||
}
|
||||
using argument_parser::base_parser::display_help;
|
||||
using argument_parser::base_parser::on_complete;
|
||||
|
||||
protected:
|
||||
void set_program_name(std::string p) {
|
||||
@@ -90,6 +89,9 @@ namespace argument_parser::v2 {
|
||||
return base::parsed_arguments;
|
||||
}
|
||||
|
||||
using argument_parser::base_parser::current_conventions;
|
||||
using argument_parser::base_parser::reset_current_conventions;
|
||||
|
||||
private:
|
||||
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
|
||||
void add_argument_impl(ArgsMap const &argument_pairs) {
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace argument_parser {
|
||||
class windows_parser : public v2::base_parser {
|
||||
public:
|
||||
windows_parser();
|
||||
using base_parser::display_help;
|
||||
};
|
||||
} // namespace v2
|
||||
} // namespace argument_parser
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
class deferred_exec {
|
||||
public:
|
||||
deferred_exec(std::function<void()> const &func) : func(func) {}
|
||||
~deferred_exec() {
|
||||
func();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> func;
|
||||
};
|
||||
|
||||
bool contains(std::unordered_map<std::string, int> const &map, std::string const &key) {
|
||||
return map.find(key) != map.end();
|
||||
@@ -101,6 +113,13 @@ namespace argument_parser {
|
||||
}
|
||||
|
||||
void base_parser::handle_arguments(std::initializer_list<conventions::convention const *const> convention_types) {
|
||||
if (std::this_thread::get_id() != this->creation_thread_id.load()) {
|
||||
throw std::runtime_error("handle_arguments must be called from the main thread");
|
||||
}
|
||||
|
||||
deferred_exec reset_current_conventions([this]() { this->reset_current_conventions(); });
|
||||
this->current_conventions(convention_types);
|
||||
|
||||
for (auto it = parsed_arguments.begin(); it != parsed_arguments.end(); ++it) {
|
||||
std::stringstream error_stream;
|
||||
bool arg_correctly_handled = false;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "windows_parser.hpp"
|
||||
#include "argument_parser.hpp"
|
||||
#include "parser_v2.hpp"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <iostream>
|
||||
@@ -96,6 +99,14 @@ namespace argument_parser::v2 {
|
||||
windows_parser::windows_parser() {
|
||||
parse_windows_arguments(ref_parsed_args(),
|
||||
[this](std::string const &program_name) { this->set_program_name(program_name); });
|
||||
|
||||
add_argument({{flags::ShortArgument, "h"},
|
||||
{flags::LongArgument, "help"},
|
||||
{flags::Action, helpers::make_non_parametered_action([this]() {
|
||||
this->display_help(this->current_conventions());
|
||||
std::exit(0);
|
||||
})},
|
||||
{flags::HelpText, "Prints this help text."}});
|
||||
}
|
||||
} // namespace argument_parser::v2
|
||||
|
||||
|
||||
Reference in New Issue
Block a user