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