Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -1405,9 +1405,9 @@ Now all the values, that caused an exception for the `parse_args` example, are c
You can retrieve the argument's value(s) with:

```cpp
(const) value_type value = parser.value<value_type>("argument_name"); // (1)
(const) value_type value = parser.value_or<value_type>("argument_name", fallback_value); // (2)
(const) std::vector<value_type> values = parser.values<value_type>("argument_name"); // (3)
/*const*/ value_type value = parser.value<value_type>("argument_name"); // (1)
/*const*/ value_type value = parser.value_or<value_type>("argument_name", fallback_value); // (2)
const std::vector<value_type>& values = parser.values<value_type>("argument_name"); // (3)
```

1. Returns the given argument's value.
Expand Down
36 changes: 15 additions & 21 deletions include/argon/argument.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#include "argon/nargs/range.hpp"
#include "argon/types.hpp"
#include "argon/util/concepts.hpp"
#include "argon/util/ranges.hpp"

#ifdef AP_TESTING

Expand Down Expand Up @@ -55,7 +54,7 @@ enum class argument_type : bool { positional, optional };
* @tparam T The value type accepted by the argument (defaults to std::string).
*/
template <argument_type ArgT, util::c_argument_value_type T = std::string>
class argument : public detail::argument_base {
class argument : public detail::typed_argument_base<T> {
public:
using value_type = T; ///< The argument's value type alias.
using count_type = nargs::count_type; ///< The argument's count type alias.
Expand Down Expand Up @@ -344,7 +343,7 @@ class argument : public detail::argument_base {
requires(not util::c_is_none<value_type> and std::equality_comparable<value_type>)
{
for (const auto& value : values)
this->_default_values.emplace_back(std::make_any<value_type>(value));
this->_default_values.emplace_back(value);
this->_required = false;
return *this;
}
Expand Down Expand Up @@ -372,7 +371,7 @@ class argument : public detail::argument_base {
argument& default_values(const std::convertible_to<value_type> auto&... values) noexcept
requires(not util::c_is_none<value_type>)
{
(this->_default_values.emplace_back(std::make_any<value_type>(values)), ...);
(this->_default_values.emplace_back(values), ...);
this->_required = false;
return *this;
}
Expand All @@ -389,7 +388,7 @@ class argument : public detail::argument_base {
requires(not util::c_is_none<value_type> and type == argument_type::optional)
{
for (const auto& value : values)
this->_implicit_values.emplace_back(std::make_any<value_type>(value));
this->_implicit_values.emplace_back(value);
return *this;
}

Expand All @@ -414,7 +413,7 @@ class argument : public detail::argument_base {
argument& implicit_values(const std::convertible_to<value_type> auto&... values) noexcept
requires(not util::c_is_none<value_type> and type == argument_type::optional)
{
(this->_implicit_values.emplace_back(std::make_any<value_type>(values)), ...);
(this->_implicit_values.emplace_back(values), ...);
return *this;
}

Expand Down Expand Up @@ -474,15 +473,10 @@ class argument : public detail::argument_base {
if (not this->_choices.empty())
bld.add_range_param("choices", this->_choices);
if (not this->_default_values.empty())
bld.add_range_param(
"default value(s)", util::any_range_cast_view<value_type>(this->_default_values)
);
bld.add_range_param("default value(s)", this->_default_values);
if constexpr (type == argument_type::optional) {
if (not this->_implicit_values.empty())
bld.add_range_param(
"implicit value(s)",
util::any_range_cast_view<value_type>(this->_implicit_values)
);
bld.add_range_param("implicit value(s)", this->_implicit_values);
}
}

Expand Down Expand Up @@ -557,7 +551,7 @@ class argument : public detail::argument_base {
* @note If multiple values are available, the first one is returned.
* @throws std::logic_error if no values are available.
*/
[[nodiscard]] const std::any& value() const override {
[[nodiscard]] detail::arg_return_type<T> value() const override {
if (this->has_parsed_values())
return this->_values.front();

Expand All @@ -570,13 +564,13 @@ class argument : public detail::argument_base {
}

/// @return Reference to the vector of parsed values for the argument.
[[nodiscard]] const std::vector<std::any>& values() const override {
[[nodiscard]] const std::vector<T>& values() const override {
return this->_values_impl();
}

/// @return Reference to the vector of parsed values for the argument.
/// @note For none-type arguments, the method always returns an empty vector.
[[nodiscard]] const std::vector<std::any>& _values_impl() const noexcept
[[nodiscard]] const std::vector<T>& _values_impl() const noexcept
requires(util::c_is_none<value_type>)
{
return this->_values;
Expand All @@ -587,7 +581,7 @@ class argument : public detail::argument_base {
* @note If no parsed values are available, the method attempts to return the predefined values (default/implicit).
* @note The method is enabled only if `value_type` is not `none_type`.
*/
[[nodiscard]] const std::vector<std::any>& _values_impl() const noexcept
[[nodiscard]] const std::vector<T>& _values_impl() const noexcept
requires(not util::c_is_none<value_type>)
{
if (this->has_parsed_values())
Expand Down Expand Up @@ -631,7 +625,7 @@ class argument : public detail::argument_base {
* @note - For positional arguments, the default value list is returned.
* @note - For optional arguments, if the argument has been used, the implicit value list is returned, otherwise the default value list is returned.
*/
[[nodiscard]] const std::vector<std::any>& _predefined_values() const
[[nodiscard]] const std::vector<T>& _predefined_values() const
requires(not util::c_is_none<value_type>)
{
if constexpr (type == argument_type::optional) {
Expand Down Expand Up @@ -726,9 +720,9 @@ class argument : public detail::argument_base {
const argon::detail::argument_name _name; ///< The argument's name.
std::optional<std::string> _help_msg; ///< The argument's help message.
nargs::range _nargs_range; ///< The argument's nargs range attribute value.
[[no_unique_address]] value_arg_specific_type<std::vector<std::any>>
[[no_unique_address]] value_arg_specific_type<std::vector<value_type>>
_default_values; ///< The argument's default value list.
[[no_unique_address]] value_arg_specific_type<optional_specific_type<std::vector<std::any>>>
[[no_unique_address]] value_arg_specific_type<optional_specific_type<std::vector<value_type>>>
_implicit_values; ///< The optional argument's implicit value list.
[[no_unique_address]] value_arg_specific_type<std::vector<value_type>>
_choices; ///< The argument's valid choices collection.
Expand All @@ -748,7 +742,7 @@ class argument : public detail::argument_base {
// parsing result
[[no_unique_address]] optional_specific_type<std::size_t>
_count; ///< The argument's value count.
std::vector<std::any> _values; ///< The argument's parsed values.
std::vector<value_type> _values; ///< The argument's parsed values.

// default attribute values
static constexpr bool _default_required = (type == argument_type::positional);
Expand Down
41 changes: 17 additions & 24 deletions include/argon/argument_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#include "argon/argument_group.hpp"
#include "argon/detail/argument_token.hpp"
#include "argon/types.hpp"
#include "argon/util/ranges.hpp"

#include <algorithm>
#include <format>
Expand Down Expand Up @@ -167,8 +166,8 @@ void add_default_argument(const default_argument, argument_parser&) noexcept;
* .try_parse_args(argc, argv);
*
* // Access parsed argument values
* const std::string input_file = parser.value("input");
* const std::string output_file = parser.value("output");
* const std::string& input_file = parser.value("input");
* const std::string& output_file = parser.value("output");
*
* // Application logic here
* std::cout << "Copying from " << input_file << " to " << output_file << std::endl;
Expand Down Expand Up @@ -782,15 +781,14 @@ class argument_parser {
* @throws argon::lookup_failure, argon::type_error
*/
template <util::c_argument_value_type T = std::string>
[[nodiscard]] T value(std::string_view arg_name) const {
[[nodiscard]] detail::arg_return_type<T> value(std::string_view arg_name) const {
const auto arg = this->_get_argument(arg_name);

try {
return std::any_cast<T>(arg->value());
}
catch (const std::bad_any_cast&) {
const auto* typed_arg = dynamic_cast<const detail::typed_argument_base<T>*>(arg.get());
if (not typed_arg)
throw type_error::invalid_value_type<T>(arg->name());
}

return typed_arg->value();
}

/**
Expand All @@ -806,17 +804,18 @@ class argument_parser {
[[nodiscard]] T value_or(std::string_view arg_name, U&& fallback_value) const {
const auto arg = this->_get_argument(arg_name);

const auto* typed_arg = dynamic_cast<const detail::typed_argument_base<T>*>(arg.get());
if (not typed_arg)
throw type_error::invalid_value_type<T>(arg->name());

try {
return std::any_cast<T>(arg->value());
return typed_arg->value();
}
catch (const std::logic_error&) {
// positional: no value parsed
// optional: no value parsed + no predefined value
return T{std::forward<U>(fallback_value)};
}
catch (const std::bad_any_cast&) {
throw type_error::invalid_value_type<T>(arg->name());
}
}

/**
Expand All @@ -825,22 +824,16 @@ class argument_parser {
* @param arg_name The name of the argument.
* @return The values of the argument as a vector.
* @throws argon::lookup_failure, argon::type_error
* @todo Use std::ranges::to after transition to C++23 for range casting
*/
template <util::c_argument_value_type T = std::string>
[[nodiscard]] std::vector<T> values(std::string_view arg_name) const {
[[nodiscard]] const std::vector<T>& values(std::string_view arg_name) const {
const auto arg = this->_get_argument(arg_name);

try {
std::vector<T> values;
std::ranges::copy(
util::any_range_cast_view<T>(arg->values()), std::back_inserter(values)
);
return values;
}
catch (const std::bad_any_cast&) {
const auto* typed_arg = dynamic_cast<const detail::typed_argument_base<T>*>(arg.get());
if (not typed_arg)
throw type_error::invalid_value_type<T>(arg->name());
}

return typed_arg->values();
}

/**
Expand Down
18 changes: 15 additions & 3 deletions include/argon/detail/argument_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
#include "argon/detail/argument_name.hpp"
#include "argon/detail/help_builder.hpp"

#include <any>
#include <iostream>
#include <string>
#include <vector>

namespace argon {
Expand Down Expand Up @@ -87,12 +87,24 @@ class argument_base {

/// @return The ordering relationship of argument range.
virtual std::weak_ordering nvalues_ordering() const noexcept = 0;
};

/// @brief Returns by value if it is a *small* and trivially copyable. Otherwise, returns by const reference.
/// @note This trait was introduced to avoid problems with `std::vector<bool>` reference proxy objects.
/// @todo Move to the `argon::traits` namespace
template <typename T>
using arg_return_type =
std::conditional_t<std::is_trivially_copyable_v<T> and sizeof(T) <= sizeof(void*), T, const T&>;

/// @brief Strongly-typed intermediate argument interface
template <typename T>
class typed_argument_base : public argument_base {
public:
/// @return Reference to the stored value of the argument.
virtual const std::any& value() const = 0;
virtual arg_return_type<T> value() const = 0;

/// @return Reference to the vector of parsed values of the argument.
virtual const std::vector<std::any>& values() const = 0;
virtual const std::vector<T>& values() const = 0;
};

} // namespace detail
Expand Down
2 changes: 1 addition & 1 deletion include/argon/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ struct type_error : public argument_parser_exception {
return type_error(std::format(
"Invalid value type specified for argument [{}] = {}.",
arg_name.str(),
util::get_demangled_type_name<InvalidType>()
util::demangled_type_name<InvalidType>()
));
}
};
Expand Down
34 changes: 0 additions & 34 deletions include/argon/util/ranges.hpp

This file was deleted.

2 changes: 1 addition & 1 deletion include/argon/util/typing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace argon::util {
* @ingroup util
*/
template <typename T>
constexpr std::string_view get_demangled_type_name() {
constexpr std::string_view demangled_type_name() {
#if defined(__clang__) || defined(__GNUC__)
constexpr std::string_view func_name = __PRETTY_FUNCTION__;
constexpr std::string_view begin_key = "T = ";
Expand Down
4 changes: 2 additions & 2 deletions tests/include/argument_parser_test_fixture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ struct argument_parser_test_fixture {

template <c_argument_value_type T = std::string>
void add_arguments(const std::size_t n_positional_args, std::size_t n_optional_args) {
add_positional_args(n_positional_args);
add_optional_args(n_optional_args, n_positional_args);
add_positional_args<T>(n_positional_args);
add_optional_args<T>(n_optional_args, n_positional_args);
}

[[nodiscard]] arg_token_vec_t init_arg_tokens(
Expand Down
12 changes: 4 additions & 8 deletions tests/include/argument_test_fixture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using argon::argument;
using argon::argument_type;
using argon::detail::arg_return_type;
using argon::detail::argument_name;
using argon::detail::help_builder;
using argon::util::as_string;
Expand Down Expand Up @@ -43,12 +44,7 @@ struct argument_test_fixture {

template <argument_type ArgT, c_argument_value_type T>
void set_value_force(argument<ArgT, T>& arg, const T& value) const {
set_value_force(arg, as_string(value));
}

template <argument_type ArgT, c_argument_value_type T>
void set_value_force(argument<ArgT, T>& arg, const std::string& str_value) const {
arg._values.emplace_back(str_value);
arg._values.emplace_back(value);
}

template <argument_type ArgT, c_argument_value_type T>
Expand Down Expand Up @@ -77,12 +73,12 @@ struct argument_test_fixture {
}

template <argument_type ArgT, c_argument_value_type T>
[[nodiscard]] const std::any& get_value(const argument<ArgT, T>& arg) const {
[[nodiscard]] arg_return_type<T> get_value(const argument<ArgT, T>& arg) const {
return arg.value();
}

template <argument_type ArgT, c_argument_value_type T>
[[nodiscard]] const std::vector<std::any>& get_values(const argument<ArgT, T>& arg) const {
[[nodiscard]] const std::vector<T>& get_values(const argument<ArgT, T>& arg) const {
return arg.values();
}

Expand Down
Loading
Loading