mirror of
https://github.com/sametersoylu/argument-parser.git
synced 2026-04-13 03:41:18 +00:00
@@ -1,21 +1,54 @@
|
|||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
project(argument_parser)
|
project(argument_parser VERSION 1.0)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_Release ${CMAKE_CURRENT_SOURCE_DIR}/bin/release)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_Release ${CMAKE_CURRENT_SOURCE_DIR}/bin/release)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_Debug ${CMAKE_CURRENT_SOURCE_DIR}/bin/debug)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_Debug ${CMAKE_CURRENT_SOURCE_DIR}/bin/debug)
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
include_directories(src/headers)
|
|
||||||
include_directories(src/headers/parser)
|
|
||||||
include_directories(src/headers/conventions)
|
|
||||||
include_directories(src/headers/conventions/implementations)
|
|
||||||
include_directories(src/headers/parser/platform_headers)
|
|
||||||
include_directories(src/headers/parser/parsing_traits)
|
|
||||||
|
|
||||||
file(GLOB_RECURSE SRC_FILES "src/source/*.cpp" "src/source/**/*.cpp" "src/source/**/**/*.cpp")
|
file(GLOB_RECURSE SRC_FILES "src/source/*.cpp" "src/source/**/*.cpp" "src/source/**/**/*.cpp")
|
||||||
|
|
||||||
add_executable(test src/main.cpp ${SRC_FILES})
|
add_library(argument_parser ${SRC_FILES})
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
target_include_directories(argument_parser PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/headers>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/headers/parser>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/headers/conventions>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/headers/conventions/implementations>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/headers/parser/platform_headers>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/headers/parser/parsing_traits>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/parser>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/conventions>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/conventions/implementations>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/parser/platform_headers>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/parser/parsing_traits>
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS argument_parser
|
||||||
|
EXPORT argument_parserTargets
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY src/headers/
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(EXPORT argument_parserTargets
|
||||||
|
FILE argument_parserTargets.cmake
|
||||||
|
NAMESPACE argument_parser::
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/argument_parser
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(test src/main.cpp)
|
||||||
|
target_link_libraries(test PRIVATE argument_parser)
|
||||||
@@ -1,23 +1,72 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <list>
|
|
||||||
#include <optional>
|
|
||||||
#include <type_traits>
|
|
||||||
#ifndef ARGUMENT_PARSER_HPP
|
#ifndef ARGUMENT_PARSER_HPP
|
||||||
#define ARGUMENT_PARSER_HPP
|
#define ARGUMENT_PARSER_HPP
|
||||||
|
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <base_convention.hpp>
|
#include <base_convention.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
#include <traits.hpp>
|
#include <traits.hpp>
|
||||||
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace argument_parser {
|
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 {
|
class action_base {
|
||||||
public:
|
public:
|
||||||
virtual ~action_base() = default;
|
virtual ~action_base() = default;
|
||||||
@@ -127,6 +176,13 @@ namespace argument_parser {
|
|||||||
}
|
}
|
||||||
} // namespace helpers
|
} // 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 {
|
class base_parser {
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -177,6 +233,18 @@ namespace argument_parser {
|
|||||||
std::string program_name;
|
std::string program_name;
|
||||||
std::vector<std::string> parsed_arguments;
|
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:
|
private:
|
||||||
void assert_argument_not_exist(std::string const &short_arg, std::string const &long_arg) const;
|
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);
|
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<std::string, int> long_arguments;
|
||||||
std::unordered_map<int, std::string> reverse_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;
|
std::list<std::function<void(base_parser const &)>> on_complete_events;
|
||||||
|
|
||||||
friend class linux_parser;
|
friend class linux_parser;
|
||||||
|
|||||||
@@ -77,9 +77,8 @@ namespace argument_parser::v2 {
|
|||||||
return base::get_optional<T>(arg);
|
return base::get_optional<T>(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_complete(std::function<void(argument_parser::base_parser const &)> const &action) {
|
using argument_parser::base_parser::display_help;
|
||||||
base::on_complete(action);
|
using argument_parser::base_parser::on_complete;
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void set_program_name(std::string p) {
|
void set_program_name(std::string p) {
|
||||||
@@ -90,6 +89,9 @@ namespace argument_parser::v2 {
|
|||||||
return base::parsed_arguments;
|
return base::parsed_arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using argument_parser::base_parser::current_conventions;
|
||||||
|
using argument_parser::base_parser::reset_current_conventions;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
|
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
|
||||||
void add_argument_impl(ArgsMap const &argument_pairs) {
|
void add_argument_impl(ArgsMap const &argument_pairs) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace argument_parser {
|
|||||||
class linux_parser : public v2::base_parser {
|
class linux_parser : public v2::base_parser {
|
||||||
public:
|
public:
|
||||||
linux_parser();
|
linux_parser();
|
||||||
|
using base_parser::display_help;
|
||||||
};
|
};
|
||||||
} // namespace v2
|
} // namespace v2
|
||||||
} // namespace argument_parser
|
} // namespace argument_parser
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace argument_parser {
|
|||||||
class macos_parser : public v2::base_parser {
|
class macos_parser : public v2::base_parser {
|
||||||
public:
|
public:
|
||||||
macos_parser();
|
macos_parser();
|
||||||
|
using base_parser::display_help;
|
||||||
};
|
};
|
||||||
} // namespace v2
|
} // namespace v2
|
||||||
} // namespace argument_parser
|
} // namespace argument_parser
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace argument_parser {
|
|||||||
class windows_parser : public v2::base_parser {
|
class windows_parser : public v2::base_parser {
|
||||||
public:
|
public:
|
||||||
windows_parser();
|
windows_parser();
|
||||||
|
using base_parser::display_help;
|
||||||
};
|
};
|
||||||
} // namespace v2
|
} // namespace v2
|
||||||
} // namespace argument_parser
|
} // namespace argument_parser
|
||||||
|
|||||||
@@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#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) {
|
bool contains(std::unordered_map<std::string, int> const &map, std::string const &key) {
|
||||||
return map.find(key) != map.end();
|
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) {
|
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) {
|
for (auto it = parsed_arguments.begin(); it != parsed_arguments.end(); ++it) {
|
||||||
std::stringstream error_stream;
|
std::stringstream error_stream;
|
||||||
bool arg_correctly_handled = false;
|
bool arg_correctly_handled = false;
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ namespace argument_parser {
|
|||||||
for (std::string line; std::getline(command_line_file, line, '\0');) {
|
for (std::string line; std::getline(command_line_file, line, '\0');) {
|
||||||
parsed_arguments.emplace_back(line);
|
parsed_arguments.emplace_back(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 v2
|
} // namespace v2
|
||||||
} // namespace argument_parser
|
} // namespace argument_parser
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ namespace argument_parser {
|
|||||||
ref_parsed_args().emplace_back(argv[i]);
|
ref_parsed_args().emplace_back(argv[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 v2
|
} // namespace v2
|
||||||
} // namespace argument_parser
|
} // namespace argument_parser
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
#include "windows_parser.hpp"
|
#include "windows_parser.hpp"
|
||||||
|
#include "argument_parser.hpp"
|
||||||
|
#include "parser_v2.hpp"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -96,6 +99,14 @@ namespace argument_parser::v2 {
|
|||||||
windows_parser::windows_parser() {
|
windows_parser::windows_parser() {
|
||||||
parse_windows_arguments(ref_parsed_args(),
|
parse_windows_arguments(ref_parsed_args(),
|
||||||
[this](std::string const &program_name) { this->set_program_name(program_name); });
|
[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
|
} // namespace argument_parser::v2
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user