Sharg 1.2.3-rc.1
The argument parser for bio-c++ tools.
Loading...
Searching...
No Matches
charconv
Go to the documentation of this file.
1// -*- C++ -*-
2// SPDX-FileCopyrightText: 2006-2026, Knut Reinert & Freie Universität Berlin
3// SPDX-FileCopyrightText: 2016-2026, Knut Reinert & MPI für molekulare Genetik
4// SPDX-License-Identifier: BSD-3-Clause
5
11#ifndef SEQAN_STD_CHARCONV_SHIM // to avoid multiple definitions if other seqan modules also implement this
12#define SEQAN_STD_CHARCONV_SHIM
13
14#include <version> // From C++20 onwards, all feature macros should be defined here.
15
16// Workaround for llvm marking float charconv as unavailable
17#if defined(__APPLE__) && defined(_LIBCPP_VERSION)
18# ifdef _LIBCPP___CHARCONV_FROM_CHARS_FLOATING_POINT_H
19# warning This file needs to be included before any <charconv> include.
20# endif
21# if __has_include(<__configuration/availability.h>)
22# include <__configuration/availability.h>
23# undef _LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT
24# define _LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT /* nothing */
25# undef _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT
26# define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT /* nothing */
27# endif
28#endif
29
30#include <charconv>
31#include <utility> // __cpp_lib_to_chars may be defined here as currently documented.
32
48// =========================================================================
49// If float implementation is missing, add our own shim-implementation
50// =========================================================================
51
52#if __cpp_lib_to_chars < 201611
53# include <algorithm>
54# include <cassert>
55# include <cctype>
56# include <cerrno>
57# include <cstdlib>
58# include <sstream>
59# include <string>
60# include <type_traits>
61
62namespace sharg::contrib::charconv_float
63{
64using ::std::chars_format;
65using ::std::from_chars_result;
66using ::std::to_chars_result;
67
71template <typename value_type>
73inline to_chars_result to_chars_floating_point(char * first, char * last, value_type value) noexcept
74{
75 assert(first != nullptr);
76 assert(last != nullptr);
77
79 ss << value;
80 auto str = ss.str();
81
82 if (last - first < static_cast<std::ptrdiff_t>(str.size()))
83 return {last, std::errc::value_too_large};
84
85 std::copy(str.begin(), str.end(), first);
86
87 return {first + str.size(), std::errc{}};
88}
89
93template <typename value_type>
95inline from_chars_result from_chars_floating_point(char const * first,
96 char const * last,
97 value_type & value,
98 chars_format fmt = chars_format::general) noexcept
99{
100 // The locale issue:
101 // std::from_chars is documented to be locale independent. The accepted patterns
102 // are identical to the one used by strtod in the defailt ("C") locale.
103 //
104 // The functions strto[d/f/ld] used here are locale dependent but
105 // setting the locale manually by std::setlocale is not thread safe.
106 // So for the time being this workaround is locale dependent.
107 // strto[d/f/ld] skip leading whitespace and accept a leading +, from_chars does neither.
108 if (first >= last || *first == '+' || std::isspace(static_cast<unsigned char>(*first)))
109 return {first, std::errc::invalid_argument};
110
111 auto is_exponent = [](char const c)
112 {
113 return c == 'e' || c == 'E';
114 };
115
116 bool const is_negative = *first == '-';
117 char const * const digits = first + is_negative;
118 bool const is_inf_or_nan = digits != last && (*digits == 'i' || *digits == 'I' || *digits == 'n' || *digits == 'N');
119
120 // strto[d/f/ld] parse hexadecimal floats if the sequence has a 0x prefix, from_chars never accepts the prefix:
121 // "0x123" is parsed as the value "0" with unparsed remainder "x123". Hence, only the "0" is handed to strto[d/f/ld].
122 bool const has_hex_prefix = last - digits >= 2 && digits[0] == '0' && (digits[1] == 'x' || digits[1] == 'X');
123 char const * parse_last = has_hex_prefix ? digits + 1 : last;
124
125 // If fmt is fixed, the exponent is not part of the pattern and parsing stops before it.
126 if (fmt == chars_format::fixed && !is_inf_or_nan)
127 parse_last = std::find_if(first, parse_last, is_exponent);
128
129 // If fmt is hex, strto[d/f/ld] need the 0x prefix (after the sign) to parse the digits as hexadecimal.
130 // The prefix is subtracted from the number of parsed characters afterwards.
131 ptrdiff_t const prefix_size = (fmt == chars_format::hex && !is_inf_or_nan) ? 2 : 0;
132
133 // In contrast to std::from_chars, std::strto[f/d/ld] does not treat the second
134 // parameter (str_end) as "end of the sequence to parse" but merely as an out
135 // parameter to indicate where the parsing ended. Therefore, a null-terminated
136 // copy of the sequence is needed to ensure correct from_chars functionality.
137 // Sequences that do not fit into the stack buffer are copied to the heap.
138 constexpr ptrdiff_t stack_buffer_size = 100;
139 char stack_buffer[stack_buffer_size];
140 std::string heap_buffer;
141 ptrdiff_t const buffer_size = (parse_last - first) + prefix_size + 1;
142 char * const buffer = [&]()
143 {
144 if (buffer_size <= stack_buffer_size)
145 return &stack_buffer[0];
146
147 heap_buffer.resize(buffer_size);
148 return heap_buffer.data();
149 }();
150
151 char * buffer_end = std::copy(first, digits, buffer);
152 if (prefix_size)
153 {
154 *buffer_end++ = '0';
155 *buffer_end++ = 'x';
156 }
157 buffer_end = std::copy(digits, parse_last, buffer_end);
158 *buffer_end = '\0';
159
160 value_type tmp{};
161 char * end;
162 int const previous_errno = errno;
163 errno = 0;
164
166 {
167 tmp = strtof(buffer, &end);
168 }
170 {
171 tmp = strtod(buffer, &end);
172 }
173 if constexpr (std::is_same_v<std::remove_reference_t<value_type>, long double>)
174 {
175 tmp = strtold(buffer, &end);
176 }
177
178 bool const is_out_of_range = errno == ERANGE;
179 errno = previous_errno;
180
181 // If only the prepended prefix (or rather its "0") has been parsed, nothing of the original sequence matched.
182 char const * const ptr = first + std::max<ptrdiff_t>(0, (end - buffer) - prefix_size);
183
184 if (ptr == first)
185 return {first, std::errc::invalid_argument};
186
187 // If fmt is scientific, the exponent is required.
188 if (fmt == chars_format::scientific && !is_inf_or_nan && std::find_if(first, ptr, is_exponent) == ptr)
189 return {first, std::errc::invalid_argument};
190
191 if (is_out_of_range)
192 return {ptr, std::errc::result_out_of_range};
193
194 // Success.
195 value = tmp;
196 return {ptr, {}};
197}
198
199} // namespace sharg::contrib::charconv_float
200
201namespace sharg::contrib::charconv_float
202{
203// -----------------------------------------------------------------------------
204// to_chars for floating point types
205// -----------------------------------------------------------------------------
206
210template <typename floating_point_type>
212inline to_chars_result to_chars(char * first, char * last, floating_point_type value) noexcept
213{
214 return to_chars_floating_point(first, last, value);
215}
216
217// -----------------------------------------------------------------------------
218// from_chars for floating point types
219// -----------------------------------------------------------------------------
220
273template <typename floating_point_type>
275inline from_chars_result from_chars(char const * first,
276 char const * last,
277 floating_point_type & value,
278 chars_format fmt = chars_format::general) noexcept
279{
280 return from_chars_floating_point(first, last, value, fmt);
281}
282} // namespace sharg::contrib::charconv_float
283
284namespace std
285{
286// gcc-11 also defines float versions, but they don't clash with ours, because they use explicit overloads for each
287// float type. That means the stdlib has a higher priority in overload resolution then our shim implementation.
288using ::sharg::contrib::charconv_float::from_chars; // import our shim-float version
289using ::sharg::contrib::charconv_float::to_chars; // import our shim-float version
290} // namespace std
291
292#endif // __cpp_lib_to_chars < 201611
293
294#endif // SEQAN_STD_CHARCONV_SHIM
The <charconv> header from C++17's standard library.
T copy(T... args)
T data(T... args)
T find_if(T... args)
to_chars_result to_chars(char *first, char *last, floating_point_type value) noexcept
std::to_chars overload for floating point via a std::stringstream for default base = 10.
Definition charconv:212
from_chars_result from_chars_floating_point(char const *first, char const *last, value_type &value, chars_format fmt=chars_format::general) noexcept
Delegates to functions strto[d/f/ld] for floating point value extraction.
Definition charconv:95
to_chars_result to_chars_floating_point(char *first, char *last, value_type value) noexcept
std::to_chars implementation for floating point via a std::stringstream for default base = 10.
Definition charconv:73
from_chars_result from_chars(char const *first, char const *last, floating_point_type &value, chars_format fmt=chars_format::general) noexcept
Parse a char sequence into an floating point value.
Definition charconv:275
T is_same_v
T resize(T... args)
T str(T... args)
Hide me