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