| 26 | |
| 27 | template <typename ConvResultT, typename ResultT = ConvResultT> |
| 28 | inline cxx20::expected<ResultT, Error> |
| 29 | stringToInteger(ConvResultT (&Conv)(const char *, char **, int), |
| 30 | std::string Value) noexcept { |
| 31 | using namespace std::literals; |
| 32 | char *EndPtr; |
| 33 | const char *CStr = Value.c_str(); |
| 34 | auto SavedErrNo = std::exchange(errno, 0); |
| 35 | const auto Result = Conv(CStr, &EndPtr, 10); |
| 36 | std::swap(SavedErrNo, errno); |
| 37 | if (Value.empty() || *EndPtr != '\0') { |
| 38 | return cxx20::unexpected<Error>( |
| 39 | std::in_place, ErrCode::InvalidArgument, |
| 40 | fmt::format("invalid integer value: {}", Value)); |
| 41 | } |
| 42 | auto InsideRange = [](auto WiderResult) constexpr noexcept { |
| 43 | using WiderResultT = decltype(WiderResult); |
| 44 | if constexpr (std::is_same_v<ResultT, WiderResultT>) { |
| 45 | return true; |
| 46 | } else { |
| 47 | return static_cast<WiderResultT>(std::numeric_limits<ResultT>::min()) <= |
| 48 | WiderResult && |
| 49 | WiderResult <= |
| 50 | static_cast<WiderResultT>(std::numeric_limits<ResultT>::max()); |
| 51 | } |
| 52 | }; |
| 53 | if (SavedErrNo == ERANGE || !InsideRange(Result)) { |
| 54 | return cxx20::unexpected<Error>( |
| 55 | std::in_place, ErrCode::OutOfRange, |
| 56 | fmt::format("integer value out of range: {}", Value)); |
| 57 | } |
| 58 | return static_cast<ResultT>(Result); |
| 59 | } |
| 60 | |
| 61 | template <typename ConvResultT, typename ResultT = ConvResultT> |
| 62 | inline cxx20::expected<ResultT, Error> |