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 <cassert>
53# include <concepts>
54# include <sstream>
55
56namespace seqan3::contrib::charconv_float
57{
58using ::std::chars_format;
59using ::std::from_chars_result;
60using ::std::to_chars_result;
61
65template <std::floating_point value_type>
66inline to_chars_result to_chars_floating_point(char * first, char * last, value_type value) noexcept
67{
68 assert(first != nullptr);
69 assert(last != nullptr);
70
72 ss << value;
73 auto str = ss.str();
74
75 if (last - first < static_cast<std::ptrdiff_t>(str.size()))
76 return {last, std::errc::value_too_large};
77
78 std::copy(str.begin(), str.end(), first);
79
80 return {first + str.size(), std::errc{}};
81}
82
86template <std::floating_point value_type>
87inline from_chars_result from_chars_floating_point(char const * first,
88 char const * last,
89 value_type & value,
90 chars_format fmt = chars_format::general) noexcept
91{
92 // The locale issue:
93 // std::from_chars is documented to be locale independent. The accepted patterns
94 // are identical to the one used by strtod in the defailt ("C") locale.
95 //
96 // The functions strto[d/f/ld] used here are locale dependent but
97 // setting the locale manually by std::setlocale is not thread safe.
98 // So for the time being this workaround is locale dependent.
99 if (*first == '+') // + is permitted in function strto[d/f/ld] but not in from_chars
100 return {last, std::errc::invalid_argument};
101
102 float tmp{};
103 constexpr ptrdiff_t buffer_size = 100;
104 char buffer[buffer_size];
105
106 if (fmt != chars_format::general)
107 {
108 bool exponent_is_present{false};
109 for (auto it = first; it != last; ++it)
110 {
111 if (*it == 'e' || *it == 'E')
112 {
113 exponent_is_present = true;
114 break;
115 }
116 }
117
118 if (fmt == chars_format::scientific && !exponent_is_present)
119 return {last, std::errc::invalid_argument};
120
121 if (fmt == chars_format::fixed && exponent_is_present)
122 return {last, std::errc::invalid_argument};
123 }
124
125 // In contrast to std::from_chars, std::strto[f/d/ld] does not treat the second
126 // parameter (str_end) as "end of the sequence to parse" but merely as an out
127 // parameter to indicate where the parsing ended. Therefore, if [last] does
128 // not point to the end of a null-terminated string, a buffer is needed to
129 // represent the truncated sequence and ensure correct from_chars functionality.
130 char * start;
131
132 if ((*last != '\0') || fmt == chars_format::hex)
133 {
134 // If hex format is explicitly expected, the 0x prefix is not allowed in the
135 // the original sequence according to the std::from_chars cppreference
136 // documentation.
137 // In order to use strto[f/d/ld], the prefix must be prepended to achieve
138 // correct parsing. This will also automatically lead to an error if the
139 // original sequence did contain a 0x prefix and thus reflect the correct
140 // requirements of std::from_chars.
141 ptrdiff_t offset{0};
142 if (fmt == chars_format::hex)
143 {
144 buffer[0] = '0';
145 buffer[1] = 'x';
146 offset = 2;
147 }
148
149 std::copy(first, last, &buffer[offset]);
150 buffer[std::min<ptrdiff_t>(buffer_size - offset, last - first)] = '\0';
151
152 start = &buffer[0];
153 }
154 else
155 {
156 start = const_cast<char *>(first);
157 }
158
159 char * end;
160
161 if constexpr (std::same_as<std::remove_reference_t<value_type>, float>)
162 {
163 tmp = strtof(start, &end);
164 }
165 if constexpr (std::same_as<std::remove_reference_t<value_type>, double>)
166 {
167 tmp = strtod(start, &end);
168 }
169 if constexpr (std::same_as<std::remove_reference_t<value_type>, long double>)
170 {
171 tmp = strtold(start, &end);
172 }
173
174 last = first + (end - start);
175
176 if (errno == ERANGE)
177 {
178 return {last, std::errc::result_out_of_range};
179 }
180 else if (tmp == 0 && end == start)
181 {
182 return {last, std::errc::invalid_argument};
183 }
184
185 // Success.
186 value = tmp;
187 return {last, {}};
188}
189
190} // namespace seqan3::contrib::charconv_float
191
192namespace seqan3::contrib::charconv_float
193{
194// -----------------------------------------------------------------------------
195// to_chars for floating point types
196// -----------------------------------------------------------------------------
197
201template <std::floating_point floating_point_type>
202inline to_chars_result to_chars(char * first, char * last, floating_point_type value) noexcept
203{
204 return to_chars_floating_point(first, last, value);
205}
206
207// -----------------------------------------------------------------------------
208// from_chars for floating point types
209// -----------------------------------------------------------------------------
210
266template <std::floating_point floating_point_type>
267inline from_chars_result from_chars(char const * first,
268 char const * last,
269 floating_point_type & value,
270 chars_format fmt = chars_format::general) noexcept
271{
272 return from_chars_floating_point(first, last, value, fmt);
273}
274} // namespace seqan3::contrib::charconv_float
275
276namespace std
277{
278// gcc-11 also defines float versions, but they don't clash with ours, because they use explicit overloads for each
279// float type. That means the stdlib has a higher priority in overload resolution then our shim implementation.
280using ::seqan3::contrib::charconv_float::from_chars; // import our shim-float version
281using ::seqan3::contrib::charconv_float::to_chars; // import our shim-float version
282} // namespace std
283
284#endif // __cpp_lib_to_chars < 201611
285
286#endif // SEQAN_STD_CHARCONV_SHIM
T copy(T... args)
T end(T... args)
T from_chars(T... args)
@ offset
Sequence (seqan3::field::seq) relative start position (0-based), unsigned value.
SeqAn specific customisations in the standard namespace.
T str(T... args)
T strtof(T... args)
T to_chars(T... args)
Hide me