I have written the following function:
#include <iostream>
#include <utility>
#include <string>
#include <string_view>
#include <limits>
#include <optional>
#include <stdexcept>
// header file
std::optional<int> isInteger( const std::string_view token, const std::pair<int, int> acceptableRange =
std::pair<int, int>( std::numeric_limits<int>::min( ), std::numeric_limits<int>::max( ) ) ) noexcept;
// source file
std::optional<int> isInteger( const std::string_view token, const std::pair<int, int> acceptableRange ) noexcept
{
if ( token.empty( ) )
{
return { }; // not convertible
}
try
{
std::size_t pos { 0 };
const int result_integer { std::stoi( std::string{ token }, &pos, 10 ) };
const auto& [ minAcceptableValue, maxAcceptableValue ] { acceptableRange };
if ( pos == token.length( ) && ( result_integer <= maxAcceptableValue && result_integer >= minAcceptableValue ) )
{
return result_integer; // convertible, this is the happy ending
}
else
{
return { }; // not convertible
}
}
catch ( const std::invalid_argument& ia ) { return { }; } // not convertible
catch ( const std::out_of_range& oor ) { return { }; } // not convertible
catch ( const std::length_error& le ) { return { }; } // not convertible
catch ( const std::bad_alloc& ba ) { return { }; } // not convertible
}
// call site
int main( )
{
const std::string str { "12" };
const std::pair<int, int> acceptableRange { std::make_pair<int, int>( 0, 50 ) };
const std::optional<int> tempInteger { isInteger( str, acceptableRange ) };
if ( tempInteger ) { std::cout << "Converted value: " << tempInteger.value( ) << '\n'; }
else { std::cout << "Conversion failed" << '\n'; }
}
The above function converts any string_view
to an int
if possible, otherwise returns an empty std::optional
. I thought that returning an empty optional
acts as an error code and informs the caller of the failure. So there isn't any need to let it throw and then handle the exceptions in the call site. I think that stack unwinding is unnecessary in this case.
I have a few questions:
- Is this function good in terms of performance? Or should I use an alternative from another library? If so, then what can be the alternative?
- Does it cover all the edge cases? I have tested it with many different (valid and invalid) inputs but it never failed.
- Is it good that it's handling the possible exceptions inside its scope?
- I declared it
noexcept
and this decreased the size of the executable (just a little). Do you recommend doing this for this function since no exception will escape its body? - Can anything about this function be further improved?
Also I use GCC v11.2 (just in case you need to know).