ReUseX  0.0.5
3D Point Cloud Processing for Building Reuse
Loading...
Searching...
No Matches
parse_number.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Povl Filip Sonne-Frederiksen
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6
7// Locale-independent text -> number. std::stod / strtod / std::stof follow
8// the C locale's LC_NUMERIC: a process that calls setlocale(LC_ALL, "") —
9// every QApplication does — reads "799.85" as 799 under da_DK, and stops a
10// strtod loop dead at the '.' (Q2 review: the Qt client's intrinsics). These
11// use std::from_chars, which never consults the locale.
12
13#include <charconv>
14#include <cstddef>
15#include <optional>
16#include <stdexcept>
17#include <string>
18#include <string_view>
19#include <system_error>
20
21namespace reusex::utils {
22
27template <typename T>
28std::optional<T> parse_number(std::string_view s,
29 std::size_t *consumed = nullptr) {
30 std::size_t i = 0;
31 while (i < s.size() && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' ||
32 s[i] == '\r' || s[i] == '\f' || s[i] == '\v'))
33 ++i;
34 if (i < s.size() && s[i] == '+' &&
35 !(i + 1 < s.size() && (s[i + 1] == '-' || s[i + 1] == '+')))
36 ++i;
37 T value{};
38 const auto r = std::from_chars(s.data() + i, s.data() + s.size(), value);
39 if (r.ec != std::errc{}) {
40 if (consumed)
41 *consumed = 0;
42 return std::nullopt;
43 }
44 if (consumed)
45 *consumed = static_cast<std::size_t>(r.ptr - s.data());
46 return value;
47}
48
51inline double to_double(std::string_view s, std::size_t *pos = nullptr) {
52 std::size_t used = 0;
53 const auto v = parse_number<double>(s, &used);
54 if (!v)
55 throw std::invalid_argument("not a number: '" + std::string(s) + "'");
56 if (pos)
57 *pos = used;
58 return *v;
59}
60
62inline float to_float(std::string_view s, std::size_t *pos = nullptr) {
63 std::size_t used = 0;
64 const auto v = parse_number<float>(s, &used);
65 if (!v)
66 throw std::invalid_argument("not a number: '" + std::string(s) + "'");
67 if (pos)
68 *pos = used;
69 return *v;
70}
71
72} // namespace reusex::utils
float to_float(std::string_view s, std::size_t *pos=nullptr)
std::stof's contract without its locale.
std::optional< T > parse_number(std::string_view s, std::size_t *consumed=nullptr)
Parse a number at the start of s, after optional whitespace and a '+' (strtod accepts both; from_char...
double to_double(std::string_view s, std::size_t *pos=nullptr)
std::stod's contract without its locale: throws std::invalid_argument when s does not start with a nu...