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