charconv.hh 7.03 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
        if (!manipulator(s))
          return from_chars_result{first, std::errc::not_supported};
Praetorius, Simon's avatar
Praetorius, Simon committed
82 83 84 85 86 87 88 89 90 91

        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
92
            return from_chars_result{s.eof() ? last : first, std::errc::result_out_of_range};
Praetorius, Simon's avatar
Praetorius, Simon committed
93 94 95 96 97
          else
            // extraction fails
            return from_chars_result{first, std::errc::invalid_argument};
        }

98 99 100
        // NOTE: Can not determine pointer to the first character not matching the pattern.
        //       Returning last if all characters are consumed.
        return from_chars_result{s.eof() ? last : first, std::errc{}};
Praetorius, Simon's avatar
Praetorius, Simon committed
101 102 103 104 105 106 107 108
      }

      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"
109 110
        if (!manipulator(s))
          return to_chars_result{last, std::errc::not_supported};
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
      return Impl::fromCharsImpl(first, last, value, [base](auto& s)
140 141
      {
        s >> std::setbase(base);
142
        return true;
143
      });
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)
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 false;
          // Reading hexfloat values not supported by many standard libraries, currently.
Praetorius, Simon's avatar
Praetorius, Simon committed
162
        }
163
        return true;
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
      return Impl::toCharsImpl(first, last, value, [base](auto& s)
174 175
      {
        s << std::setbase(base);
176
        return true;
177
      });
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*/) { return true; });
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)
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 true;
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)
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 true;
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