I have written the following function that aims to find the first occurring string_view in the cli_option
string_view and if finds any, it returns an iterator to the matched string in the array called cli_options
.
Here is an MRE:
#include <array>
#include <string_view>
#include <algorithm>
namespace
{
// Define the array of CLI options
constexpr std::array<std::string_view, 9> cli_options {"--help", "--version", "--init-file=",
"-d", "-f", /*...*/};
}
auto check_cli_option(const std::string_view cli_option) noexcept
{
// happy path
for ( auto it { std::cbegin( cli_options ) }; it < std::cend( cli_options ); ++it )
{
const auto& supported_cli_option { *it };
if ( cli_option.starts_with( supported_cli_option ) )
{
return it;
}
}
// unhappy path
// If no match is found, check which option occurs first in cli_option
auto pos_of_first_occurring_option { std::string_view::npos };
auto iter_to_first_occurring_option { std::cend( cli_options ) };
for ( auto it { std::cbegin( cli_options ) }; it < std::cend( cli_options ); ++it )
{
const auto& supported_cli_option { *it };
if ( const auto pos { cli_option.find( supported_cli_option ) };
pos < pos_of_first_occurring_option )
{
pos_of_first_occurring_option = pos;
iter_to_first_occurring_option = it;
}
}
return iter_to_first_occurring_option;
}
The cli_option
needs to start with one of the items in the array to be considered a valid option. Otherwise, the function starts the second loop to find which one occurs first (if any). So an input like "-d" or "-duncic43" is valid but "--d" or "3$F-d" or "d-d-f" etc are invalid even though -d
is included in all of them. So in short any junk is allowed to follow the option. The junk characters must not precede the option itself though.
I also combined the two for loops but the compiler for some reason generates more code (and probably less efficient?) for that version of the function. I guess I won't use that one.
The question is: Can this be simplified? Especially by using a standard algorithm. I looked at std::find_first_of
but that is not intended for searching for multiple strings in a single string.
By adopting test to provided code and ignoring text description I got nice equivalent code which uses C++20 ranges:
auto check_cli_option(const std::string_view cli_option) noexcept
{
return std::ranges::min_element(cli_options, std::less<>{}, [cli_option] (auto option) {
return cli_option.find(option);
});
if (cli_option.find(*it) != std::string_view::npos) return it;
return std::cend(cli_options);
}
https://godbolt.org/z/EEE4az7nr
Still this logic is strange so I'm not happy with it.