Search code examples
c++algorithmc++23string-view

How to find the first occurring string_view in another string_view?


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.


Solution

  • 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.