diff --git a/include/boost/convert/base.hpp b/include/boost/convert/base.hpp index 033437e..7758fd4 100644 --- a/include/boost/convert/base.hpp +++ b/include/boost/convert/base.hpp @@ -89,7 +89,8 @@ struct boost::cnv::cnvbase BOOST_CNV_STRING_TO (string_type const& s, optional< ldbl_type>& r) const { str_to_(s, r); } template - derived_type& operator()(argument_pack const& arg) + typename std::enable_if::value, derived_type&>::type + operator()(argument_pack const& arg) { BOOST_CNV_PARAM_TRY(base); BOOST_CNV_PARAM_TRY(adjust); @@ -98,6 +99,7 @@ struct boost::cnv::cnvbase BOOST_CNV_PARAM_TRY(skipws); BOOST_CNV_PARAM_TRY(width); BOOST_CNV_PARAM_TRY(fill); + BOOST_CNV_PARAM_TRY(notation); // BOOST_CNV_PARAM_TRY(locale); return this->dncast(); @@ -105,16 +107,7 @@ struct boost::cnv::cnvbase protected: - cnvbase() - : - skipws_ (false), - precision_ (0), - uppercase_ (false), - width_ (0), - fill_ (' '), - base_ (boost::cnv::base::dec), - adjust_ (boost::cnv::adjust::right) - {} + cnvbase() = default; template void @@ -185,20 +178,22 @@ struct boost::cnv::cnvbase BOOST_CNV_PARAM_SET(skipws) { skipws_ = arg[cnv::parameter:: skipws]; } BOOST_CNV_PARAM_SET(width) { width_ = arg[cnv::parameter:: width]; } BOOST_CNV_PARAM_SET(fill) { fill_ = arg[cnv::parameter:: fill]; } + BOOST_CNV_PARAM_SET(notation) { notation_ = arg[cnv::parameter:: notation]; } // BOOST_CNV_PARAM_SET(locale) { locale_ = arg[cnv::parameter:: locale]; } // ULONG_MAX(8 bytes) = 18446744073709551615 (20(10) or 32(2) characters) // double (8 bytes) max is 316 chars - static int BOOST_CONSTEXPR_OR_CONST bufsize_ = 512; + static int inline BOOST_CONSTEXPR_OR_CONST bufsize_ = 512; - bool skipws_; - int precision_; - bool uppercase_; - int width_; - int fill_; - cnv::base base_; - cnv::adjust adjust_; -// std::locale locale_; + bool skipws_ = false; + int precision_ = 0; + bool uppercase_ = false; + int width_ = 0; + int fill_ = ' '; + cnv::base base_ = boost::cnv::base::dec; + cnv::adjust adjust_ = boost::cnv::adjust::right; + cnv::notation notation_ = boost::cnv::notation::fixed; +// std::locale locale_; }; #undef BOOST_CNV_TO_STRING diff --git a/include/boost/convert/detail/char.hpp b/include/boost/convert/detail/char.hpp index b1201a0..a3d49e4 100644 --- a/include/boost/convert/detail/char.hpp +++ b/include/boost/convert/detail/char.hpp @@ -14,6 +14,7 @@ namespace boost { namespace cnv using char_type = char; using uchar_type = unsigned char; using wchar_type = wchar_t; + using char_cptr = char const*; namespace detail { diff --git a/include/boost/convert/parameters.hpp b/include/boost/convert/parameters.hpp index 1f4c28f..96195ff 100644 --- a/include/boost/convert/parameters.hpp +++ b/include/boost/convert/parameters.hpp @@ -13,7 +13,7 @@ namespace boost { namespace cnv { enum class adjust { left, right, center }; enum class base { bin =2, oct =8, dec =10, hex =16 }; - enum class notation { fixed, scientific }; + enum class notation { fixed, scientific, hex }; namespace parameter { diff --git a/include/boost/convert/printf.hpp b/include/boost/convert/printf.hpp index 22ed77b..801dcc6 100644 --- a/include/boost/convert/printf.hpp +++ b/include/boost/convert/printf.hpp @@ -21,6 +21,7 @@ struct boost::cnv::printf : boost::cnv::cnvbase { using this_type = boost::cnv::printf; using base_type = boost::cnv::cnvbase; + using fmt_type = char_cptr const*; using base_type::operator(); @@ -58,27 +59,65 @@ struct boost::cnv::printf : boost::cnv::cnvbase return type_pos::value; } - char const* printf_format(int pos) const + char_cptr fmt( + int pos, + fmt_type fxd_d_fmt, fmt_type fxd_x_fmt, fmt_type fxd_o_fmt, + fmt_type sci_d_fmt, fmt_type sci_x_fmt, fmt_type sci_o_fmt, + fmt_type hex_d_fmt, fmt_type hex_x_fmt, fmt_type hex_o_fmt) const { - static char const* d_fmt[] = { "%.*f", "%.*f", "%.*d", "%.*u", "%.*hd", "%.*hu", "%.*ld", "%.*lu" }; // Must match managed_types - static char const* x_fmt[] = { "%.*f", "%.*f", "%.*x", "%.*x", "%.*hx", "%.*hx", "%.*lx", "%.*lx" }; // Must match managed_types - static char const* o_fmt[] = { "%.*f", "%.*f", "%.*o", "%.*o", "%.*ho", "%.*ho", "%.*lo", "%.*lo" }; // Must match managed_types - char const* fmt = base_ == boost::cnv::base::dec ? d_fmt[pos] - : base_ == boost::cnv::base::hex ? x_fmt[pos] - : base_ == boost::cnv::base::oct ? o_fmt[pos] - : (BOOST_ASSERT(0), nullptr); - return fmt; + if (notation_ == notation::fixed) + return base_ == base::dec ? fxd_d_fmt[pos] + : base_ == base::hex ? fxd_x_fmt[pos] + : base_ == base::oct ? fxd_o_fmt[pos] + : (BOOST_ASSERT(0), nullptr); + + if (notation_ == notation::scientific) + return base_ == base::dec ? sci_d_fmt[pos] + : base_ == base::hex ? sci_x_fmt[pos] + : base_ == base::oct ? sci_o_fmt[pos] + : (BOOST_ASSERT(0), nullptr); + + if (notation_ == notation::hex) + return base_ == base::dec ? hex_d_fmt[pos] + : base_ == base::hex ? hex_x_fmt[pos] + : base_ == base::oct ? hex_o_fmt[pos] + : (BOOST_ASSERT(0), nullptr); + + return (BOOST_ASSERT(0), nullptr); } - char const* sscanf_format(int pos) const + char_cptr printf_format(int pos) const { - static char const* d_fmt[] = { "%lf", "%f", "%d", "%u", "%hd", "%hu", "%ld", "%lu" }; // Must match managed_types - static char const* x_fmt[] = { "%lf", "%f", "%x", "%x", "%hx", "%hx", "%lx", "%lx" }; // Must match managed_types - static char const* o_fmt[] = { "%lf", "%f", "%o", "%o", "%ho", "%ho", "%lo", "%lo" }; // Must match managed_types - char const* fmt = base_ == boost::cnv::base::dec ? d_fmt[pos] - : base_ == boost::cnv::base::hex ? x_fmt[pos] - : base_ == boost::cnv::base::oct ? o_fmt[pos] - : (BOOST_ASSERT(0), nullptr); - return fmt; + char_cptr constexpr fxd_d_fmt[] = { "%.*f", "%.*f", "%.*d", "%.*u", "%.*hd", "%.*hu", "%.*ld", "%.*lu" }; // Must match managed_types + char_cptr constexpr fxd_x_fmt[] = { "%.*f", "%.*f", "%.*x", "%.*x", "%.*hx", "%.*hx", "%.*lx", "%.*lx" }; // Must match managed_types + char_cptr constexpr fxd_o_fmt[] = { "%.*f", "%.*f", "%.*o", "%.*o", "%.*ho", "%.*ho", "%.*lo", "%.*lo" }; // Must match managed_types + char_cptr constexpr sci_d_fmt[] = { "%.*e", "%.*e", "%.*d", "%.*u", "%.*hd", "%.*hu", "%.*ld", "%.*lu" }; // Must match managed_types + char_cptr constexpr sci_x_fmt[] = { "%.*e", "%.*e", "%.*x", "%.*x", "%.*hx", "%.*hx", "%.*lx", "%.*lx" }; // Must match managed_types + char_cptr constexpr sci_o_fmt[] = { "%.*e", "%.*e", "%.*o", "%.*o", "%.*ho", "%.*ho", "%.*lo", "%.*lo" }; // Must match managed_types + char_cptr constexpr hex_d_fmt[] = { "%.*a", "%.*a", "%.*d", "%.*u", "%.*hd", "%.*hu", "%.*ld", "%.*lu" }; // Must match managed_types + char_cptr constexpr hex_x_fmt[] = { "%.*a", "%.*a", "%.*x", "%.*x", "%.*hx", "%.*hx", "%.*lx", "%.*lx" }; // Must match managed_types + char_cptr constexpr hex_o_fmt[] = { "%.*a", "%.*a", "%.*o", "%.*o", "%.*ho", "%.*ho", "%.*lo", "%.*lo" }; // Must match managed_types + + return fmt(pos, + fxd_d_fmt, fxd_x_fmt, fxd_o_fmt, + sci_d_fmt, sci_x_fmt, sci_o_fmt, + hex_d_fmt, hex_x_fmt, hex_o_fmt); + } + char_cptr sscanf_format(int pos) const + { + char_cptr constexpr fxd_d_fmt[] = { "%lf", "%f", "%d", "%u", "%hd", "%hu", "%ld", "%lu" }; // Must match managed_types + char_cptr constexpr fxd_x_fmt[] = { "%lf", "%f", "%x", "%x", "%hx", "%hx", "%lx", "%lx" }; // Must match managed_types + char_cptr constexpr fxd_o_fmt[] = { "%lf", "%f", "%o", "%o", "%ho", "%ho", "%lo", "%lo" }; // Must match managed_types + char_cptr constexpr sci_d_fmt[] = { "%le", "%e", "%d", "%u", "%hd", "%hu", "%ld", "%lu" }; // Must match managed_types + char_cptr constexpr sci_x_fmt[] = { "%le", "%e", "%x", "%x", "%hx", "%hx", "%lx", "%lx" }; // Must match managed_types + char_cptr constexpr sci_o_fmt[] = { "%le", "%e", "%o", "%o", "%ho", "%ho", "%lo", "%lo" }; // Must match managed_types + char_cptr constexpr hex_d_fmt[] = { "%la", "%a", "%d", "%u", "%hd", "%hu", "%ld", "%lu" }; // Must match managed_types + char_cptr constexpr hex_x_fmt[] = { "%la", "%a", "%x", "%x", "%hx", "%hx", "%lx", "%lx" }; // Must match managed_types + char_cptr constexpr hex_o_fmt[] = { "%la", "%a", "%o", "%o", "%ho", "%ho", "%lo", "%lo" }; // Must match managed_types + + return fmt(pos, + fxd_d_fmt, fxd_x_fmt, fxd_o_fmt, + sci_d_fmt, sci_x_fmt, sci_o_fmt, + hex_d_fmt, hex_x_fmt, hex_o_fmt); } }; diff --git a/include/boost/convert/spirit.hpp b/include/boost/convert/spirit.hpp index 93340d4..aa5ecaf 100644 --- a/include/boost/convert/spirit.hpp +++ b/include/boost/convert/spirit.hpp @@ -45,7 +45,7 @@ struct boost::cnv::spirit : public boost::cnv::cnvbase char_type* end = beg; bool good = boost::spirit::karma::generate(end, generator(), value_in); - + return cnv::range(beg, good ? end : beg); } }; diff --git a/include/boost/convert/stream.hpp b/include/boost/convert/stream.hpp index e35069c..d01e713 100644 --- a/include/boost/convert/stream.hpp +++ b/include/boost/convert/stream.hpp @@ -156,8 +156,9 @@ struct boost::cnv::basic_stream { cnv::notation notation = arg[cnv::parameter::notation]; - /**/ if (notation == cnv::notation:: fixed) std::fixed(stream_); + /**/ if (notation == cnv::notation:: fixed) std::fixed(stream_); else if (notation == cnv::notation::scientific) std::scientific(stream_); + else if (notation == cnv::notation:: hex) std::hexfloat(stream_); else BOOST_ASSERT(!"Not implemented"); } diff --git a/include/boost/convert/strtol.hpp b/include/boost/convert/strtol.hpp index e61926d..65e329a 100644 --- a/include/boost/convert/strtol.hpp +++ b/include/boost/convert/strtol.hpp @@ -153,21 +153,21 @@ boost::cnv::strtol::str_to_i(cnv::range range, boost::optional; using iterator = typename range_type::iterator; - iterator s = range.begin(); - uint_type ch = *s; - bool const is_negative = ch == '-' ? (ch = *++s, true) : ch == '+' ? (ch = *++s, false) : false; - bool const is_unsigned = std::is_same::value; - uint_type base = uint_type(base_); + iterator s = range.begin(); + uint_type ch = *s; + bool is_negative = ch == '-' ? (ch = *++s, true) : ch == '+' ? (ch = *++s, false) : false; + bool is_unsigned = std::is_same::value; + uint_type base = uint_type(base_); /**/ if (is_negative && is_unsigned) return; else if ((base == 0 || base == 16) && ch == '0' && (*++s == 'x' || *s == 'X')) ++s, base = 16; else if (base == 0) base = ch == '0' ? (++s, 8) : 10; - unsigned_type const max = (std::numeric_limits::max)(); - unsigned_type const umax = max + (is_negative ? 1 : 0); - unsigned_type const cutoff = umax / base; - uint_type const cutlim = umax % base; - unsigned_type result = 0; + unsigned_type max = (std::numeric_limits::max)(); + unsigned_type umax = max + (is_negative ? 1 : 0); + unsigned_type cutoff = umax / base; + uint_type cutlim = umax % base; + unsigned_type result = 0; for (; s != range.sentry(); ++s) { diff --git a/test/jamfile.v2 b/test/jamfile.v2 index 4251c5d..ee773a5 100644 --- a/test/jamfile.v2 +++ b/test/jamfile.v2 @@ -29,8 +29,6 @@ exe convert_test_performance_spirit : performance_spirit.cpp ; run callable.cpp : : : : convert_test_callable ; run fallbacks.cpp : : : : convert_test_fallbacks ; run spirit_converter.cpp : : : : convert_test_spirit_converter ; -run stream_converter.cpp : : : : convert_test_stream_converter ; -run printf_converter.cpp : : : : convert_test_printf_converter ; run strtol_converter.cpp : : : : convert_test_strtol_converter ; run lcast_converter.cpp : : : : convert_test_lcast_converter ; run encryption.cpp : : : : convert_test_encryption ; @@ -38,4 +36,8 @@ run user_type.cpp : : : : convert_test_user_type ; run str_to_int.cpp : : : : convert_test_str_to_int ; run sfinae.cpp : : : : convert_test_sfinae ; run has_member.cpp : : : : convert_test_has_member ; +run printf_converter.cpp : + /boost/test/included_unit_test_framework : : : convert_test_printf_converter ; +run stream_converter.cpp : + /boost/test/included_unit_test_framework : : : convert_test_stream_converter ; diff --git a/test/performance_spirit.cpp b/test/performance_spirit.cpp index 0131a64..3e9b586 100644 --- a/test/performance_spirit.cpp +++ b/test/performance_spirit.cpp @@ -20,7 +20,6 @@ int main(int, char const* []) { return 0; } #include #include -#include #include namespace diff --git a/test/printf_converter.cpp b/test/printf_converter.cpp index 66f76c1..5907196 100644 --- a/test/printf_converter.cpp +++ b/test/printf_converter.cpp @@ -11,12 +11,33 @@ int main(int, char const* []) { return 0; } #include #include +#include using std::string; using boost::convert; +namespace cnv = boost::cnv; namespace arg = boost::cnv::parameter; +static +void +test_notation() +{ + //[charconv_notation + boost::cnv::printf cnv; + + BOOST_TEST( "-3.14159" == convert(-3.14159, cnv(arg::notation = cnv::notation::fixed)(arg::precision = 5)).value()); + BOOST_TEST( "-3.142e+00" == convert(-3.14159, cnv(arg::notation = cnv::notation::scientific)(arg::precision = 3)).value()); + BOOST_TEST("-0x1.9220p+1" == convert(-3.14159, cnv(arg::notation = cnv::notation::hex)(arg::precision = 4)).value()); + + const auto close = boost::math::fpc::close_at_tolerance(1); + + BOOST_TEST_WITH(-3.14159, convert("-3.14159", cnv(arg::notation = cnv::notation::fixed)).value(), close); + BOOST_TEST_WITH(-3.14159, convert("-3.142e+00", cnv(arg::notation = cnv::notation::scientific)).value(), close); + BOOST_TEST_WITH(-3.14159, convert("-0x1.9220p+1", cnv(arg::notation = cnv::notation::hex)).value(), close); + //] +} + int main(int, char const* []) { @@ -43,6 +64,8 @@ main(int, char const* []) BOOST_TEST(s01 == "12.345600"); BOOST_TEST(s02 == "12.346"); + test_notation(); + return boost::report_errors(); } diff --git a/test/stream_converter.cpp b/test/stream_converter.cpp index 2f79831..a417527 100644 --- a/test/stream_converter.cpp +++ b/test/stream_converter.cpp @@ -11,6 +11,7 @@ int main(int, char const* []) { return 0; } #include #include +#include #include #include #include @@ -348,6 +349,28 @@ test_user_str() //] } +static +void +test_notation() +{ + //[charconv_notation + boost::cnv::cstream cnv; + + BOOST_TEST( "-3.14159" == convert(-3.14159, cnv(arg::notation = cnv::notation::fixed)(arg::precision = 5)).value()); + BOOST_TEST("-3.142e+00" == convert(-3.14159, cnv(arg::notation = cnv::notation::scientific)(arg::precision = 3)).value()); + + // precision doesn't affect hexfloat + BOOST_TEST("-0x1.921f9f01b866ep+1" == convert(-3.14159, cnv(arg::notation = cnv::notation::hex)).value()); + + const auto close = boost::math::fpc::close_at_tolerance(1); + + BOOST_TEST_WITH(-3.14159, convert("-3.14159", cnv(arg::notation = cnv::notation::fixed)).value(), close); + BOOST_TEST_WITH(-3.14159, convert("-3.142e+00", cnv(arg::notation = cnv::notation::scientific)).value(), close); + // not supported due to https://gcc.gnu.org/bugzilla//show_bug.cgi?id=81122 + // BOOST_TEST_WITH(-3.14159, convert("-0x1.921f9f01b866ep+1", cnv(arg::notation = cnv::notation::hex)).value(), close); + //] +} + int main(int, char const* []) { @@ -365,6 +388,7 @@ main(int, char const* []) test_locale(); test_dbl_to_str(); test_user_str(); + test_notation(); } catch(boost::bad_optional_access const&) {