@@ -500,11 +500,13 @@ class parser
500500 if (!parse_was_called)
501501 throw design_error{" You can only ask which options have been set after calling the function `parse()`." };
502502
503+ constexpr bool id_is_long = !std::same_as<id_type, char >;
504+
503505 // the detail::format_parse::find_option_id call in the end expects either a char or std::string
504- using char_or_string_t = std::conditional_t <std::same_as<id_type, char >, char , std::string>;
505- char_or_string_t short_or_long_id = {id}; // e.g. convert char * to string here if necessary
506+ using char_or_string_t = std::conditional_t <id_is_long, std::string, char >;
507+ char_or_string_t const short_or_long_id {id}; // e.g. convert char * to string here if necessary
506508
507- if constexpr (!std::same_as<id_type, char > ) // long id was given
509+ if constexpr (id_is_long ) // long id was given
508510 {
509511 if (short_or_long_id.size () == 1 )
510512 {
@@ -514,12 +516,17 @@ class parser
514516 }
515517 }
516518
517- if (std::find (used_option_ids.begin (), used_option_ids.end (), std::string{id}) == used_option_ids.end ())
518- throw design_error{" You can only ask for option identifiers that you added with add_option() before." };
519+ id_pair const & ids = [this , &short_or_long_id]()
520+ {
521+ auto it = find_used_id (short_or_long_id);
522+ if (!it)
523+ throw design_error{" You can only ask for option identifiers that you added with add_option() before." };
524+ return *it;
525+ }();
519526
520527 // we only need to search for an option before the `end_of_options_indentifier` (`--`)
521528 auto end_of_options = std::find (cmd_arguments.begin (), cmd_arguments.end (), end_of_options_indentifier);
522- auto option_it = detail::format_parse::find_option_id (cmd_arguments.begin (), end_of_options, short_or_long_id );
529+ auto option_it = detail::format_parse::find_option_id (cmd_arguments.begin (), end_of_options, ids );
523530 return option_it != end_of_options;
524531 }
525532
@@ -715,7 +722,11 @@ class parser
715722 format{detail::format_help{{}, {}, false }}; // Will be overwritten in any case.
716723
717724 // !\brief List of option/flag identifiers that are already used.
718- std::set<std::string> used_option_ids{" h" , " hh" , " help" , " advanced-help" , " export-help" , " version" , " copyright" };
725+ std::set<id_pair> used_option_ids{{" h" , " help" },
726+ {" hh" , " advanced-help" },
727+ {" " , " export-help" },
728+ {" " , " version" },
729+ {" " , " copyright" }};
719730
720731 // !\brief The command line arguments.
721732 std::vector<std::string> cmd_arguments{};
@@ -884,11 +895,34 @@ class parser
884895 * otherwise.
885896 */
886897 template <typename id_type>
887- bool id_exists (id_type const & id)
898+ std::optional<id_pair> find_used_id (id_type const & id) const
888899 {
889900 if (detail::format_parse::is_empty_id (id))
890- return false ;
891- return (!(used_option_ids.insert (std::string ({id}))).second );
901+ return std::nullopt ;
902+
903+ if constexpr (std::same_as<id_type, char >)
904+ {
905+ std::string const id_str{std::string (1 , id)};
906+ auto it = std::ranges::find_if (used_option_ids,
907+ [&id_str](id_pair const & element)
908+ {
909+ return element.short_id == id_str;
910+ });
911+ if (it != used_option_ids.end ())
912+ return *it;
913+ }
914+ else
915+ {
916+ auto it = std::ranges::find_if (used_option_ids,
917+ [&id](id_pair const & element)
918+ {
919+ return element.long_id == id;
920+ });
921+ if (it != used_option_ids.end ())
922+ return *it;
923+ }
924+
925+ return std::nullopt ;
892926 }
893927
894928 /* !\brief Verifies that the short and the long identifiers are correctly formatted.
@@ -908,27 +942,33 @@ class parser
908942 return valid_chars.find (c) != std::string::npos;
909943 };
910944
911- if (id_exists (short_id))
912- throw design_error (" Option Identifier '" + std::string (1 , short_id) + " ' was already used before." );
913- if (id_exists (long_id))
914- throw design_error (" Option Identifier '" + long_id + " ' was already used before." );
945+ bool const short_id_empty = detail::format_parse::is_empty_id (short_id);
946+ bool const long_id_empty = detail::format_parse::is_empty_id (long_id);
947+ bool const long_id_valid = std::ranges::all_of (long_id,
948+ [&is_valid](char c)
949+ {
950+ return ((c == ' -' ) || is_valid (c));
951+ });
952+
953+ // Validity
954+ if (short_id_empty && long_id_empty)
955+ throw design_error (" Option Identifiers cannot both be empty." );
956+ if (!short_id_empty && !is_valid (short_id))
957+ throw design_error (" Option identifiers may only contain alphanumeric characters, '_', or '@'." );
915958 if (long_id.length () == 1 )
916959 throw design_error (" Long IDs must be either empty, or longer than one character." );
917- if ((short_id != ' \0 ' ) && !is_valid (short_id))
918- throw design_error (" Option identifiers may only contain alphanumeric characters, '_', or '@'." );
919- if (long_id.size () > 0 && (long_id[0 ] == ' -' ))
960+ if (!long_id_empty && (long_id[0 ] == ' -' ))
920961 throw design_error (" First character of long ID cannot be '-'." );
962+ if (!long_id_empty && !long_id_valid)
963+ throw design_error (" Long identifiers may only contain alphanumeric characters, '_', '-', or '@'." );
921964
922- std::for_each (long_id.begin (),
923- long_id.end (),
924- [&is_valid](char c)
925- {
926- if (!((c == ' -' ) || is_valid (c)))
927- throw design_error (
928- " Long identifiers may only contain alphanumeric characters, '_', '-', or '@'." );
929- });
930- if (detail::format_parse::is_empty_id (short_id) && detail::format_parse::is_empty_id (long_id))
931- throw design_error (" Option Identifiers cannot both be empty." );
965+ // Availability
966+ if (find_used_id (short_id))
967+ throw design_error (" Option Identifier '" + std::string (1 , short_id) + " ' was already used before." );
968+ if (find_used_id (long_id))
969+ throw design_error (" Option Identifier '" + long_id + " ' was already used before." );
970+
971+ used_option_ids.insert ({std::string (1 , short_id), long_id});
932972 }
933973
934974 // !brief Verify the configuration given to a sharg::parser::add_option call.
0 commit comments