diff options
| author | Markus Mittendrein <maxmitti@maxmitti.tk> | 2022-01-09 17:43:10 +0100 |
|---|---|---|
| committer | Markus Mittendrein <maxmitti@maxmitti.tk> | 2022-01-09 17:43:10 +0100 |
| commit | f1ff922eafc6972dd6437d6e9908146fa92577fa (patch) | |
| tree | 1a947b24944bc94f3fc88c13e713ff77ba366bec | |
| parent | 3a79b82bac837e54da6f9e564a3272aa473388c4 (diff) | |
| download | cxxformat-f1ff922eafc6972dd6437d6e9908146fa92577fa.tar.gz cxxformat-f1ff922eafc6972dd6437d6e9908146fa92577fa.zip | |
Improve exception message for out of range numbers in format strings
| -rw-r--r-- | include/cxxformat/core.hpp | 32 | ||||
| -rw-r--r-- | include/cxxformat/runtime.hpp | 2 |
2 files changed, 22 insertions, 12 deletions
diff --git a/include/cxxformat/core.hpp b/include/cxxformat/core.hpp index 6eec0ef..2a78b26 100644 --- a/include/cxxformat/core.hpp +++ b/include/cxxformat/core.hpp @@ -224,6 +224,16 @@ namespace format { return {val, p}; } + template<std::unsigned_integral T = unsigned int> + constexpr std::pair<std::optional<T>, std::size_t> parseNumberWithMessage(std::string_view s, std::size_t pos, std::string_view numberName) + { + try { + return parseNumber<T>(s, pos); + } catch(const std::out_of_range&) { + throw std::out_of_range{std::string{numberName} + " is too big; maximum is " + std::to_string(std::numeric_limits<T>::max())}; + } + } + namespace exceptions { struct incomplete_specifier : std::invalid_argument { using std::invalid_argument::invalid_argument; @@ -263,7 +273,7 @@ namespace format { }; bool hasExplicitArgIndex = false; - const auto argIndex = parseNumber<ArgIndex>(fmt, pos); + const auto argIndex = parseNumberWithMessage<ArgIndex>(fmt, pos, "Argument index or field width"); if (argIndex.first) { checkEos(argIndex.second); @@ -315,8 +325,8 @@ namespace format { checkEos(); ArgIndex maxArg = 0; - const auto argIndexOrNextArg = [&pos, &nextArg, &maxArg, checkEos, fmt](decltype(*format_specifier::minWidth)& argIndex) { - const auto index = parseNumber<ArgIndex>(fmt, pos); + const auto argIndexOrNextArg = [&pos, &nextArg, &maxArg, checkEos, fmt](decltype(*format_specifier::minWidth)& argIndex, std::string_view numberName) { + const auto index = parseNumberWithMessage<ArgIndex>(fmt, pos, numberName); checkEos(index.second); if (index.first && fmt[index.second] == '$') { @@ -336,22 +346,22 @@ namespace format { maxArg = std::max(maxArg, argIndex); }; - const auto handleWidthPrecision = [&pos, argIndexOrNextArg, fmt, checkEos](decltype(format_specifier::minWidth)& indexOrValue, bool allowEmpty = false){ + const auto handleWidthPrecision = [&pos, argIndexOrNextArg, fmt, checkEos](decltype(format_specifier::minWidth)& indexOrValue, std::string_view numberName, bool allowEmpty = false){ bool asArg = false; if (fmt[pos] == '*') { asArg = true; ++pos; checkEos(); - argIndexOrNextArg(indexOrValue.emplace()); + argIndexOrNextArg(indexOrValue.emplace(), numberName); } else { - const auto width = parseNumber<ArgIndex>(fmt, pos); - if (width.first) + const auto value = parseNumberWithMessage<ArgIndex>(fmt, pos, numberName); + if (value.first) { - indexOrValue = *width.first; - pos = width.second; + indexOrValue = *value.first; + pos = value.second; } else if (allowEmpty) { @@ -360,14 +370,14 @@ namespace format { } return asArg; }; - spec.flags.widthAsArg = handleWidthPrecision(spec.minWidth); + spec.flags.widthAsArg = handleWidthPrecision(spec.minWidth, "Field width"); if (fmt[pos] == '.') { ++pos; checkEos(); - spec.flags.precisionAsArg = handleWidthPrecision(spec.precision, true); + spec.flags.precisionAsArg = handleWidthPrecision(spec.precision, "Precision", true); } { diff --git a/include/cxxformat/runtime.hpp b/include/cxxformat/runtime.hpp index 85c6ecf..7659468 100644 --- a/include/cxxformat/runtime.hpp +++ b/include/cxxformat/runtime.hpp @@ -184,7 +184,7 @@ namespace format { throw exceptions::incomplete_specifier{"Incomplete format specifier “%s” at end of format string"_format(fmt.substr(nextSpec))}; } catch(const std::exception& e) { constexpr std::size_t showPartSize = 10; - throw std::invalid_argument{"%s when parsing format specifier “%s%s” at column %u"_format(e.what(), fmt.substr(nextSpec, showPartSize), nextSpec + showPartSize > fmt.size() ? "" : "…", nextSpec)}; + throw std::invalid_argument{"%s; when parsing format specifier “%s%s” at column %u"_format(e.what(), fmt.substr(nextSpec, showPartSize), nextSpec + showPartSize > fmt.size() ? "" : "…", nextSpec)}; } } } |
