diff --git a/CHANGELOG.md b/CHANGELOG.md index 885093bc5a3..9657092cfbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ If possible, provide tooling that performs the changes, e.g. a shell-script. #### Argument parser * Simplified reading file extensions from formatted files in the input/output file validators. +* API break: The seqan3::value_list_validator is now constructible from a range or a parameter pack but not from an + initialiser list any more (e.g. `seqan3::value_list_validator{{1,2,3}}` does **not** work, use + `seqan3::value_list_validator{1,2,3}` instead). * Enable subcommand argument parsing ([How-to](https://docs.seqan.de/seqan/3-master-user/subcommand_arg_parse.html)). #### Core diff --git a/doc/tutorial/argument_parser/solution6.cpp b/doc/tutorial/argument_parser/solution6.cpp index b1d96c919c4..0465727f61b 100644 --- a/doc/tutorial/argument_parser/solution6.cpp +++ b/doc/tutorial/argument_parser/solution6.cpp @@ -90,7 +90,7 @@ void initialize_argument_parser(argument_parser & parser, cmd_arguments & args) //![value_list_validator] parser.add_option(args.aggregate_by, 'a', "aggregate-by", "Choose your method of aggregation.", - option_spec::DEFAULT, value_list_validator{{"median", "mean"}}); + option_spec::DEFAULT, value_list_validator{"median", "mean"}); //![value_list_validator] parser.add_flag(args.header_is_set, 'H', "header-is-set", "Let us know whether your data file contains a " diff --git a/include/seqan3/argument_parser/validators.hpp b/include/seqan3/argument_parser/validators.hpp index 91bf9ec7593..89d5405fa10 100644 --- a/include/seqan3/argument_parser/validators.hpp +++ b/include/seqan3/argument_parser/validators.hpp @@ -178,11 +178,45 @@ class value_list_validator //!\brief Type of values that are tested by validator using value_type = option_value_type; - /*!\brief Constructing from a vector. - * \param[in] v The vector of valid values to test. + /*!\name Constructors, destructor and assignment + * \{ */ - value_list_validator(std::vector v) : values{std::move(v)} - {} + value_list_validator() = default; //!< Defaulted. + value_list_validator(value_list_validator const &) = default; //!< Defaulted. + value_list_validator(value_list_validator &&) = default; //!< Defaulted. + value_list_validator & operator=(value_list_validator const &) = default; //!< Defaulted. + value_list_validator & operator=(value_list_validator &&) = default; //!< Defaulted. + ~value_list_validator() = default; //!< Defaulted. + + /*!\brief Constructing from a range. + * \tparam range_type The type of range; must model std::ranges::forward_range and value_list_validator::value_type + * must be constructible from the reference type of the given range. + * \param[in] rng The range of valid values to test. + */ + template + //!\cond + requires std::constructible_from> + //!\endcond + value_list_validator(range_type rng) + { + values.clear(); + std::ranges::move(std::move(rng), std::ranges::back_inserter(values)); + } + + /*!\brief Constructing from a parameter pack. + * \tparam option_types The type of option values in the parameter pack; The value_list_validator::value_type must + * be constructible from each type in the parameter pack. + * \param[in] opts The parameter pack values. + */ + template + //!\cond + requires (std::constructible_from && ...) + //!\endcond + value_list_validator(option_types && ...opts) + { + (values.emplace_back(std::forward(opts)), ...); + } + //!\} /*!\brief Tests whether cmp lies inside values. * \param cmp The input value to check. @@ -200,11 +234,10 @@ class value_list_validator * \throws parser_invalid_argument */ template - //!\cond - requires std::convertible_to, value_type const &> - //!\endcond void operator()(range_type const & range) const { + static_assert(std::convertible_to, option_value_type>, + "The value type of the given range must be convertible to the validator::value_type."); std::for_each(range.begin(), range.end(), [&] (auto cmp) { (*this)(cmp); }); } @@ -217,26 +250,51 @@ class value_list_validator private: //!\brief Minimum of the range to test. - std::vector values; + std::vector values{}; }; /*!\brief Type deduction guides * \relates seqan3::value_list_validator * \{ */ -//!\brief Deduction guide for `std::vector` over an arithmetic type. -template -value_list_validator(std::vector) -> value_list_validator; +//!\brief Deduction guide for a parameter pack. +template +//!\cond + requires !std::same_as && !arithmetic +//!\endcond +value_list_validator(first_type, option_types...) -> value_list_validator; + +//!\brief Deduction guide for a parameter pack over an arithmetic type. +template +//!\cond + requires (arithmetic> && ...) +//!\endcond +value_list_validator(option_types...) -> value_list_validator; + +//!\brief Deduction guide for a parameter pack over a const char pointer delegates to value type std::string. +template +//!\cond + requires (std::same_as && ...) +//!\endcond +value_list_validator(option_types...) -> value_list_validator; -//!\brief Deduction guide for `std::initializer_list` over an arithmetic type. -template -value_list_validator(std::initializer_list) -> value_list_validator; +//!\brief Deduction guide for ranges over an arithmetic type. +template +//!\cond + requires arithmetic> +//!\endcond +value_list_validator(range_type && rng) -> value_list_validator; -//!\brief Deduction guide for `std::vector` over `const char *`. -value_list_validator(std::vector) -> value_list_validator; +//!\brief Deduction guide for ranges over a value type convertible to std::string. +template + //!\cond + requires std::convertible_to, std::string> + //!\endcond +value_list_validator(range_type && rng) -> value_list_validator; -//!\brief Deduction guide for `std::initializer_list` over `const char *`. -value_list_validator(std::initializer_list) -> value_list_validator; +//!\brief Deduction guide for ranges. +template +value_list_validator(range_type && rng) -> value_list_validator>; //!\} /*!\brief An abstract base class for the file and directory validators. diff --git a/test/snippet/argument_parser/validators_2.cpp b/test/snippet/argument_parser/validators_2.cpp index 15ce2ffc79a..f18a5efb59c 100644 --- a/test/snippet/argument_parser/validators_2.cpp +++ b/test/snippet/argument_parser/validators_2.cpp @@ -7,7 +7,7 @@ int main(int argc, const char ** argv) //![validator_call] int myint; - seqan3::value_list_validator my_validator{{2, 4, 6, 8, 10}}; + seqan3::value_list_validator my_validator{2, 4, 6, 8, 10}; myparser.add_option(myint,'i',"integer","Give me a number.", seqan3::option_spec::DEFAULT, my_validator); diff --git a/test/unit/argument_parser/format_parse_validators_test.cpp b/test/unit/argument_parser/format_parse_validators_test.cpp index 49e20874834..e805779e472 100644 --- a/test/unit/argument_parser/format_parse_validators_test.cpp +++ b/test/unit/argument_parser/format_parse_validators_test.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -691,16 +692,33 @@ TEST(validator_test, arithmetic_range_validator_error) TEST(validator_test, value_list_validator_success) { + // type deduction + // -------------- + // all arithmetic types are deduced to double in order to easily allow chaining of arithmetic validators + EXPECT_TRUE((std::same_as, decltype(value_list_validator{1, 2, 3})>)); + // The same holds for a range of arithmetic types + std::vector v{1, 2, 3}; + EXPECT_TRUE((std::same_as, decltype(value_list_validator{v})>)); + EXPECT_TRUE((std::same_as, decltype(value_list_validator{v | views::take(2)})>)); + // const char * is deduced to std::string + std::vector v2{"ha", "ba", "ma"}; + EXPECT_TRUE((std::same_as, decltype(value_list_validator{"ha", "ba", "ma"})>)); + EXPECT_TRUE((std::same_as, decltype(value_list_validator{v2})>)); + EXPECT_TRUE((std::same_as, decltype(value_list_validator{v2 | views::take(2)})>)); + + // usage + // ----- std::string option_value; int option_value_int; std::vector option_vector; std::vector option_vector_int; // option + std::vector valid_str_values{"ha", "ba", "ma"}; const char * argv[] = {"./argument_parser_test", "-s", "ba"}; argument_parser parser{"test_parser", 3, argv, false}; parser.add_option(option_value, 's', "string-option", "desc", - option_spec::DEFAULT, value_list_validator{{"ha", "ba", "ma"}}); + option_spec::DEFAULT, value_list_validator{valid_str_values | views::take(2)}); testing::internal::CaptureStderr(); EXPECT_NO_THROW(parser.parse()); @@ -711,7 +729,7 @@ TEST(validator_test, value_list_validator_success) const char * argv2[] = {"./argument_parser_test", "-i", "-21"}; argument_parser parser2{"test_parser", 3, argv2, false}; parser2.add_option(option_value_int, 'i', "int-option", "desc", - option_spec::DEFAULT, value_list_validator{{0, -21, 10}}); + option_spec::DEFAULT, value_list_validator{0, -21, 10}); testing::internal::CaptureStderr(); EXPECT_NO_THROW(parser2.parse()); @@ -721,7 +739,7 @@ TEST(validator_test, value_list_validator_success) // positional option const char * argv3[] = {"./argument_parser_test", "ma"}; argument_parser parser3{"test_parser", 2, argv3, false}; - parser3.add_positional_option(option_value, "desc", value_list_validator{{"ha", "ba", "ma"}}); + parser3.add_positional_option(option_value, "desc", value_list_validator{valid_str_values}); testing::internal::CaptureStderr(); EXPECT_NO_THROW(parser3.parse()); @@ -731,8 +749,7 @@ TEST(validator_test, value_list_validator_success) // positional option - vector const char * argv4[] = {"./argument_parser_test", "ha", "ma"}; argument_parser parser4{"test_parser", 3, argv4, false}; - parser4.add_positional_option(option_vector, "desc", - value_list_validator{{"ha", "ba", "ma"}}); + parser4.add_positional_option(option_vector, "desc", value_list_validator{"ha", "ba", "ma"}); testing::internal::CaptureStderr(); EXPECT_NO_THROW(parser4.parse()); @@ -744,7 +761,7 @@ TEST(validator_test, value_list_validator_success) const char * argv5[] = {"./argument_parser_test", "-i", "-10", "-i", "48"}; argument_parser parser5{"test_parser", 5, argv5, false}; parser5.add_option(option_vector_int, 'i', "int-option", "desc", - option_spec::DEFAULT, value_list_validator{{-10,48,50}}); + option_spec::DEFAULT, value_list_validator{-10, 48, 50}); testing::internal::CaptureStderr(); EXPECT_NO_THROW(parser5.parse()); @@ -757,7 +774,7 @@ TEST(validator_test, value_list_validator_success) const char * argv7[] = {"./argument_parser_test", "-h"}; argument_parser parser7{"test_parser", 2, argv7, false}; parser7.add_option(option_vector_int, 'i', "int-option", "desc", - option_spec::DEFAULT, value_list_validator{{-10,48,50}}); + option_spec::DEFAULT, value_list_validator{-10, 48, 50}); option_vector_int.clear(); testing::internal::CaptureStdout(); @@ -767,7 +784,7 @@ TEST(validator_test, value_list_validator_success) "===========" + basic_options_str + " -i, --int-option (List of signed 32 bit integer's)" - " desc Default: []. Value must be one of [-10,48,50]." + + " desc Default: []. Value must be one of [-10, 48, 50]." + basic_version_str); EXPECT_TRUE(ranges::equal((my_stdout | std::views::filter(!is_space)), expected | std::views::filter(!is_space))); @@ -784,14 +801,14 @@ TEST(validator_test, value_list_validator_error) const char * argv[] = {"./argument_parser_test", "-s", "sa"}; argument_parser parser{"test_parser", 3, argv, false}; parser.add_option(option_value, 's', "string-option", "desc", - option_spec::DEFAULT, value_list_validator{{"ha", "ba", "ma"}}); + option_spec::DEFAULT, value_list_validator{"ha", "ba", "ma"}); EXPECT_THROW(parser.parse(), validation_failed); // positional option const char * argv3[] = {"./argument_parser_test", "30"}; argument_parser parser3{"test_parser", 2, argv3, false}; - parser3.add_positional_option(option_value_int, "desc", value_list_validator{{0, 5, 10}}); + parser3.add_positional_option(option_value_int, "desc", value_list_validator{0, 5, 10}); EXPECT_THROW(parser3.parse(), validation_failed); @@ -799,7 +816,7 @@ TEST(validator_test, value_list_validator_error) const char * argv4[] = {"./argument_parser_test", "fo", "ma"}; argument_parser parser4{"test_parser", 3, argv4, false}; parser4.add_positional_option(option_vector, "desc", - value_list_validator{{"ha", "ba", "ma"}}); + value_list_validator{"ha", "ba", "ma"}); EXPECT_THROW(parser4.parse(), validation_failed); @@ -807,7 +824,7 @@ TEST(validator_test, value_list_validator_error) const char * argv5[] = {"./argument_parser_test", "-i", "-10", "-i", "488"}; argument_parser parser5{"test_parser", 5, argv5, false}; parser5.add_option(option_vector_int, 'i', "int-option", "desc", - option_spec::DEFAULT, value_list_validator{{-10,48,50}}); + option_spec::DEFAULT, value_list_validator{-10, 48, 50}); EXPECT_THROW(parser5.parse(), validation_failed); }