Skip to content

Commit

Permalink
YT-CPPAP-26: Split the implementation to separate header files
Browse files Browse the repository at this point in the history
- Split the library implementation into multiple header files
- Changed the license info in all implementation files
- Changed the names of some elements to better describe their purpose
  • Loading branch information
SpectraL519 committed Feb 1, 2025
1 parent d288aff commit 3afa032
Show file tree
Hide file tree
Showing 29 changed files with 1,537 additions and 1,476 deletions.
1 change: 1 addition & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[submodule "cpp-ap-demo"]
path = cpp-ap-demo
url = https://github.com/SpectraL519/cpp-ap-demo.git
branch = cpp-ap-v2
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ else()
endif()

project(cpp-ap
VERSION 1.0
VERSION 2.0.0
DESCRIPTION "Command-line argument parser for C++20"
HOMEPAGE_URL "https://github.com/SpectraL519/cpp-ap"
LANGUAGES CXX
Expand Down
2 changes: 1 addition & 1 deletion Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ PROJECT_NAME = "CPP-AP"
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER = 1.2
PROJECT_NUMBER = 2.0.0

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023-2024 Jakub Musiał and other contributors
Copyright (c) 2023-2025 Jakub Musiał and other contributors
https://github.com/SpectraL519/cpp-ap

Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
55 changes: 28 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ Command-line argument parser for C++20

## Overview

The goal of the project was to create a light, intuitive and simple to use command-line argument parser library for the `C++20` and newer standards.

The `CPP-AP` library does not require installing any additional tools or heavy libraries, like with `boost::program_options`. Much like with the `Doctest` framework - the only thing you need to do is copy the `argument_parser.hpp` file into the include directory of your project and you're set to go.
`CPP-AP` is a lightweight and feature-rich command-line argument parsing library, designed as an alternative to `boost::program_options`. It offers a modern, intuitive interface allowing for straightforward argument configuration and parsing.

> [!NOTE]
> [v1.0](https://github.com/SpectraL519/cpp-ap/commit/9a9e5360766b732f322ae2efe3cf5ec5f9268eef) of the library has been developed for the *Team Programming* course at the *Wrocław University of Science and Technology*.
Expand Down Expand Up @@ -201,35 +199,38 @@ Parameters which can be specified for both positional and optional arguments inc
- `action` - a function performed after reading an argument's value.
Actions are represented as functions, which take the argument's value as an argument. There are two types of actions:
- Void actions - `void(value_type&)`
- Valued actions - `value_type(const value_type&)`
The default action is an empty void function.
- `modify` actions | `void(value_type&)` - applied to the initialized value of an argument.
Actions can be used to modify a value parsed from the command-line:
```c++
parser.add_optional_argument<std::string>("name", "n")
.action<ap::action_type::modify>([](std::string& name) { name[0] = std::toupper(name[0]); });
```
```c++
parser.add_optional_argument<double>("denominator", "d")
.action<ap::void_action>([](double& value) { value = 1. / value; });
```
- `transform` actions | `value_type(const value_type&)` - applied to the parsed value. The returned value will be used to initialize the argument's value.
or en equivalent valued action:
```c++
std::string to_lower(std::string s) {
for (auto& c : s)
c = static_cast<char>(std::tolower(c));
return s;
}
```c++
parser.add_optional_argument<double>("denominator", "d")
.action<ap::valued_action>([](const double& value) { return 1. / value; });
```
parser.add_optional_argument<std::string>("key", "k")
.action<ap::action_type::transform>(to_lower);
```
Actions can also be used to perform some value checking logic, e.g. the predefined `check_file_exists` which checks if a file with a given name exists:
Actions can also be used to perform some value checking logic instead of actualy modifying or transforming a value, as e.g. the predefined `check_file_exists` which verifies whether a file with a given name exists:
```c++
parser.add_optional_argument("input", "i")
.action<ap::void_action>(ap::action::check_file_exists());
.action<ap::action_type::modify>(ap::action::check_file_exists());
```
> **NOTE:** The default action is an empty `modify` action.
#### Optional argument specific parameters
- `required` - if this option is set for an argument, failure of parsing it's value will result in an error.
- `required` - if this option is set for an argument and it's value is not passed in the command-line, an exception will be thrown.
```c++
parser.add_optional_argument("output", "o").required();
Expand Down Expand Up @@ -293,11 +294,11 @@ The `CPP-AP` library has a few default arguments defined. To add a default argum
```c++
// add positional arguments - pass a std::vector of default positional arguments
parser.default_positional_arguments({...});
// here ... represents a list of ap::default_argument::positional values (or alternatively ap::default_posarg)
// here `...` represents a list of ap::argument::default_positional values
// add optional arguments - pass a std::vector of default optional arguments
parser.default_positional_arguments({...});
// here ... represents a list of ap::default_argument::optional values (or alternatively ap::default_optarg)
// here `...` represents a list of ap::argument::default_optional values
```
The supported default arguments are:
Expand All @@ -307,7 +308,7 @@ The supported default arguments are:
```c++
// equivalent to:
parser.add_positional_argument<std::string>("input")
.action<ap::void_action>(ap::action::check_file_exists())
.action<ap::action_type::modify>(ap::action::check_file_exists())
.help("Input file path");
```
Expand Down Expand Up @@ -343,14 +344,14 @@ The supported default arguments are:
parser.add_optional_argument("input", "i")
.required()
.nargs(1)
.action<ap::void_action>(ap::action::check_file_exists())
.action<ap::action_type::modify>(ap::action::check_file_exists())
.help("Input file path");
// multi_input - equivalent to:
parser.add_optional_argument("input", "i")
.required()
.nargs(ap::nargs::at_least(1))
.action<ap::void_action>(ap::action::check_file_exists())
.action<ap::action_type::modify>(ap::action::check_file_exists())
.help("Input files paths");
```
Expand Down Expand Up @@ -402,7 +403,7 @@ int main(int argc, char* argv[]) {
try {
parser.parse_args(argc, argv);
}
catch (const ap::argument_parser_error& err) {
catch (const ap::argument_parser_exception& err) {
std::cerr << "[ERROR] : " << err.what() << std::endl << parser << std::endl;
std::exit(EXIT_FAILURE);
}
Expand Down Expand Up @@ -437,7 +438,7 @@ int main(int argc, char* argv[]) {
- Positional arguments are parsed first, in the order they were defined in and without a flag.
In the example above the first command-line argument must be the value for `positional_argument`:
In the example above the first command-line argument must be the value for the `positional` argument:
```shell
./power 2
Expand Down
39 changes: 39 additions & 0 deletions include/ap/action/detail/utility.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2023-2025 Jakub Musiał
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.

#pragma once

#include "ap/action/specifiers.hpp"

namespace ap::action::detail {

/**
* @brief The concept is satisfied when `AS` is either a valued or void argument action
* @tparam AS The action specifier type.
*/
template <typename AS>
concept c_action_specifier = ap::detail::c_one_of<AS, action_type::transform, action_type::modify>;

/// @brief Template argument action callable type alias.
template <c_action_specifier AS, ap::detail::c_argument_value_type T>
using callable_type = typename AS::template type<T>;

/// @brief Template argument action callabla variant type alias.
template <ap::detail::c_argument_value_type T>
using action_variant_type =
std::variant<callable_type<action_type::transform, T>, callable_type<action_type::modify, T>>;

/**
* @brief Checks if an argument action variant holds a void action.
* @tparam T The argument value type.
* @param action The action variant.
* @return True if the held action is a void action.
*/
template <ap::detail::c_argument_value_type T>
[[nodiscard]] constexpr inline bool is_modify_action(const action_variant_type<T>& action
) noexcept {
return std::holds_alternative<callable_type<action_type::modify, T>>(action);
}

} // namespace ap::action::detail
28 changes: 28 additions & 0 deletions include/ap/action/predefined_actions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) 2023-2025 Jakub Musiał
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.

#pragma once

#include "ap/error/exceptions.hpp"
#include "detail/utility.hpp"

#include <filesystem>

namespace ap::action {

/// @brief Returns a default argument action.
template <ap::detail::c_argument_value_type T>
detail::callable_type<ap::action_type::modify, T> none() noexcept {
return [](T&) {};
}

/// @brief Returns a predefined action for file name handling arguments. Checks whether a file with the given name exists.
inline detail::callable_type<ap::action_type::modify, std::string> check_file_exists() noexcept {
return [](std::string& file_path) {
if (not std::filesystem::exists(file_path))
throw argument_parser_exception(std::format("File `{}` does not exists!", file_path));
};
}

} // namespace ap::action
43 changes: 43 additions & 0 deletions include/ap/action/specifiers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2023-2025 Jakub Musiał
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.

#pragma once

#include "ap/detail/concepts.hpp"

#include <functional>

namespace ap::action_type {

// TODO:
// * on_read_action
// * on_flag_action

/*
* @brief Represents a transformating action.
*
* Represents an argument action which transforms the parsed value and
* returns a new value with which the argument will be initialized.
*/
struct transform {
template <ap::detail::c_argument_value_type T>
using type = std::function<T(const T&)>;
};

/*
* @brief Represents a modifying action.
*
* Represents an argument action which modifies the value of an
* already initialized argument.
*
* NOTE: The modify action doesn't have to actually modify the
* underlying value - it can simply perform some action on it.
* Example: `ap::action::check_file_exists`
*/
struct modify {
template <ap::detail::c_argument_value_type T>
using type = std::function<void(T&)>;
};

} // namespace ap::action_type
17 changes: 17 additions & 0 deletions include/ap/argument/default.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2023-2025 Jakub Musiał
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.

#pragma once

#include <cstdint>

namespace ap::argument {

/// @brief Enum class representing positional arguments.
enum class default_positional : uint8_t { input, output };

/// @brief Enum class representing optional arguments.
enum class default_optional : uint8_t { help, input, output, multi_input, multi_output };

} // namespace ap::argument
Loading

0 comments on commit 3afa032

Please sign in to comment.