From ccd9acfecc24534e5969ec32de5934c723bcce5a Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sat, 19 Jan 2013 10:12:32 -0800 Subject: [PATCH] default to fast, simple, and correct (per ostringstream) to_string conversions fully removing boost::lexical_cast, disabling karma, and fixing up tests --- src/conversions.cpp | 130 ++++++++++++++-------- tests/cpp_tests/conversions_test.cpp | 158 +++++++++++++++++++++++++-- 2 files changed, 233 insertions(+), 55 deletions(-) diff --git a/src/conversions.cpp b/src/conversions.cpp index 34666c77b..0ded98381 100644 --- a/src/conversions.cpp +++ b/src/conversions.cpp @@ -24,7 +24,8 @@ #include #include -// boost +#include + #include #define BOOST_SPIRIT_AUTO(domain_, name, expr) \ @@ -34,20 +35,18 @@ boost::spirit::domain_::domain, name##_expr_type); \ BOOST_AUTO(name, boost::proto::deep_copy(expr)); \ -#include // log10 +//#define MAPNIK_KARMA_TO_STRING -// boost +#ifdef MAPNIK_KARMA_TO_STRING #include -#include // trunc to avoid needing C++11 - -#if BOOST_VERSION >= 104500 -#include -#include -#else -#include +#if BOOST_VERSION < 104500 +#error you must have >= boost 104500 to use karma for string output #endif -#include +#include +#include // log10 +#include // trunc to avoid needing C++11 +#endif namespace mapnik { @@ -62,8 +61,6 @@ BOOST_SPIRIT_AUTO(qi, LONGLONG, qi::long_long) BOOST_SPIRIT_AUTO(qi, FLOAT, qi::float_) BOOST_SPIRIT_AUTO(qi, DOUBLE, qi::double_) - - struct bool_symbols : qi::symbols { bool_symbols() @@ -163,7 +160,7 @@ bool string2float(const char * value, float & result) return r && (iter == end); } -#if BOOST_VERSION >= 104500 +#ifdef MAPNIK_KARMA_TO_STRING bool to_string(std::string & str, int value) { @@ -286,48 +283,89 @@ bool to_string(std::string & str, double value) #else -template -bool to_string_lexical(std::string & str, T value) +bool to_string(std::string & s, int val) { - try + s.resize(s.capacity()); + while (true) { - str = boost::lexical_cast(value); - return true; + size_t n2 = static_cast(snprintf(&s[0], s.size()+1, "%d", val)); + if (n2 <= s.size()) + { + s.resize(n2); + break; + } + s.resize(n2); } - catch (std::exception const& ex) - { - return false; - } -} - -bool to_string(std::string & str, int value) -{ - return to_string_lexical(str, value); + return true; } #ifdef BIGINT -bool to_string(std::string & str, mapnik::value_integer value) +bool to_string(std::string & s, mapnik::value_integer val) { - return to_string_lexical(str, value); + s.resize(s.capacity()); + while (true) + { + size_t n2 = static_cast(snprintf(&s[0], s.size()+1, "%lld", val)); + if (n2 <= s.size()) + { + s.resize(n2); + break; + } + s.resize(n2); + } + return true; } - -bool to_string(std::string & str, unsigned value) -{ - return to_string_lexical(str, value); -} - -bool to_string(std::string & str, bool value) -{ - return to_string_lexical(str, value); -} - -bool to_string(std::string & str, double value) -{ - return to_string_lexical(str, value); -} - #endif +bool to_string(std::string & s, unsigned val) +{ + s.resize(s.capacity()); + while (true) + { + size_t n2 = static_cast(snprintf(&s[0], s.size()+1, "%u", val)); + if (n2 <= s.size()) + { + s.resize(n2); + break; + } + s.resize(n2); + } + return true; +} + +bool to_string(std::string & s, bool val) +{ + if (val) s = "true"; + else s = "false"; + return true; +} + +bool to_string(std::string & s, double val) +{ + double abs_n = std::fabs(val); + std::string format; + if (abs_n >= 1e16 || abs_n < 1e-4) + { + format = "%e"; + } + else + { + format = "%g"; + } + s.resize(s.capacity()); + while (true) + { + size_t n2 = static_cast(snprintf(&s[0], s.size()+1, "%g", val)); + if (n2 <= s.size()) + { + s.resize(n2); + break; + } + s.resize(n2); + } + return true; +} + #endif } // end namespace util diff --git a/tests/cpp_tests/conversions_test.cpp b/tests/cpp_tests/conversions_test.cpp index ded5c917f..bdb9d102c 100644 --- a/tests/cpp_tests/conversions_test.cpp +++ b/tests/cpp_tests/conversions_test.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -69,16 +70,104 @@ int main( int, char*[] ) BOOST_TEST_EQ( out, "0.000123457" ); out.clear(); - to_string(out, double(1000000000000000)); - BOOST_TEST_EQ( out, "1000000000000000" ); + to_string(out, double(0.0001)); + BOOST_TEST_EQ( out, "0.0001" ); out.clear(); - + + to_string(out, double(0.00001)); + BOOST_TEST_EQ( out, "1e-05" ); + out.clear(); + + to_string(out, double(0.000001)); + BOOST_TEST_EQ( out, "1e-06" ); + out.clear(); + + to_string(out, double(0.0000001)); + BOOST_TEST_EQ( out, "1e-07" ); + out.clear(); + + to_string(out, double(0.00000001)); + BOOST_TEST_EQ( out, "1e-08" ); + out.clear(); + + to_string(out, double(0.000000001)); + BOOST_TEST_EQ( out, "1e-09" ); + out.clear(); + + to_string(out, double(0.0000000001)); + BOOST_TEST_EQ( out, "1e-10" ); + out.clear(); + + to_string(out, double(0.00000000001)); + BOOST_TEST_EQ( out, "1e-11" ); + out.clear(); + + to_string(out, double(0.000000000001)); + BOOST_TEST_EQ( out, "1e-12" ); + out.clear(); + + to_string(out, double(0.0000000000001)); + BOOST_TEST_EQ( out, "1e-13" ); + out.clear(); + + to_string(out, double(0.00000000000001)); + BOOST_TEST_EQ( out, "1e-14" ); + out.clear(); + + to_string(out, double(0.000000000000001)); + BOOST_TEST_EQ( out, "1e-15" ); + out.clear(); + + to_string(out, double(100000)); + BOOST_TEST_EQ( out, "100000" ); + out.clear(); + + to_string(out, double(1000000)); + BOOST_TEST_EQ( out, "1e+06" ); + out.clear(); + + to_string(out, double(10000000)); + BOOST_TEST_EQ( out, "1e+07" ); + out.clear(); + + to_string(out, double(100000000)); + BOOST_TEST_EQ( out, "1e+08" ); + out.clear(); + + to_string(out, double(1000000000)); + BOOST_TEST_EQ( out, "1e+09" ); + out.clear(); + + to_string(out, double(10000000000)); + BOOST_TEST_EQ( out, "1e+10" ); + out.clear(); + + to_string(out, double(100000000000)); + BOOST_TEST_EQ( out, "1e+11" ); + out.clear(); + + to_string(out, double(1000000000000)); + BOOST_TEST_EQ( out, "1e+12" ); + out.clear(); + + to_string(out, double(10000000000000)); + BOOST_TEST_EQ( out, "1e+13" ); + out.clear(); + + to_string(out, double(100000000000000)); + BOOST_TEST_EQ( out, "1e+14" ); + out.clear(); + + to_string(out, double(1000000000000005)); + BOOST_TEST_EQ( out, "1e+15" ); + out.clear(); + to_string(out, double(-1000000000000000)); - BOOST_TEST_EQ( out, "-1000000000000000" ); + BOOST_TEST_EQ( out, "-1e+15" ); out.clear(); to_string(out, double(100000000000000.1)); - BOOST_TEST_EQ( out, "100000000000000.1" ); + BOOST_TEST_EQ( out, "1e+14" ); out.clear(); to_string(out, double(1.00001)); @@ -86,7 +175,11 @@ int main( int, char*[] ) out.clear(); to_string(out, double(1234000000000000)); - BOOST_TEST_EQ( out, "1234000000000000" ); + BOOST_TEST_EQ( out, "1.234e+15" ); + out.clear(); + + to_string(out, double(1e+16)); + BOOST_TEST_EQ( out, "1e+16" ); out.clear(); to_string(out, double(1.234e+16)); @@ -96,12 +189,59 @@ int main( int, char*[] ) to_string(out, double(-1.234e+16)); BOOST_TEST_EQ( out, "-1.234e+16" ); out.clear(); + // https://github.com/mapbox/tilemill/issues/1456 + to_string(out, double(8.3)); + BOOST_TEST_EQ( out, "8.3" ); + out.clear(); - // Test int - - to_string(out, int(2)); + // int + to_string(out, int(2)); BOOST_TEST_EQ( out, "2" ); out.clear(); + + to_string(out, int(0)); + BOOST_TEST_EQ( out, "0" ); + out.clear(); + + to_string(out, int(-2)); + BOOST_TEST_EQ( out, "-2" ); + out.clear(); + + to_string(out, int(2147483647)); + BOOST_TEST_EQ( out, "2147483647" ); + out.clear(); + + to_string(out, int(-2147483648)); + BOOST_TEST_EQ( out, "-2147483648" ); + out.clear(); + + // unsigned + to_string(out, unsigned(4294967295)); + BOOST_TEST_EQ( out, "4294967295" ); + out.clear(); + +#ifdef BIGINT + // long long + to_string(out,mapnik::value_integer(-0)); + BOOST_TEST_EQ( out, "0" ); + out.clear(); + + to_string(out,mapnik::value_integer(-2)); + BOOST_TEST_EQ( out, "-2" ); + out.clear(); + + to_string(out,mapnik::value_integer(9223372036854775807)); + BOOST_TEST_EQ( out, "9223372036854775807" ); + out.clear(); +#endif + // bool + to_string(out, true); + BOOST_TEST_EQ( out, "true" ); + out.clear(); + + to_string(out, false); + BOOST_TEST_EQ( out, "false" ); + out.clear(); } catch (std::exception const & ex) {