// -*- 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 #ifdef __has_include #if __has_include() && defined(__cpp_lib_to_chars) && !defined(DUNE_NO_STD_CHARCONV) #define DUNE_HAVE_CHARCONV 1 #endif #endif #ifdef DUNE_HAVE_CHARCONV #include #else #include #include #include #include #include #include #include #include #include #include #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 { /** * \brief Performs the conversion of a character sequence [first, last) to * a value of type `T` by using a `std::istringstream` formatted stream extracter. * with format flags set by the `manipulator`. * * On success, returns a value of type \ref from_chars_result such that `ptr` * has the value equal to `last` if all characters match and `ec` is * value-initialized. * * If there is no pattern match, returns a value of type \ref from_chars_result * such that `ptr` equals `first` and `ec` equals `std::errc::invalid_argument`. * `value` is unmodified. * * If the pattern was matched, but the parsed value is not in the range representable * by the type of `value`, returns value of type \ref from_chars_result such * that `ec` equals ´std::errc::result_out_of_range` and `ptr` has value equal * to `last`. `value` is unmodified. * * NOTE: Can not determine pointer to the first character not matching the * pattern, as required by `std::from_chars`. Returning `last` if all * characters are consumed. **/ template 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" if (!manipulator(s)) return from_chars_result{first, std::errc::not_supported}; try { s >> value; // extract value } catch (...) { s.setstate(std::ios_base::failbit); } if (s.fail()) { if (value == std::numeric_limits::max() || value == std::numeric_limits::min()) // range error return from_chars_result{s.eof() ? last : first, std::errc::result_out_of_range}; else // extraction fails return from_chars_result{first, std::errc::invalid_argument}; } return from_chars_result{s.eof() ? last : first, std::errc{}}; } /** * \brief Performas a conversion of `value` of type `T` to a character * sequence stored in the memory range [first, last) by using a * `std::ostringstream` formatted stream inserter with format flags set by * the `manipulator`. * * On success, returns a value of type \ref to_chars_result such that `ec` * equals value-initialized `std::errc` and `ptr` is the one-past-the-end * pointer of the characters written. Note that the string is not NUL-terminated. * * On error, returns a value of type \ref to_chars_result holding * `std::errc::value_too_large` in `ec`, a copy of the value `last` in `ptr`, * and leaves the contents of the range [first, last) in unspecified state. **/ template 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" if (!manipulator(s)) return to_chars_result{last, std::errc::not_supported}; // negative integer values in base != 10 are not correctly converted const 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() : 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. /// \see Impl::fromCharsImpl template::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); return true; }); } /// \brief Converts a character sequence to a floating-point value /// \see Impl::fromCharsImpl // NOTE: Reading hexfloat values not supported by many standard libraries, currently. // So, `chars_format::hex` results in an error. template::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; return false; } return true; }); } /// \brief Converts an integer value to a character sequence /// \see Impl::toCharsImpl template ::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); return true; }); } /// \brief Converts a floating-point value to a character sequence /// \see Impl::toCharsImpl template ::value, int> = 0> to_chars_result to_chars (char* first, char* last, T value) { return Impl::toCharsImpl(first, last, value, [](auto& /*s*/) { return true; }); } /// \brief Converts a floating-point value to a character sequence with additional format specifier /// \see Impl::toCharsImpl template ::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; return true; }); } /// \brief Converts a floating-point value to a character sequence with format specifier and precision /// \see Impl::toCharsImpl template ::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; return true; }); } #endif // DUNE_HAVE_CHARCONV } // end namespace Std } // end namespace Dune #endif // DUNE_COMMON_STD_CHARCONV_HH