From 4263ace68830f77feeb4060577359bef353cf702 Mon Sep 17 00:00:00 2001 From: Vladimir Batov Date: Fri, 4 Jul 2014 13:45:46 +1000 Subject: [PATCH] cnv::spirit work-in-progress --- .gitignore | 3 + doc/performance.qbk | 3 +- example/getting_started.cpp | 10 ++- include/boost/convert/detail/base.hpp | 8 ++- include/boost/convert/spirit.hpp | 93 ++++++++++++++++----------- include/boost/convert/strtol.hpp | 16 ++--- makefile | 3 +- test/jamfile.v2 | 1 + test/main.cpp | 1 + test/performance.cpp | 15 +++-- test/spirit.cpp | 44 ------------- test/spirit_converter.cpp | 47 ++++++++++++++ test/test.hpp | 1 + 13 files changed, 140 insertions(+), 105 deletions(-) delete mode 100644 test/spirit.cpp create mode 100644 test/spirit_converter.cpp diff --git a/.gitignore b/.gitignore index 6cfbb3e..7ca8ca3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ bin/ doc/bin/ doc/html/ doc/convert_reference.xml +doc/doxygen/ +doc/inspect.txt +convert.pdf *.log *.bat include/boost/none.hpp diff --git a/doc/performance.qbk b/doc/performance.qbk index fea9821..3e31a65 100644 --- a/doc/performance.qbk +++ b/doc/performance.qbk @@ -139,7 +139,8 @@ which is essentially a glorified 12-bytes character buffer sufficient to accommo It produced the following results: - strtol int-to std::string/small-string=14.49/4.02 seconds. + strtol int-to std::string/small-string: 14.56/4.28 seconds. + spirit int-to std::string/small-string: 16.12/6.06 seconds. [endsect] [endsect] diff --git a/example/getting_started.cpp b/example/getting_started.cpp index 56ed75d..5e60f96 100644 --- a/example/getting_started.cpp +++ b/example/getting_started.cpp @@ -85,14 +85,18 @@ static void getting_started_example2() //[getting_started_headers3 #include +#include //] static void getting_started_example3() { //[getting_started_example3 - boost::cnv::strtol cnv; + boost::cnv::lexical_cast cnv1; + boost::cnv::strtol cnv2; + boost::cnv::spirit cnv3; - int i1 = lexical_cast("123"); - int i2 = convert("123", cnv).value(); // Two times faster than lexical_cast. + int i1 = convert("123", cnv1).value(); + int i2 = convert("123", cnv2).value(); // Two times faster than lexical_cast. + int i3 = convert("123", cnv3).value(); // Four times faster than lexical_cast. //] } diff --git a/include/boost/convert/detail/base.hpp b/include/boost/convert/detail/base.hpp index 8350a6d..a7914d4 100644 --- a/include/boost/convert/detail/base.hpp +++ b/include/boost/convert/detail/base.hpp @@ -66,11 +66,14 @@ struct boost::cnv::detail::cnvbase protected: - template string_type format (char*, char*, char const*, char const*) const; + template string_type format (char*, char*, char const*) const; derived const& dncast () const { return *static_cast(this); } derived& dncast () { return *static_cast(this); } + // ULONG_MAX(8 bytes) = 18446744073709551615 (20(10) or 32(2) characters) + // double (8 bytes) max is 316 chars + static int const bufsize_ = 1024; int base_; bool skipws_; int precision_; @@ -88,8 +91,7 @@ string_type boost::cnv::detail::cnvbase::format( char* beg, char* end, - char const* bufbeg, - char const* bufend) const + char const* bufbeg) const { // TODO: Need boundary checks. diff --git a/include/boost/convert/spirit.hpp b/include/boost/convert/spirit.hpp index 4b21a3c..04618b3 100644 --- a/include/boost/convert/spirit.hpp +++ b/include/boost/convert/spirit.hpp @@ -6,12 +6,27 @@ #define BOOST_CONVERT_SPIRIT_BASED_CONVERTER_HPP #include -#include -#include +#include +#include +#include +#include +#include + +#include namespace boost { namespace cnv { struct spirit; + + namespace detail + { + template + struct generator : boost::spirit::traits::create_generator::type {}; + + template <> struct generator< float> : coerce::detail::real_generator {}; + template <> struct generator< double> : coerce::detail::real_generator {}; + template <> struct generator : coerce::detail::real_generator {}; + } }} struct boost::cnv::spirit : public boost::cnv::detail::cnvbase @@ -21,50 +36,56 @@ struct boost::cnv::spirit : public boost::cnv::detail::cnvbase + template void operator()( int v, optional& r) const { to_str(v, r); } + template void operator()( long int v, optional& r) const { to_str(v, r); } + template void operator()( double v, optional& r) const { to_str(v, r); } + template void operator()(long double v, optional& r) const { to_str(v, r); } + + template void operator()(string_type const& s, optional< int>& r) const { str_to(s, r); } + template void operator()(string_type const& s, optional< long int>& r) const { str_to(s, r); } + template void operator()(string_type const& s, optional< double>& r) const { str_to(s, r); } + template void operator()(string_type const& s, optional& r) const { str_to(s, r); } + + template void - operator()(TypeIn const& value_in, boost::optional& result_out) const + str_to(string_type const& string_in, optional& result_out) const { - try - { - result_out = coerce::as(value_in, coerce::tag::none()); + typedef typename boost::range_iterator::type iterator; + typedef typename boost::iterator_range::type iterator_range; + typedef typename boost::spirit::traits::create_parser::type parser; + + iterator_range range = boost::as_literal(string_in); + iterator beg = range.begin(); + iterator end = range.end(); + out_type result; + + if (boost::spirit::qi::parse(beg, end, parser(), result)) + if (beg == end) // ensure the whole string has been parsed + result_out = result; + } + template + void + to_str(in_type const& value_in, optional& result_out) const + { + typedef char const* iterator; + typedef cnv::detail::generator generator; + + char buf[bufsize_]; + char* beg = buf + bufsize_ / 2; + char* end = beg; + bool good = boost::spirit::karma::generate(end, generator(), value_in); + + if (good) + result_out = base_type::format(beg, end, buf); +// result_out = coerce::as(value_in, coerce::tag::none()); // TODO: Just adding 'if' costs about 15-20% performance // compared to that same but DIRECT call to coerce::as or qi::parse. // /**/ if (base_ == 10) result_out = coerce::as(value_in, coerce::tag::none()); // else if (base_ == 8) result_out = coerce::as(value_in, coerce::tag::oct()); // else if (base_ == 16) result_out = coerce::as(value_in, coerce::tag::hex()); - } - catch(boost::coerce::bad_cast const&) - { - } } }; -#ifdef asdasdasdasdasdasd - -#include -#include -#include -#include -#include - - template - typename boost::enable_if, void>::type - operator()(StringIn const& string_in, boost::optional& result_out) const - { - typedef cnv::str::range range_type; - typedef typename range_type::char_type char_type; - - char_type const* beg = cnv::str::range::begin(string_in); - char_type const* end = cnv::str::range::end(string_in); - int result; - - if (boost::spirit::qi::parse(beg, end, boost::spirit::int_, result)) - if (beg == end) // ensure the whole string was parsed - result_out = result; - } -#endif - #endif // BOOST_CONVERT_SPIRIT_BASED_CONVERTER_HPP diff --git a/include/boost/convert/strtol.hpp b/include/boost/convert/strtol.hpp index 2e0f09d..ea118de 100644 --- a/include/boost/convert/strtol.hpp +++ b/include/boost/convert/strtol.hpp @@ -71,10 +71,6 @@ struct boost::cnv::strtol : public boost::cnv::detail::cnvbase string_type d_to_str (double) const; static double adjust_fraction (double, int precision); static int get_char (int v) { return (v < 10) ? (v += '0') : (v += 'A' - 10); } - - // ULONG_MAX(8 bytes) = 18446744073709551615 (20(10) or 32(2) characters) - // double (8 bytes) max is 316 chars - static int const bufsize = 1024; }; template @@ -135,9 +131,9 @@ boost::cnv::strtol::i_to_str(Type value) const { // C1. Base=10 optimization improves performance 10% - char buf[bufsize]; + char buf[bufsize_]; bool const negative = (value < 0) ? (value = -value, true) : false; - char* end = buf + bufsize / 2; + char* end = buf + base_type::bufsize_ / 2; char* beg = end; if (base_ == 10) for (; value; *(--beg) = int(value % 10) + '0', value /= 10); //C1 @@ -146,7 +142,7 @@ boost::cnv::strtol::i_to_str(Type value) const if (beg == end) *(--beg) = '0'; if (negative) *(--beg) = '-'; - return base_type::format(beg, end, buf, buf + bufsize); + return base_type::format(beg, end, buf); } inline @@ -180,8 +176,8 @@ inline string_type boost::cnv::strtol::d_to_str(double value) const { - char buf[bufsize]; - char* end = buf + bufsize / 2; + char buf[bufsize_]; + char* end = buf + base_type::bufsize_ / 2; char* beg = end; char* ipos = end - 1; bool const negative = (value < 0) ? (value = -value, true) : false; @@ -210,7 +206,7 @@ boost::cnv::strtol::d_to_str(double value) const } if (negative) *(--beg) = '-'; - return base_type::format(beg, end, buf, buf + bufsize); + return base_type::format(beg, end, buf); } #endif // BOOST_CONVERT_STRTOL_CONVERTER_HPP diff --git a/makefile b/makefile index ef300ce..e3f7b93 100644 --- a/makefile +++ b/makefile @@ -14,10 +14,10 @@ sources_1 = test/callable.cpp \ test/sfinae.cpp \ test/stream_converter.cpp \ test/strtol_converter.cpp \ + test/spirit_converter.cpp \ test/user_type.cpp target_2 = convert-test-performance.exe -target_3 = convert-test-spirit.exe target_4 = convert-example-algorithms target_5 = convert-example-default_converter target_6 = convert-example-getting_serious @@ -26,7 +26,6 @@ target_8 = convert-example-lexical_cast target_9 = convert-example-stream sources_2 = test/performance.cpp -sources_3 = test/spirit.cpp sources_4 = example/algorithms.cpp sources_5 = example/default_converter.cpp sources_6 = example/getting_serious.cpp diff --git a/test/jamfile.v2 b/test/jamfile.v2 index a771339..d937be8 100644 --- a/test/jamfile.v2 +++ b/test/jamfile.v2 @@ -35,6 +35,7 @@ run callable.cpp sfinae.cpp stream_converter.cpp strtol_converter.cpp + spirit_converter.cpp user_type.cpp : : : : convert_test ; diff --git a/test/main.cpp b/test/main.cpp index 0a35da0..bfe057d 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -18,6 +18,7 @@ int main(int argc, char const* argv[]) { + test::cnv::spirit_converter(); test::cnv::scratchpad(); test::cnv::sfinae(); test::cnv::is_converter(); diff --git a/test/performance.cpp b/test/performance.cpp index f483ce2..fdd185c 100644 --- a/test/performance.cpp +++ b/test/performance.cpp @@ -255,39 +255,42 @@ main(int argc, char const* argv[]) printf("strtol int-to std::string/small-string: %.2f/%.2f seconds.\n", local::to_str(boost::cnv::strtol()), local::to_str< my_string, int>(boost::cnv::strtol())); + printf("spirit int-to std::string/small-string: %.2f/%.2f seconds.\n", + local::to_str(boost::cnv::spirit()), + local::to_str< my_string, int>(boost::cnv::spirit())); //] - printf("int-to-str: spirit/itostr/lcast/prntf/stream=%8.2f/%8.2f/%8.2f/%8.2f/%8.2f seconds.\n", + printf("int-to-str: spirit/itostr/lcast/prntf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n", local::to_str(boost::cnv::spirit()), local::to_str(boost::cnv::strtol()), local::to_str(boost::cnv::lexical_cast()), local::to_str(boost::cnv::printf()), local::to_str(boost::cnv::cstream())); - printf("lng-to-str: spirit/ltostr/lcast/prntf/stream=%8.2f/%8.2f/%8.2f/%8.2f/%8.2f seconds.\n", + printf("lng-to-str: spirit/ltostr/lcast/prntf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n", local::to_str(boost::cnv::spirit()), local::to_str(boost::cnv::strtol()), local::to_str(boost::cnv::lexical_cast()), local::to_str(boost::cnv::printf()), local::to_str(boost::cnv::cstream())); - printf("dbl-to-str: spirit/dtostr/lcast/prntf/stream=%8.2f/%8.2f/%8.2f/%8.2f/%8.2f seconds.\n", + printf("dbl-to-str: spirit/dtostr/lcast/prntf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n", local::to_str(boost::cnv::spirit()), local::to_str(boost::cnv::strtol()(arg::precision = 6)), local::to_str(boost::cnv::lexical_cast()), local::to_str(boost::cnv::printf()(arg::precision = 6)), local::to_str(boost::cnv::cstream()(arg::precision = 6))); - printf("str-to-int: spirit/strtoi/lcast/scanf/stream=%8.2f/%8.2f/%8.2f/%8.2f/%8.2f seconds.\n", + printf("str-to-int: spirit/strtoi/lcast/scanf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n", local::str_to(boost::cnv::spirit()), local::str_to(boost::cnv::strtol()), local::str_to(boost::cnv::lexical_cast()), local::str_to(boost::cnv::printf()), local::str_to(boost::cnv::cstream())); - printf("str-to-lng: spirit/strtol/lcast/scanf/stream=%8.2f/%8.2f/%8.2f/%8.2f/%8.2f seconds.\n", + printf("str-to-lng: spirit/strtol/lcast/scanf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n", local::str_to(boost::cnv::spirit()), local::str_to(boost::cnv::strtol()), local::str_to(boost::cnv::lexical_cast()), local::str_to(boost::cnv::printf()), local::str_to(boost::cnv::cstream())); - printf("str-to-dbl: spirit/strtod/lcast/scanf/stream=%8.2f/%8.2f/%8.2f/%8.2f/%8.2f seconds.\n", + printf("str-to-dbl: spirit/strtod/lcast/scanf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n", local::str_to(boost::cnv::spirit()), local::str_to(boost::cnv::strtol()), local::str_to(boost::cnv::lexical_cast()), diff --git a/test/spirit.cpp b/test/spirit.cpp deleted file mode 100644 index a1e12ed..0000000 --- a/test/spirit.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Boost.Convert test and usage example -// Copyright (c) 2009-2014 Vladimir Batov. -// Use, modification and distribution are subject to the Boost Software License, -// Version 1.0. See http://www.boost.org/LICENSE_1_0.txt. - -#include -#include -#include -#include -#include "./forward.hpp" - -using std::string; -using boost::convert; - -namespace arg = boost::cnv::parameter; - -struct boost::cnv::by_default : public boost::cnv::spirit {}; - -int -main(int argc, char const* argv[]) -{ - boost::cnv::spirit cnv; - - boost::optional i1 = convert("uhm"); - boost::optional i2 = convert("123"); - boost::optional i3 = convert("0x23", cnv(arg::base = boost::cnv::base::hex)); -// boost::optional d1 = convert("12.uhm"); -// boost::optional d2 = convert("12.3"); - boost::optional s1 = convert(123456789012345678); -// boost::optional s2 = convert(12.34); - - BOOST_TEST(!i1); - BOOST_TEST( i2 && i2.value() == 123); - BOOST_TEST( i3 && i3.value() == 35); -// BOOST_TEST(!d1); -// BOOST_TEST( d2 && d2.value() == 12.3); - BOOST_TEST( s1); - BOOST_TEST( s1 && s1.value() == "123456789012345678"); -// BOOST_TEST( s2 && s2.value() == "12.34"); - -// printf("%s\n", s2->c_str()); - - return boost::report_errors(); -} diff --git a/test/spirit_converter.cpp b/test/spirit_converter.cpp new file mode 100644 index 0000000..4d2c420 --- /dev/null +++ b/test/spirit_converter.cpp @@ -0,0 +1,47 @@ +// Boost.Convert test and usage example +// Copyright (c) 2009-2014 Vladimir Batov. +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. See http://www.boost.org/LICENSE_1_0.txt. + +#include +#include +#include +#include +#include "./test.hpp" + +using std::string; +using boost::convert; + +namespace arg = boost::cnv::parameter; + +struct boost::cnv::by_default : public boost::cnv::spirit {}; + +void +test::cnv::spirit_converter() +{ + char const* const c_stri ("12345"); + char const* const c_strd ("123.45"); + std::string const std_stri (c_stri); + std::string const std_strd (c_strd); + my_string const my_stri (c_stri, c_stri + strlen(c_stri)); + my_string const my_strd (c_strd, c_strd + strlen(c_strd)); + + boost::cnv::spirit cnv; + + BOOST_TEST( 12345 == convert< int>( c_stri).value()); + BOOST_TEST( 12345 == convert< int>(std_stri).value()); + BOOST_TEST( 12345 == convert< int>( my_stri).value()); + BOOST_TEST( 12345 == convert( c_stri).value()); + BOOST_TEST( 12345 == convert(std_stri).value()); + BOOST_TEST( 12345 == convert( my_stri).value()); + BOOST_TEST(123.45 == convert< double>( c_strd).value()); + BOOST_TEST(123.45 == convert< double>(std_strd).value()); + BOOST_TEST(123.45 == convert< double>( my_strd).value()); + + BOOST_TEST(!convert< int>("uhm")); + BOOST_TEST(!convert("12.uhm")); + + BOOST_TEST( "1234" == convert( 1234).value()); + BOOST_TEST("12.34" == convert(12.34).value()); + printf("%s\n", convert(12.34).value().c_str()); +} diff --git a/test/test.hpp b/test/test.hpp index bb616ec..7a718ac 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -164,6 +164,7 @@ namespace test static void stream_converter (); static void printf_converter (); static void strtol_converter (); + static void spirit_converter (); static void int_to_string (); static void user_type ();