From f95c436072386c02435c8c5622032a3676609050 Mon Sep 17 00:00:00 2001 From: Svenja Mehringer Date: Fri, 11 Mar 2022 08:31:19 +0100 Subject: [PATCH 1/5] [MISC] Remove inheritance of seqan3::detail::customisation_point_object from enumeration_names_cpo. --- include/sharg/auxiliary.hpp | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/include/sharg/auxiliary.hpp b/include/sharg/auxiliary.hpp index 47bca531..52f54d27 100644 --- a/include/sharg/auxiliary.hpp +++ b/include/sharg/auxiliary.hpp @@ -76,12 +76,17 @@ std::unordered_map enumeration_names(t) = delete; //!\ingroup argument_parser //!\remark For a complete overview, take a look at \ref argument_parser template -struct enumeration_names_cpo : public seqan3::detail::customisation_point_object, 1> +struct enumeration_names_cpo { - //!\brief CRTP base class seqan3::detail::customisation_point_object. - using base_t = seqan3::detail::customisation_point_object, 1>; - //!\brief Only this class is allowed to import the constructors from #base_t. (CRTP safety idiom) - using base_t::base_t; + /*!\name Constructors, destructor and assignment + * \{ + */ + constexpr enumeration_names_cpo() = default; //!< Defaulted. + constexpr enumeration_names_cpo(enumeration_names_cpo &&) = default; //!< Defaulted. + constexpr enumeration_names_cpo(enumeration_names_cpo const &) = default; //!< Defaulted. + constexpr enumeration_names_cpo & operator=(enumeration_names_cpo &&) = default; //!< Defaulted. + constexpr enumeration_names_cpo & operator=(enumeration_names_cpo const &) = default; //!< Defaulted. + //!\} /*!\brief If `option_t` isn't std::is_nothrow_default_constructible, enumeration_names will be called with * std::type_identity instead of a default constructed alphabet. @@ -115,6 +120,23 @@ struct enumeration_names_cpo : public seqan3::detail::customisation_point_object ( /*return*/ enumeration_names(option_or_type_identity{}) /*;*/ ); + + /*!\brief SFINAE-friendly call-operator to resolve CPO overload resolution. + * + * This operator implements the actual CPO overload resolution. Overload resolution will try each base class of + * seqan3::detail::priority_tag<1> and seqan3::detail::priority_tag<0>. + * seqan3::detail::priority_tag<0> as first argument to `cpo_overload`. That means a high priority in + * seqan3::detail::priority_tag will be evaluated first and one can define an order which overload should be + * prioritised if multiple overloads match. + * + * It perfectly forwards the result and noexcept-property of the `cpo_overload`. + */ + template + constexpr auto operator()(args_t && ...args) const + SEQAN3_CPO_OVERLOAD_BODY + ( + /*return*/ cpo_overload(seqan3::detail::priority_tag<1>{}, std::forward(args)...) /*;*/ + ); }; } // namespace sharg::detail::adl_only From 188f42e70086f48e4a3fa605bd7372625a61a259 Mon Sep 17 00:00:00 2001 From: Svenja Mehringer Date: Fri, 11 Mar 2022 08:48:43 +0100 Subject: [PATCH 2/5] [MISC] Replace seqan3 CPO macros with plain code. --- include/sharg/auxiliary.hpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/include/sharg/auxiliary.hpp b/include/sharg/auxiliary.hpp index 52f54d27..f109fecb 100644 --- a/include/sharg/auxiliary.hpp +++ b/include/sharg/auxiliary.hpp @@ -101,10 +101,12 @@ struct enumeration_names_cpo * \tparam option_type The type of the option. (Needed to defer instantiation for incomplete types.) */ template - static constexpr auto SEQAN3_CPO_OVERLOAD(seqan3::detail::priority_tag<1>) - ( - /*return*/ sharg::custom::argument_parsing::enumeration_names /*;*/ - ); + static constexpr auto cpo_overload(seqan3::detail::priority_tag<1>) + noexcept(noexcept(sharg::custom::argument_parsing::enumeration_names)) \ + -> decltype(sharg::custom::argument_parsing::enumeration_names) \ + { + return sharg::custom::argument_parsing::enumeration_names; + } /*!\brief CPO overload (check 1 out of 2): argument dependent lookup (ADL), i.e. * `enumeration_names(option_type{})` @@ -116,10 +118,12 @@ struct enumeration_names_cpo * `enumeration_names(std::type_identity{})` will be called. */ template - static constexpr auto SEQAN3_CPO_OVERLOAD(seqan3::detail::priority_tag<0>) - ( - /*return*/ enumeration_names(option_or_type_identity{}) /*;*/ - ); + static constexpr auto cpo_overload(seqan3::detail::priority_tag<0>) + noexcept(noexcept(enumeration_names(option_or_type_identity{}))) + -> decltype(enumeration_names(option_or_type_identity{})) + { + return enumeration_names(option_or_type_identity{}); + } /*!\brief SFINAE-friendly call-operator to resolve CPO overload resolution. * @@ -133,10 +137,11 @@ struct enumeration_names_cpo */ template constexpr auto operator()(args_t && ...args) const - SEQAN3_CPO_OVERLOAD_BODY - ( - /*return*/ cpo_overload(seqan3::detail::priority_tag<1>{}, std::forward(args)...) /*;*/ - ); + noexcept(noexcept(cpo_overload(seqan3::detail::priority_tag<1>{}, std::forward(args)...))) + -> decltype(cpo_overload(seqan3::detail::priority_tag<1>{}, std::forward(args)...)) + { + return cpo_overload(seqan3::detail::priority_tag<1>{}, std::forward(args)...); + } }; } // namespace sharg::detail::adl_only From ae09bd0a8a6f42c9e87345b5f347994ebefb576f Mon Sep 17 00:00:00 2001 From: Svenja Mehringer Date: Fri, 11 Mar 2022 08:52:44 +0100 Subject: [PATCH 3/5] [MISC] Remove dependency of seqan3::detail::priority_tag. --- include/sharg/auxiliary.hpp | 39 ++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/include/sharg/auxiliary.hpp b/include/sharg/auxiliary.hpp index f109fecb..d33d0b8d 100644 --- a/include/sharg/auxiliary.hpp +++ b/include/sharg/auxiliary.hpp @@ -16,8 +16,6 @@ #include #include -#include - #include namespace sharg::custom @@ -65,6 +63,25 @@ struct argument_parsing : argument_parsing } // sharg::custom +namespace sharg::detail +{ +//!\brief A tag that allows controlled overload resolution via implicit base conversion rules. +//!\ingroup core +template +struct priority_tag +//!\cond +// Doxygen fail +: priority_tag +//!\endcond +{}; + +//!\brief Recursion anchor for sharg::detail::priority_tag. +template <> +struct priority_tag<0> +{}; + +} // sharg::detail + namespace sharg::detail::adl_only { @@ -72,7 +89,7 @@ namespace sharg::detail::adl_only template std::unordered_map enumeration_names(t) = delete; -//!\brief seqan3::detail::customisation_point_object (CPO) definition for sharg::enumeration_names. +//!\brief sharg::detail::customisation_point_object (CPO) definition for sharg::enumeration_names. //!\ingroup argument_parser //!\remark For a complete overview, take a look at \ref argument_parser template @@ -101,7 +118,7 @@ struct enumeration_names_cpo * \tparam option_type The type of the option. (Needed to defer instantiation for incomplete types.) */ template - static constexpr auto cpo_overload(seqan3::detail::priority_tag<1>) + static constexpr auto cpo_overload(sharg::detail::priority_tag<1>) noexcept(noexcept(sharg::custom::argument_parsing::enumeration_names)) \ -> decltype(sharg::custom::argument_parsing::enumeration_names) \ { @@ -118,7 +135,7 @@ struct enumeration_names_cpo * `enumeration_names(std::type_identity{})` will be called. */ template - static constexpr auto cpo_overload(seqan3::detail::priority_tag<0>) + static constexpr auto cpo_overload(sharg::detail::priority_tag<0>) noexcept(noexcept(enumeration_names(option_or_type_identity{}))) -> decltype(enumeration_names(option_or_type_identity{})) { @@ -128,19 +145,19 @@ struct enumeration_names_cpo /*!\brief SFINAE-friendly call-operator to resolve CPO overload resolution. * * This operator implements the actual CPO overload resolution. Overload resolution will try each base class of - * seqan3::detail::priority_tag<1> and seqan3::detail::priority_tag<0>. - * seqan3::detail::priority_tag<0> as first argument to `cpo_overload`. That means a high priority in - * seqan3::detail::priority_tag will be evaluated first and one can define an order which overload should be + * sharg::detail::priority_tag<1> and sharg::detail::priority_tag<0>. + * sharg::detail::priority_tag<0> as first argument to `cpo_overload`. That means a high priority in + * sharg::detail::priority_tag will be evaluated first and one can define an order which overload should be * prioritised if multiple overloads match. * * It perfectly forwards the result and noexcept-property of the `cpo_overload`. */ template constexpr auto operator()(args_t && ...args) const - noexcept(noexcept(cpo_overload(seqan3::detail::priority_tag<1>{}, std::forward(args)...))) - -> decltype(cpo_overload(seqan3::detail::priority_tag<1>{}, std::forward(args)...)) + noexcept(noexcept(cpo_overload(sharg::detail::priority_tag<1>{}, std::forward(args)...))) + -> decltype(cpo_overload(sharg::detail::priority_tag<1>{}, std::forward(args)...)) { - return cpo_overload(seqan3::detail::priority_tag<1>{}, std::forward(args)...); + return cpo_overload(sharg::detail::priority_tag<1>{}, std::forward(args)...); } }; From 715722f1cd9bc4908123fe5f306bfcae5722f5a2 Mon Sep 17 00:00:00 2001 From: Svenja Mehringer Date: Fri, 11 Mar 2022 09:07:01 +0100 Subject: [PATCH 4/5] [MISC] Move enumeration_names CPO into it's own header. --- include/sharg/auxiliary.hpp | 229 +--------------- include/sharg/concept.hpp | 2 +- include/sharg/enumeration_names.hpp | 250 ++++++++++++++++++ .../validators_input_file_ext_from_file.cpp | 2 + .../validators_output_file_ext_from_file.cpp | 2 + test/unit/CMakeLists.txt | 1 + test/unit/enumeration_names_test.cpp | 204 ++++++++++++++ test/unit/format_parse_test.cpp | 190 ------------- 8 files changed, 461 insertions(+), 419 deletions(-) create mode 100644 include/sharg/enumeration_names.hpp create mode 100644 test/unit/enumeration_names_test.cpp diff --git a/include/sharg/auxiliary.hpp b/include/sharg/auxiliary.hpp index d33d0b8d..404ad0a5 100644 --- a/include/sharg/auxiliary.hpp +++ b/include/sharg/auxiliary.hpp @@ -12,223 +12,14 @@ #pragma once -#include +#include #include -#include #include -namespace sharg::custom -{ - -/*!\brief A type that can be specialised to provide customisation point implementations for the sharg::argument_parser - * such that third party types may be adapted. - * \tparam t The type you wish to specialise for. - * \ingroup argument_parser - * - * \details - * - * ### Named Enumerations - * - * In order to use a third party type within the sharg::argument_parser::add_option or - * sharg::argument_parser::add_positional_option call, you can specialise this struct in the following way: - * - * \include test/snippet/argument_parser/custom_argument_parsing_enumeration.cpp - * - * Please note that by default the `t const`, `t &` and `t const &` specialisations of this class inherit the - * specialisation for `t` so you usually only need to provide a specialisation for `t`. - * - * \note Only use this if you cannot provide respective functions in your namespace. See the tutorial - * \ref tutorial_argument_parser for an example of customising a type within your own namespace. - * - * \remark For a complete overview, take a look at \ref argument_parser - */ -template -struct argument_parsing -{}; // forward - -//!\cond -template -struct argument_parsing : argument_parsing -{}; - -template -struct argument_parsing : argument_parsing -{}; - -template -struct argument_parsing : argument_parsing -{}; -//!\endcond - -} // sharg::custom - -namespace sharg::detail -{ -//!\brief A tag that allows controlled overload resolution via implicit base conversion rules. -//!\ingroup core -template -struct priority_tag -//!\cond -// Doxygen fail -: priority_tag -//!\endcond -{}; - -//!\brief Recursion anchor for sharg::detail::priority_tag. -template <> -struct priority_tag<0> -{}; - -} // sharg::detail - -namespace sharg::detail::adl_only -{ - -//!\brief Poison-pill overload to prevent non-ADL forms of unqualified lookup. -template -std::unordered_map enumeration_names(t) = delete; - -//!\brief sharg::detail::customisation_point_object (CPO) definition for sharg::enumeration_names. -//!\ingroup argument_parser -//!\remark For a complete overview, take a look at \ref argument_parser -template -struct enumeration_names_cpo -{ - /*!\name Constructors, destructor and assignment - * \{ - */ - constexpr enumeration_names_cpo() = default; //!< Defaulted. - constexpr enumeration_names_cpo(enumeration_names_cpo &&) = default; //!< Defaulted. - constexpr enumeration_names_cpo(enumeration_names_cpo const &) = default; //!< Defaulted. - constexpr enumeration_names_cpo & operator=(enumeration_names_cpo &&) = default; //!< Defaulted. - constexpr enumeration_names_cpo & operator=(enumeration_names_cpo const &) = default; //!< Defaulted. - //!\} - - /*!\brief If `option_t` isn't std::is_nothrow_default_constructible, enumeration_names will be called with - * std::type_identity instead of a default constructed alphabet. - */ - template - using option_or_type_identity - = std::conditional_t>, - std::remove_cvref_t, - std::type_identity>; - - /*!\brief CPO overload (check 1 out of 2): explicit customisation via `sharg::custom::argument_parsing` - * \tparam option_type The type of the option. (Needed to defer instantiation for incomplete types.) - */ - template - static constexpr auto cpo_overload(sharg::detail::priority_tag<1>) - noexcept(noexcept(sharg::custom::argument_parsing::enumeration_names)) \ - -> decltype(sharg::custom::argument_parsing::enumeration_names) \ - { - return sharg::custom::argument_parsing::enumeration_names; - } - - /*!\brief CPO overload (check 1 out of 2): argument dependent lookup (ADL), i.e. - * `enumeration_names(option_type{})` - * \tparam option_type The type of the option. (Needed to defer instantiation for incomplete types.) - * - * \details - * - * If the option_type isn't std::is_nothrow_default_constructible, - * `enumeration_names(std::type_identity{})` will be called. - */ - template - static constexpr auto cpo_overload(sharg::detail::priority_tag<0>) - noexcept(noexcept(enumeration_names(option_or_type_identity{}))) - -> decltype(enumeration_names(option_or_type_identity{})) - { - return enumeration_names(option_or_type_identity{}); - } - - /*!\brief SFINAE-friendly call-operator to resolve CPO overload resolution. - * - * This operator implements the actual CPO overload resolution. Overload resolution will try each base class of - * sharg::detail::priority_tag<1> and sharg::detail::priority_tag<0>. - * sharg::detail::priority_tag<0> as first argument to `cpo_overload`. That means a high priority in - * sharg::detail::priority_tag will be evaluated first and one can define an order which overload should be - * prioritised if multiple overloads match. - * - * It perfectly forwards the result and noexcept-property of the `cpo_overload`. - */ - template - constexpr auto operator()(args_t && ...args) const - noexcept(noexcept(cpo_overload(sharg::detail::priority_tag<1>{}, std::forward(args)...))) - -> decltype(cpo_overload(sharg::detail::priority_tag<1>{}, std::forward(args)...)) - { - return cpo_overload(sharg::detail::priority_tag<1>{}, std::forward(args)...); - } -}; - -} // namespace sharg::detail::adl_only - namespace sharg { -/*!\name Customisation Points - * \{ - */ - -/*!\brief Return a conversion map from std::string_view to option_type. - * \tparam your_type Type of the value to retrieve the conversion map for. - * \param value The value is not used, just its type. - * \returns A std::unordered_map that maps a string identifier to a value of your_type. - * \ingroup argument_parser - * \details - * - * This is a function object. Invoke it with the parameter(s) specified above. - * - * It acts as a wrapper and looks for two possible implementations (in this order): - * - * 1. A static member `enumeration_names` in `sharg::custom::argument_parsing` that is of type - * `std::unordered_map>`. - * 2. A free function `enumeration_names(your_type const a)` in the namespace of your type (or as `friend`) which - * returns a `std::unordered_map>`. - * - * ### Example - * - * If you are working on a type in your own namespace, you should implement a free function like this: - * - * \include test/snippet/argument_parser/custom_enumeration.cpp - * - * **Only if you cannot access the namespace of your type to customize** you may specialize - * the sharg::custom::argument_parsing struct like this: - * - * \include test/snippet/argument_parser/custom_argument_parsing_enumeration.cpp - * - * \remark For a complete overview, take a look at \ref argument_parser - * - * ### Customisation point - * - * This is a customisation point (see \ref about_customisation). To specify the behaviour for your own type, - * simply provide one of the two functions specified above. - */ -template -//!\cond - requires requires { { detail::adl_only::enumeration_names_cpo{}() }; } -//!\endcond -inline auto const enumeration_names = detail::adl_only::enumeration_names_cpo{}(); -//!\} - -/*!\concept sharg::named_enumeration - * \brief Checks whether the free function sharg::enumeration_names can be called on the type. - * \ingroup argument_parser - * \tparam option_type The type to check. - * - * ### Requirements - * - * * A instance of sharg::enumeration_names must exist and be of type - * `std::unordered_map`. - * - * \remark For a complete overview, take a look at \ref argument_parser - */ -template -concept named_enumeration = requires -{ - { sharg::enumeration_names }; -}; - /*!\brief Used to further specify argument_parser options/flags. * \ingroup argument_parser * @@ -333,21 +124,3 @@ struct argument_parser_meta_data // holds all meta information }; } // namespace sharg - -//!\cond -namespace std -{ -template - requires sharg::named_enumeration> -inline ostream & operator<<(ostream & s, option_type && op) -{ - for (auto & [key, value] : sharg::enumeration_names) - { - if (op == value) - return s << key; - } - - return s << ""; -} -} // namespace std -//!\endcond diff --git a/include/sharg/concept.hpp b/include/sharg/concept.hpp index fbf137af..8dc46fce 100644 --- a/include/sharg/concept.hpp +++ b/include/sharg/concept.hpp @@ -14,7 +14,7 @@ #include -#include +#include namespace sharg { diff --git a/include/sharg/enumeration_names.hpp b/include/sharg/enumeration_names.hpp new file mode 100644 index 00000000..52d0db77 --- /dev/null +++ b/include/sharg/enumeration_names.hpp @@ -0,0 +1,250 @@ +// ----------------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/sharg-parser/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------------- + +/*!\file + * \author Svenja Mehringer + * \brief Provides auxiliary information. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace sharg::custom +{ + +/*!\brief A type that can be specialised to provide customisation point implementations for the sharg::argument_parser + * such that third party types may be adapted. + * \tparam t The type you wish to specialise for. + * \ingroup argument_parser + * + * \details + * + * ### Named Enumerations + * + * In order to use a third party type within the sharg::argument_parser::add_option or + * sharg::argument_parser::add_positional_option call, you can specialise this struct in the following way: + * + * \include test/snippet/argument_parser/custom_argument_parsing_enumeration.cpp + * + * Please note that by default the `t const`, `t &` and `t const &` specialisations of this class inherit the + * specialisation for `t` so you usually only need to provide a specialisation for `t`. + * + * \note Only use this if you cannot provide respective functions in your namespace. See the tutorial + * \ref tutorial_argument_parser for an example of customising a type within your own namespace. + * + * \remark For a complete overview, take a look at \ref argument_parser + */ +template +struct argument_parsing +{}; // forward + +//!\cond +template +struct argument_parsing : argument_parsing +{}; + +template +struct argument_parsing : argument_parsing +{}; + +template +struct argument_parsing : argument_parsing +{}; +//!\endcond + +} // sharg::custom + +namespace sharg::detail +{ +//!\brief A tag that allows controlled overload resolution via implicit base conversion rules. +//!\ingroup core +template +struct priority_tag +//!\cond +// Doxygen fail +: priority_tag +//!\endcond +{}; + +//!\brief Recursion anchor for sharg::detail::priority_tag. +template <> +struct priority_tag<0> +{}; + +} // sharg::detail + +namespace sharg::detail::adl_only +{ + +//!\brief Poison-pill overload to prevent non-ADL forms of unqualified lookup. +template +std::unordered_map enumeration_names(t) = delete; + +//!\brief sharg::detail::customisation_point_object (CPO) definition for sharg::enumeration_names. +//!\ingroup argument_parser +//!\remark For a complete overview, take a look at \ref argument_parser +template +struct enumeration_names_cpo +{ + /*!\name Constructors, destructor and assignment + * \{ + */ + constexpr enumeration_names_cpo() = default; //!< Defaulted. + constexpr enumeration_names_cpo(enumeration_names_cpo &&) = default; //!< Defaulted. + constexpr enumeration_names_cpo(enumeration_names_cpo const &) = default; //!< Defaulted. + constexpr enumeration_names_cpo & operator=(enumeration_names_cpo &&) = default; //!< Defaulted. + constexpr enumeration_names_cpo & operator=(enumeration_names_cpo const &) = default; //!< Defaulted. + //!\} + + /*!\brief If `option_t` isn't std::is_nothrow_default_constructible, enumeration_names will be called with + * std::type_identity instead of a default constructed alphabet. + */ + template + using option_or_type_identity + = std::conditional_t>, + std::remove_cvref_t, + std::type_identity>; + + /*!\brief CPO overload (check 1 out of 2): explicit customisation via `sharg::custom::argument_parsing` + * \tparam option_type The type of the option. (Needed to defer instantiation for incomplete types.) + */ + template + static constexpr auto cpo_overload(sharg::detail::priority_tag<1>) + noexcept(noexcept(sharg::custom::argument_parsing::enumeration_names)) \ + -> decltype(sharg::custom::argument_parsing::enumeration_names) \ + { + return sharg::custom::argument_parsing::enumeration_names; + } + + /*!\brief CPO overload (check 1 out of 2): argument dependent lookup (ADL), i.e. + * `enumeration_names(option_type{})` + * \tparam option_type The type of the option. (Needed to defer instantiation for incomplete types.) + * + * \details + * + * If the option_type isn't std::is_nothrow_default_constructible, + * `enumeration_names(std::type_identity{})` will be called. + */ + template + static constexpr auto cpo_overload(sharg::detail::priority_tag<0>) + noexcept(noexcept(enumeration_names(option_or_type_identity{}))) + -> decltype(enumeration_names(option_or_type_identity{})) + { + return enumeration_names(option_or_type_identity{}); + } + + /*!\brief SFINAE-friendly call-operator to resolve CPO overload resolution. + * + * This operator implements the actual CPO overload resolution. Overload resolution will try each base class of + * sharg::detail::priority_tag<1> and sharg::detail::priority_tag<0>. + * sharg::detail::priority_tag<0> as first argument to `cpo_overload`. That means a high priority in + * sharg::detail::priority_tag will be evaluated first and one can define an order which overload should be + * prioritised if multiple overloads match. + * + * It perfectly forwards the result and noexcept-property of the `cpo_overload`. + */ + template + constexpr auto operator()(args_t && ...args) const + noexcept(noexcept(cpo_overload(sharg::detail::priority_tag<1>{}, std::forward(args)...))) + -> decltype(cpo_overload(sharg::detail::priority_tag<1>{}, std::forward(args)...)) + { + return cpo_overload(sharg::detail::priority_tag<1>{}, std::forward(args)...); + } +}; + +} // namespace sharg::detail::adl_only + +namespace sharg +{ + +/*!\name Customisation Points + * \{ + */ + +/*!\brief Return a conversion map from std::string_view to option_type. + * \tparam your_type Type of the value to retrieve the conversion map for. + * \param value The value is not used, just its type. + * \returns A std::unordered_map that maps a string identifier to a value of your_type. + * \ingroup argument_parser + * \details + * + * This is a function object. Invoke it with the parameter(s) specified above. + * + * It acts as a wrapper and looks for two possible implementations (in this order): + * + * 1. A static member `enumeration_names` in `sharg::custom::argument_parsing` that is of type + * `std::unordered_map>`. + * 2. A free function `enumeration_names(your_type const a)` in the namespace of your type (or as `friend`) which + * returns a `std::unordered_map>`. + * + * ### Example + * + * If you are working on a type in your own namespace, you should implement a free function like this: + * + * \include test/snippet/argument_parser/custom_enumeration.cpp + * + * **Only if you cannot access the namespace of your type to customize** you may specialize + * the sharg::custom::argument_parsing struct like this: + * + * \include test/snippet/argument_parser/custom_argument_parsing_enumeration.cpp + * + * \remark For a complete overview, take a look at \ref argument_parser + * + * ### Customisation point + * + * This is a customisation point (see \ref about_customisation). To specify the behaviour for your own type, + * simply provide one of the two functions specified above. + */ +template +//!\cond + requires requires { { detail::adl_only::enumeration_names_cpo{}() }; } +//!\endcond +inline auto const enumeration_names = detail::adl_only::enumeration_names_cpo{}(); +//!\} + +/*!\concept sharg::named_enumeration + * \brief Checks whether the free function sharg::enumeration_names can be called on the type. + * \ingroup argument_parser + * \tparam option_type The type to check. + * + * ### Requirements + * + * * A instance of sharg::enumeration_names must exist and be of type + * `std::unordered_map`. + * + * \remark For a complete overview, take a look at \ref argument_parser + */ +template +concept named_enumeration = requires +{ + { sharg::enumeration_names }; +}; + +} // namespace sharg + +//!\cond +namespace std +{ +template + requires sharg::named_enumeration> +inline ostream & operator<<(ostream & s, option_type && op) +{ + for (auto & [key, value] : sharg::enumeration_names) + { + if (op == value) + return s << key; + } + + return s << ""; +} +} // namespace std +//!\endcond diff --git a/test/snippet/validators_input_file_ext_from_file.cpp b/test/snippet/validators_input_file_ext_from_file.cpp index ab99cf81..27c80c57 100644 --- a/test/snippet/validators_input_file_ext_from_file.cpp +++ b/test/snippet/validators_input_file_ext_from_file.cpp @@ -1,3 +1,5 @@ +#include + #include int main() diff --git a/test/snippet/validators_output_file_ext_from_file.cpp b/test/snippet/validators_output_file_ext_from_file.cpp index ee8f81f3..dcbe10c4 100644 --- a/test/snippet/validators_output_file_ext_from_file.cpp +++ b/test/snippet/validators_output_file_ext_from_file.cpp @@ -1,3 +1,5 @@ +#include + #include int main() diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 97a6d88b..188cffe1 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -56,6 +56,7 @@ list_missing_unit_tests() add_subdirectories () +sharg_test(enumeration_names_test.cpp) sharg_test(format_parse_test.cpp) sharg_test(format_parse_validators_test.cpp) sharg_test(argument_parser_design_error_test.cpp) diff --git a/test/unit/enumeration_names_test.cpp b/test/unit/enumeration_names_test.cpp new file mode 100644 index 00000000..50f584b6 --- /dev/null +++ b/test/unit/enumeration_names_test.cpp @@ -0,0 +1,204 @@ +// ----------------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/sharg-parser/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------------- + +#include + +#include + +#include + +namespace foo +{ +enum class bar +{ + one, + two, + three +}; + +auto enumeration_names(bar) +{ + return std::unordered_map{{"one", bar::one}, {"two", bar::two}, {"three", bar::three}}; +} +} // namespace foo + +namespace Other +{ +enum class bar +{ + one, + two +}; +} // namespace Other + +namespace sharg::custom +{ +template <> +struct argument_parsing +{ + static inline std::unordered_map const enumeration_names + { + {"one", Other::bar::one}, {"1", Other::bar::one}, {"two", Other::bar::two}, {"2", Other::bar::two} + }; +}; +} // namespace sharg::custom + +TEST(parse_type_test, parse_success_enum_option) +{ + { + foo::bar option_value{}; + + const char * argv[] = {"./argument_parser_test", "-e", "two"}; + sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; + parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); + + EXPECT_NO_THROW(parser.parse()); + EXPECT_TRUE(option_value == foo::bar::two); + } + + { + Other::bar option_value{}; + + const char * argv[] = {"./argument_parser_test", "-e", "two"}; + sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; + parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); + + EXPECT_NO_THROW(parser.parse()); + EXPECT_TRUE(option_value == Other::bar::two); + } +} + +TEST(parse_type_test, parse_error_enum_option) +{ + foo::bar option_value{}; + + const char * argv[] = {"./argument_parser_test", "-e", "four"}; + sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; + parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); + + EXPECT_THROW(parser.parse(), sharg::user_input_error); +} + +// https://github.com/seqan/seqan3/issues/2464 +TEST(parse_test, issue2464) +{ + using option_t = foo::bar; + // Using a non-existing value of foo::bar should throw. + { + const char * argv[] = {"./argument_parser_test", "-e", "nine"}; + + option_t option_value{}; + + sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; + parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); + EXPECT_THROW(parser.parse(), sharg::user_input_error); + } + { + const char * argv[] = {"./argument_parser_test", "-e", "one", "-e", "nine"}; + + std::vector option_values{}; + + sharg::argument_parser parser{"test_parser", 5, argv, sharg::update_notifications::off}; + parser.add_option(option_values, 'e', "enum-option", "this is an enum option."); + EXPECT_THROW(parser.parse(), sharg::user_input_error); + } + + // Invalid inputs for enums are handled before any validator is evaluated. + // Thus the exception will be sharg::user_input_error and not sharg::validation_error. + { + const char * argv[] = {"./argument_parser_test", "-e", "nine"}; + + sharg::value_list_validator enum_validator{(sharg::enumeration_names | std::views::values)}; + option_t option_value{}; + + sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; + parser.add_option(option_value, 'e', "enum-option", "this is an enum option.", sharg::option_spec::advanced, + enum_validator); + EXPECT_THROW(parser.parse(), sharg::user_input_error); + } + { + const char * argv[] = {"./argument_parser_test", "-e", "one", "-e", "nine"}; + + sharg::value_list_validator enum_validator{(sharg::enumeration_names | std::views::values)}; + std::vector option_values{}; + + sharg::argument_parser parser{"test_parser", 5, argv, sharg::update_notifications::off}; + parser.add_option(option_values, 'e', "enum-option", "this is an enum option.", sharg::option_spec::advanced, + enum_validator); + EXPECT_THROW(parser.parse(), sharg::user_input_error); + } +} + +TEST(parse_test, enum_error_message) +{ + // foo::bar does not contain duplicate values + { + const char * argv[] = {"./argument_parser_test", "-e", "nine"}; + + foo::bar option_value{}; + + sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; + parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); + + std::string expected_message{"You have chosen an invalid input value: nine. " + "Please use one of: [one, two, three]"}; + + try + { + parser.parse(); + FAIL(); + } + catch (sharg::user_input_error const & exception) + { + EXPECT_EQ(expected_message, exception.what()); + } + catch (...) + { + FAIL(); + } + } + // Other::bar does contain duplicate values + { + const char * argv[] = {"./argument_parser_test", "-e", "nine"}; + + Other::bar option_value{}; + + sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; + parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); + + std::string expected_message{"You have chosen an invalid input value: nine. " + "Please use one of: [1, one, 2, two]"}; + + try + { + parser.parse(); + FAIL(); + } + catch (sharg::user_input_error const & exception) + { + EXPECT_EQ(expected_message, exception.what()); + } + catch (...) + { + FAIL(); + } + } +} + +// https://github.com/seqan/seqan3/pull/2381 +TEST(parse_test, container_options) +{ + std::vector option_values{}; + + const char * argv[] = {"./argument_parser_test", "-e", "two", "-e", "one", "-e", "three"}; + sharg::argument_parser parser{"test_parser", 7, argv, sharg::update_notifications::off}; + parser.add_option(option_values, 'e', "enum-option", "this is an enum option."); + + EXPECT_NO_THROW(parser.parse()); + + EXPECT_TRUE(option_values == (std::vector{foo::bar::two, foo::bar::one, foo::bar::three})); +} diff --git a/test/unit/format_parse_test.cpp b/test/unit/format_parse_test.cpp index a5fe3c2a..da8a7e5c 100644 --- a/test/unit/format_parse_test.cpp +++ b/test/unit/format_parse_test.cpp @@ -982,184 +982,6 @@ TEST(parse_test, is_option_set) EXPECT_THROW(parser.is_option_set('\0'), sharg::design_error); } -namespace foo -{ -enum class bar -{ - one, - two, - three -}; - -auto enumeration_names(bar) -{ - return std::unordered_map{{"one", bar::one}, {"two", bar::two}, {"three", bar::three}}; -} -} // namespace foo - -namespace Other -{ -enum class bar -{ - one, - two -}; -} // namespace Other - -namespace sharg::custom -{ -template <> -struct argument_parsing -{ - static inline std::unordered_map const enumeration_names - { - {"one", Other::bar::one}, {"1", Other::bar::one}, {"two", Other::bar::two}, {"2", Other::bar::two} - }; -}; -} // namespace sharg::custom - -TEST(parse_type_test, parse_success_enum_option) -{ - { - foo::bar option_value{}; - - const char * argv[] = {"./argument_parser_test", "-e", "two"}; - sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; - parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); - - EXPECT_NO_THROW(parser.parse()); - EXPECT_TRUE(option_value == foo::bar::two); - } - - { - Other::bar option_value{}; - - const char * argv[] = {"./argument_parser_test", "-e", "two"}; - sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; - parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); - - EXPECT_NO_THROW(parser.parse()); - EXPECT_TRUE(option_value == Other::bar::two); - } -} - -TEST(parse_type_test, parse_error_enum_option) -{ - foo::bar option_value{}; - - const char * argv[] = {"./argument_parser_test", "-e", "four"}; - sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; - parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); - - EXPECT_THROW(parser.parse(), sharg::user_input_error); -} - -// https://github.com/seqan/seqan3/issues/2464 -TEST(parse_test, issue2464) -{ - using option_t = foo::bar; - // Using a non-existing value of foo::bar should throw. - { - const char * argv[] = {"./argument_parser_test", "-e", "nine"}; - - option_t option_value{}; - - sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; - parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); - EXPECT_THROW(parser.parse(), sharg::user_input_error); - } - { - const char * argv[] = {"./argument_parser_test", "-e", "one", "-e", "nine"}; - - std::vector option_values{}; - - sharg::argument_parser parser{"test_parser", 5, argv, sharg::update_notifications::off}; - parser.add_option(option_values, 'e', "enum-option", "this is an enum option."); - EXPECT_THROW(parser.parse(), sharg::user_input_error); - } - - // Invalid inputs for enums are handled before any validator is evaluated. - // Thus the exception will be sharg::user_input_error and not sharg::validation_error. - { - const char * argv[] = {"./argument_parser_test", "-e", "nine"}; - - sharg::value_list_validator enum_validator{(sharg::enumeration_names | std::views::values)}; - option_t option_value{}; - - sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; - parser.add_option(option_value, 'e', "enum-option", "this is an enum option.", sharg::option_spec::advanced, - enum_validator); - EXPECT_THROW(parser.parse(), sharg::user_input_error); - } - { - const char * argv[] = {"./argument_parser_test", "-e", "one", "-e", "nine"}; - - sharg::value_list_validator enum_validator{(sharg::enumeration_names | std::views::values)}; - std::vector option_values{}; - - sharg::argument_parser parser{"test_parser", 5, argv, sharg::update_notifications::off}; - parser.add_option(option_values, 'e', "enum-option", "this is an enum option.", sharg::option_spec::advanced, - enum_validator); - EXPECT_THROW(parser.parse(), sharg::user_input_error); - } -} - -TEST(parse_test, enum_error_message) -{ - // foo::bar does not contain duplicate values - { - const char * argv[] = {"./argument_parser_test", "-e", "nine"}; - - foo::bar option_value{}; - - sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; - parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); - - std::string expected_message{"You have chosen an invalid input value: nine. " - "Please use one of: [one, two, three]"}; - - try - { - parser.parse(); - FAIL(); - } - catch (sharg::user_input_error const & exception) - { - EXPECT_EQ(expected_message, exception.what()); - } - catch (...) - { - FAIL(); - } - } - // Other::bar does contain duplicate values - { - const char * argv[] = {"./argument_parser_test", "-e", "nine"}; - - Other::bar option_value{}; - - sharg::argument_parser parser{"test_parser", 3, argv, sharg::update_notifications::off}; - parser.add_option(option_value, 'e', "enum-option", "this is an enum option."); - - std::string expected_message{"You have chosen an invalid input value: nine. " - "Please use one of: [1, one, 2, two]"}; - - try - { - parser.parse(); - FAIL(); - } - catch (sharg::user_input_error const & exception) - { - EXPECT_EQ(expected_message, exception.what()); - } - catch (...) - { - FAIL(); - } - } -} - // https://github.com/seqan/seqan3/issues/2835 TEST(parse_test, error_message_parsing) { @@ -1191,18 +1013,6 @@ TEST(parse_test, error_message_parsing) // https://github.com/seqan/seqan3/pull/2381 TEST(parse_test, container_options) { - { - std::vector option_values{}; - - const char * argv[] = {"./argument_parser_test", "-e", "two", "-e", "one", "-e", "three"}; - sharg::argument_parser parser{"test_parser", 7, argv, sharg::update_notifications::off}; - parser.add_option(option_values, 'e', "enum-option", "this is an enum option."); - - EXPECT_NO_THROW(parser.parse()); - - EXPECT_TRUE(option_values == (std::vector{foo::bar::two, foo::bar::one, foo::bar::three})); - } - { std::vector option_values{}; From 7da43d2ead2b13d939c2abd5657dc77eaffb60ac Mon Sep 17 00:00:00 2001 From: Svenja Mehringer Date: Sun, 13 Mar 2022 19:32:47 +0100 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Enrico Seiler --- include/sharg/enumeration_names.hpp | 33 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/include/sharg/enumeration_names.hpp b/include/sharg/enumeration_names.hpp index 52d0db77..e9764e1c 100644 --- a/include/sharg/enumeration_names.hpp +++ b/include/sharg/enumeration_names.hpp @@ -12,9 +12,9 @@ #pragma once +#include #include #include -#include #include @@ -89,7 +89,7 @@ namespace sharg::detail::adl_only template std::unordered_map enumeration_names(t) = delete; -//!\brief sharg::detail::customisation_point_object (CPO) definition for sharg::enumeration_names. +//!\brief Customization Point Object (CPO) definition for sharg::enumeration_names. //!\ingroup argument_parser //!\remark For a complete overview, take a look at \ref argument_parser template @@ -105,7 +105,7 @@ struct enumeration_names_cpo constexpr enumeration_names_cpo & operator=(enumeration_names_cpo const &) = default; //!< Defaulted. //!\} - /*!\brief If `option_t` isn't std::is_nothrow_default_constructible, enumeration_names will be called with + /*!\brief If `option_t` is not std::is_nothrow_default_constructible, enumeration_names will be called with * std::type_identity instead of a default constructed alphabet. */ template @@ -125,13 +125,13 @@ struct enumeration_names_cpo return sharg::custom::argument_parsing::enumeration_names; } - /*!\brief CPO overload (check 1 out of 2): argument dependent lookup (ADL), i.e. + /*!\brief CPO overload (check 2 out of 2): argument dependent lookup (ADL), i.e. * `enumeration_names(option_type{})` * \tparam option_type The type of the option. (Needed to defer instantiation for incomplete types.) * * \details * - * If the option_type isn't std::is_nothrow_default_constructible, + * If the option_type is not std::is_nothrow_default_constructible, * `enumeration_names(std::type_identity{})` will be called. */ template @@ -142,15 +142,16 @@ struct enumeration_names_cpo return enumeration_names(option_or_type_identity{}); } - /*!\brief SFINAE-friendly call-operator to resolve CPO overload resolution. + /*!\brief SFINAE-friendly call-operator to resolve the CPO overload. * - * This operator implements the actual CPO overload resolution. Overload resolution will try each base class of - * sharg::detail::priority_tag<1> and sharg::detail::priority_tag<0>. - * sharg::detail::priority_tag<0> as first argument to `cpo_overload`. That means a high priority in - * sharg::detail::priority_tag will be evaluated first and one can define an order which overload should be - * prioritised if multiple overloads match. + * This operator decides which `cpo_overload` implementation to use. It will start with the highest + * priority, in this case `sharg::detail::priority_tag<1>`. If this is not well-defined, the base class + * of the priority_tag is checked (`sharg::detail::priority_tag<0>`). + * If there are multiple `cpo_overload` overloads, the `priority_tag` decides the order of resolution, + * the highest number is tried first. * - * It perfectly forwards the result and noexcept-property of the `cpo_overload`. + * If any matching overload is found, this operator perfectly forwards the result and noexcept-property of the + * `cpo_overload`. */ template constexpr auto operator()(args_t && ...args) const @@ -172,7 +173,7 @@ namespace sharg /*!\brief Return a conversion map from std::string_view to option_type. * \tparam your_type Type of the value to retrieve the conversion map for. - * \param value The value is not used, just its type. + * \param value The value is not accessed, only its type is used. * \returns A std::unordered_map that maps a string identifier to a value of your_type. * \ingroup argument_parser * \details @@ -188,7 +189,7 @@ namespace sharg * * ### Example * - * If you are working on a type in your own namespace, you should implement a free function like this: + * If you are working on a type in your namespace, you should implement a free function like this: * * \include test/snippet/argument_parser/custom_enumeration.cpp * @@ -201,7 +202,7 @@ namespace sharg * * ### Customisation point * - * This is a customisation point (see \ref about_customisation). To specify the behaviour for your own type, + * This is a customisation point (see \ref about_customisation). To specify the behaviour for your type, * simply provide one of the two functions specified above. */ template @@ -218,7 +219,7 @@ inline auto const enumeration_names = detail::adl_only::enumeration_names_cpo must exist and be of type + * * An instance of sharg::enumeration_names must exist and be of the type * `std::unordered_map`. * * \remark For a complete overview, take a look at \ref argument_parser