charconv.hh 6.58 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
80
81
82
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
108
109
110
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// -*- 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"
        manipulator(s);

        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"
        manipulator(s);

        // 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)
    {
      return Impl::fromCharsImpl(first, last, value, [base](auto& s) { s >> std::setbase(base); });
    }

    /// \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)
    {
      return Impl::fromCharsImpl(first, last, value, [fmt](auto& s)
      {
        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;
          DUNE_THROW(NotImplemented,
            "Reading hexfloat values not supported by many standard libraries currently.");
        }
      });
    }


    /// \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)
    {
      return Impl::toCharsImpl(first, last, value, [base](auto& s) { s << std::setbase(base); });
    }

    /// \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)
    {
      return Impl::toCharsImpl(first, last, value, [](auto& /*s*/) {});
    }

    /// \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)
    {
      return Impl::toCharsImpl(first, last, value, [fmt](auto& s)
      {
        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;
      });
    }

    /// \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)
    {
      return Impl::toCharsImpl(first, last, value, [fmt,precision](auto& s)
      {
        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;
      });
    }

#endif // DUNE_HAVE_CHARCONV

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

#endif // DUNE_COMMON_STD_CHARCONV_HH