SeqAn3 3.4.0-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-2024 Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2024 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 <charconv>
15#include <utility> // __cpp_lib_to_chars may be defined here as currently documented.
16#include <version> // From C++20 onwards, all feature macros should be defined here.
17
32// =========================================================================
33// If float implementation is missing, add our own shim-implementation
34// =========================================================================
35
36#if __cpp_lib_to_chars < 201611
37# include <cassert>
38# include <concepts>
39# include <sstream>
40
41namespace seqan3::contrib::charconv_float
42{
43using ::std::chars_format;
44using ::std::from_chars_result;
45using ::std::to_chars_result;
46
50template <std::floating_point value_type>
51inline to_chars_result to_chars_floating_point(char * first, char * last, value_type value) noexcept
52{
53 assert(first != nullptr);
54 assert(last != nullptr);
55
57 ss << value;
58 auto str = ss.str();
59
60 if (last - first < static_cast<std::ptrdiff_t>(str.size()))
61 return {last, std::errc::value_too_large};
62
63 std::copy(str.begin(), str.end(), first);
64
65 return {first + str.size(), std::errc{}};
66}
67
71template <std::floating_point value_type>
72inline from_chars_result from_chars_floating_point(char const * first,
73 char const * last,
74 value_type & value,
75 chars_format fmt = chars_format::general) noexcept
76{
77 // The locale issue:
78 // std::from_chars is documented to be locale independent. The accepted patterns
79 // are identical to the one used by strtod in the defailt ("C") locale.
80 //
81 // The functions strto[d/f/ld] used here are locale dependent but
82 // setting the locale manually by std::setlocale is not thread safe.
83 // So for the time being this workaround is locale dependent.
84 if (*first == '+') // + is permitted in function strto[d/f/ld] but not in from_chars
85 return {last, std::errc::invalid_argument};
86
87 float tmp{};
88 constexpr ptrdiff_t buffer_size = 100;
89 char buffer[buffer_size];
90
91 if (fmt != chars_format::general)
92 {
93 bool exponent_is_present{false};
94 for (auto it = first; it != last; ++it)
95 {
96 if (*it == 'e' || *it == 'E')
97 {
98 exponent_is_present = true;
99 break;
100 }
101 }
102
103 if (fmt == chars_format::scientific && !exponent_is_present)
104 return {last, std::errc::invalid_argument};
105
106 if (fmt == chars_format::fixed && exponent_is_present)
107 return {last, std::errc::invalid_argument};
108 }
109
110 // In contrast to std::from_chars, std::strto[f/d/ld] does not treat the second
111 // parameter (str_end) as "end of the sequence to parse" but merely as an out
112 // parameter to indicate where the parsing ended. Therefore, if [last] does
113 // not point to the end of a null-terminated string, a buffer is needed to
114 // represent the truncated sequence and ensure correct from_chars functionality.
115 char * start;
116
117 if ((*last != '\0') || fmt == chars_format::hex)
118 {
119 // If hex format is explicitly expected, the 0x prefix is not allowed in the
120 // the original sequence according to the std::from_chars cppreference
121 // documentation.
122 // In order to use strto[f/d/ld], the prefix must be prepended to achieve
123 // correct parsing. This will also automatically lead to an error if the
124 // original sequence did contain a 0x prefix and thus reflect the correct
125 // requirements of std::from_chars.
126 ptrdiff_t offset{0};
127 if (fmt == chars_format::hex)
128 {
129 buffer[0] = '0';
130 buffer[1] = 'x';
131 offset = 2;
132 }
133
134 std::copy(first, last, &buffer[offset]);
135 buffer[std::min<ptrdiff_t>(buffer_size - offset, last - first)] = '\0';
136
137 start = &buffer[0];
138 }
139 else
140 {
141 start = const_cast<char *>(first);
142 }
143
144 char * end;
145
146 if constexpr (std::same_as<std::remove_reference_t<value_type>, float>)
147 {
148 tmp = strtof(start, &end);
149 }
150 if constexpr (std::same_as<std::remove_reference_t<value_type>, double>)
151 {
152 tmp = strtod(start, &end);
153 }
154 if constexpr (std::same_as<std::remove_reference_t<value_type>, long double>)
155 {
156 tmp = strtold(start, &end);
157 }
158
159 last = first + (end - start);
160
161 if (errno == ERANGE)
162 {
163 return {last, std::errc::result_out_of_range};
164 }
165 else if (tmp == 0 && end == start)
166 {
167 return {last, std::errc::invalid_argument};
168 }
169
170 // Success.
171 value = tmp;
172 return {last, {}};
173}
174
175} // namespace seqan3::contrib::charconv_float
176
177namespace seqan3::contrib::charconv_float
178{
179// -----------------------------------------------------------------------------
180// to_chars for floating point types
181// -----------------------------------------------------------------------------
182
186template <std::floating_point floating_point_type>
187inline to_chars_result to_chars(char * first, char * last, floating_point_type value) noexcept
188{
189 return to_chars_floating_point(first, last, value);
190}
191
192// -----------------------------------------------------------------------------
193// from_chars for floating point types
194// -----------------------------------------------------------------------------
195
251template <std::floating_point floating_point_type>
252inline from_chars_result from_chars(char const * first,
253 char const * last,
254 floating_point_type & value,
255 chars_format fmt = chars_format::general) noexcept
256{
257 return from_chars_floating_point(first, last, value, fmt);
258}
259} // namespace seqan3::contrib::charconv_float
260
261namespace std
262{
263// gcc-11 also defines float versions, but they don't clash with ours, because they use explicit overloads for each
264// float type. That means the stdlib has a higher priority in overload resolution then our shim implementation.
265using ::seqan3::contrib::charconv_float::from_chars; // import our shim-float version
266using ::seqan3::contrib::charconv_float::to_chars; // import our shim-float version
267} // namespace std
268
269#endif // __cpp_lib_to_chars < 201611
270
271#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