charconv.hh 7.13 KB
Newer Older
Praetorius, Simon's avatar
Praetorius, Simon committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
#ifndef DUNE_COMMON_STD_CHARCONV_HH
#define DUNE_COMMON_STD_CHARCONV_HH

/** \file
  * \brief Implements a backport of c++17's to_chars and from_chars facilities for a locale-independent
  * string to/from number conversion, respectively.
  **/

#include <utility>

#ifdef __has_include
  #if __has_include(<charconv>) && defined(__cpp_lib_to_chars) && !defined(DUNE_NO_STD_CHARCONV)
    #define DUNE_HAVE_CHARCONV 1
  #endif
#endif

#ifdef DUNE_HAVE_CHARCONV
  #include <charconv>
#else
  #include <algorithm>
  #include <iomanip>
  #include <ios>
  #include <iterator>
  #include <limits>
  #include <locale>
  #include <sstream>
  #include <system_error>
  #include <type_traits>
  #include <utility>
#endif

namespace Dune
{
  namespace Std
  {

#ifdef DUNE_HAVE_CHARCONV

    using chars_format = std::chars_format;

    using from_chars_result = std::from_chars_result;
    using std::from_chars;

    using to_chars_result = std::to_chars_result;
    using std::to_chars;

#else // DUNE_HAVE_CHARCONV

    // A BitmaskType used to specify floating-point formatting for to_chars and from_chars
    enum class chars_format : int
    {
      scientific = 1,
      fixed = 2,
      hex = 4,
      general = fixed | scientific
    };

    struct from_chars_result
    {
      const char* ptr;
      std::errc ec;
    };

    struct to_chars_result
    {
      char* ptr;
      std::errc ec;
    };

    namespace Impl
    {
      template<typename T, typename StreamManipulator>
      from_chars_result fromCharsImpl (const char* first, const char* last, T& value,
                                       StreamManipulator manipulator)
      {
        std::istringstream s(std::string(first,last));
        s.imbue(std::locale::classic()); // make sure we are in locale "C"
80
81
82
        auto manipulator_error = manipulator(s);
        if (manipulator_error)
          return from_chars_result{first, std::errc{manipulator_error.value()}};
Praetorius, Simon's avatar
Praetorius, Simon committed
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

        try {
          s >> value; // extract value
        } catch (...) {
          s.setstate(std::ios_base::failbit);
        }

        if (s.fail()) {
          if (value == std::numeric_limits<T>::max() || value == std::numeric_limits<T>::min())
            // range error
            return from_chars_result{first + s.tellg(), std::errc::result_out_of_range};
          else
            // extraction fails
            return from_chars_result{first, std::errc::invalid_argument};
        }

        return from_chars_result{first + s.tellg(), std::errc{}};
      }

      template<typename T, typename StreamManipulator>
      to_chars_result toCharsImpl (char* first, char* last, T value,
                                   StreamManipulator manipulator)
      {
        std::ostringstream s;
        s.imbue(std::locale::classic()); // make sure we are in locale "C"
108
109
110
        auto manipulator_error = manipulator(s);
        if (manipulator_error)
          return to_chars_result{last, std::errc{manipulator_error.value()}};
Praetorius, Simon's avatar
Praetorius, Simon committed
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

        // negative integer values in base != 10 are not correctly converted
        bool sign = value < T(0);
        try {
          s << (sign ? -value : value);
        } catch (...) {
          s.setstate(std::ios_base::failbit);
        }

        if (s.fail())
          return to_chars_result{last, std::errc::value_too_large};

        auto str = (sign ? "-" : "") + s.str();
        auto num = std::min(std::distance(first, last),
                            std::distance(str.begin(), str.end()));
        std::copy_n(str.begin(), num, first);

        return to_chars_result{first + num, std::errc{}};
      }

    } // end namespace Impl


    /// \brief Converts a character sequence to an integer value
    template<typename T,
      std::enable_if_t<std::is_integral<T>::value, int> = 0>
    from_chars_result from_chars (const char* first, const char* last, T& value, int base = 10)
    {
139
140
141
142
143
      return Impl::fromCharsImpl(first, last, value, [base](auto& s) -> std::error_code
      {
        s >> std::setbase(base);
        return {};
      });
Praetorius, Simon's avatar
Praetorius, Simon committed
144
145
146
147
148
149
150
151
    }

    /// \brief Converts a character sequence to a floating-point value
    template<typename T,
      std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
    from_chars_result from_chars (const char* first, const char* last, T& value,
                                  chars_format fmt = chars_format::general)
    {
152
      return Impl::fromCharsImpl(first, last, value, [fmt](auto& s) -> std::error_code
Praetorius, Simon's avatar
Praetorius, Simon committed
153
154
155
156
157
158
159
      {
        if ((int(fmt) & int(chars_format::scientific)) != 0)
          s >> std::scientific;
        if ((int(fmt) & int(chars_format::fixed)) != 0)
          s >> std::fixed;
        if ((int(fmt) & int(chars_format::hex)) != 0) {
          s >> std::hexfloat;
160
161
          return std::make_error_code(std::errc::not_supported);
          // Reading hexfloat values not supported by many standard libraries currently
Praetorius, Simon's avatar
Praetorius, Simon committed
162
        }
163
        return {};
Praetorius, Simon's avatar
Praetorius, Simon committed
164
165
166
167
168
169
170
171
172
      });
    }


    /// \brief Converts an integer value to a character sequence
    template <class T,
      std::enable_if_t<std::is_integral<T>::value, int> = 0>
    to_chars_result to_chars (char* first, char* last, T value, int base = 10)
    {
173
174
175
176
177
      return Impl::toCharsImpl(first, last, value, [base](auto& s) -> std::error_code
      {
        s << std::setbase(base);
        return {};
      });
Praetorius, Simon's avatar
Praetorius, Simon committed
178
179
180
181
182
183
184
    }

    /// \brief Converts a floating-point value to a character sequence
    template <class T,
      std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
    to_chars_result to_chars (char* first, char* last, T value)
    {
185
      return Impl::toCharsImpl(first, last, value, [](auto& /*s*/) -> std::error_code { return {}; });
Praetorius, Simon's avatar
Praetorius, Simon committed
186
187
188
189
190
191
192
    }

    /// \brief Converts a floating-point value to a character sequence with additional format specifier
    template <class T,
      std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
    to_chars_result to_chars (char* first, char* last, T value, chars_format fmt)
    {
193
      return Impl::toCharsImpl(first, last, value, [fmt](auto& s) -> std::error_code
Praetorius, Simon's avatar
Praetorius, Simon committed
194
195
196
197
198
199
200
      {
        if ((int(fmt) & int(chars_format::scientific)) != 0)
          s << std::scientific;
        if ((int(fmt) & int(chars_format::fixed)) != 0)
          s << std::fixed;
        if ((int(fmt) & int(chars_format::hex)) != 0)
          s << std::hexfloat;
201
        return {};
Praetorius, Simon's avatar
Praetorius, Simon committed
202
203
204
205
206
207
208
209
      });
    }

    /// \brief Converts a floating-point value to a character sequence with format specifier and precision
    template <class T,
      std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
    to_chars_result to_chars (char* first, char* last, T value, chars_format fmt, int precision)
    {
210
      return Impl::toCharsImpl(first, last, value, [fmt,precision](auto& s) -> std::error_code
Praetorius, Simon's avatar
Praetorius, Simon committed
211
212
213
214
215
216
217
218
      {
        s << std::setprecision(precision);
        if ((int(fmt) & int(chars_format::scientific)) != 0)
          s << std::scientific;
        if ((int(fmt) & int(chars_format::fixed)) != 0)
          s << std::fixed;
        if ((int(fmt) & int(chars_format::hex)) != 0)
          s << std::hexfloat;
219
        return {};
Praetorius, Simon's avatar
Praetorius, Simon committed
220
221
222
223
224
225
226
227
228
      });
    }

#endif // DUNE_HAVE_CHARCONV

  } // end namespace Std
} // end namespace Dune

#endif // DUNE_COMMON_STD_CHARCONV_HH