diff --git a/conversion/stringbuilder.h b/conversion/stringbuilder.h index 69c9ea8..c3c96bc 100644 --- a/conversion/stringbuilder.h +++ b/conversion/stringbuilder.h @@ -1,6 +1,7 @@ #ifndef CONVERSION_UTILITIES_STRINGBUILDER_H #define CONVERSION_UTILITIES_STRINGBUILDER_H +#include "./stringconversion.h" #include "../misc/traits.h" #include @@ -36,6 +37,22 @@ constexpr std::size_t computeTupleElementSize(CharType) return 1; } +template >, std::is_integral, std::is_unsigned >...> +std::size_t computeTupleElementSize(IntegralType number, typename StringType::value_type base = 10) +{ + std::size_t size = 0; + for(auto n = number; n; n /= base, ++size); + return size; +} + +template >, std::is_integral, std::is_signed >...> +std::size_t computeTupleElementSize(IntegralType number, typename StringType::value_type base = 10) +{ + std::size_t size = number < 0 ? 1 : 0; + for(auto n = number; n; n /= base, ++size); + return size; +} + template >...> void append(StringType &target, const StringType *str) { @@ -60,6 +77,28 @@ void append(StringType &target, CharType c) target += c; } +template >, std::is_integral, std::is_unsigned >...> +void append(StringType &target, IntegralType number, typename StringType::value_type base = 10) +{ + const auto start = target.begin() + target.size(); + for(; number; number /= base) { + target.insert(start, digitToChar(number % base)); + } +} + +template >, std::is_integral, std::is_signed >...> +void append(StringType &target, IntegralType number, typename StringType::value_type base = 10) +{ + if(number < 0) { + target += '-'; + number = -number; + } + const auto start = target.begin() + target.size(); + for(; number; number /= base) { + target.insert(start, digitToChar(number % base)); + } +} + template struct TupleToString { static std::size_t precomputeSize(const Tuple &tuple) @@ -140,8 +179,8 @@ constexpr auto operator %(const Tuple &lhs, const char *rhs) -> decltype(std::tu /*! * \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3. */ -template -constexpr auto operator %(const Tuple &lhs, char rhs) -> decltype(std::tuple_cat(lhs, std::make_tuple(rhs))) +template >...> +constexpr auto operator %(const Tuple &lhs, IntegralType rhs) -> decltype(std::tuple_cat(lhs, std::make_tuple(rhs))) { return std::tuple_cat(lhs, std::make_tuple(rhs)); } @@ -195,7 +234,7 @@ constexpr auto operator %(char lhs, const std::string &rhs) -> decltype(std::mak * printVelocity("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)")); * ``` */ -template, std::is_same, std::is_array, std::is_same >...> +template >...> inline std::string operator +(const Tuple &lhs, const std::string &rhs) { return tupleToString(std::tuple_cat(lhs, std::make_tuple(&rhs))); @@ -210,7 +249,7 @@ inline std::string operator +(const Tuple &lhs, const std::string &rhs) * printVelocity("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)")); * ``` */ -template, std::is_same, std::is_same, std::is_array, std::is_same >...> +template >...> inline std::string operator +(const Tuple &lhs, const char *rhs) { return tupleToString(std::tuple_cat(lhs, std::make_tuple(rhs))); @@ -225,8 +264,8 @@ inline std::string operator +(const Tuple &lhs, const char *rhs) * printVelocity("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)")); * ``` */ -template, std::is_same, std::is_same, std::is_array, std::is_same >...> -inline std::string operator +(const Tuple &lhs, char rhs) +template, std::is_integral >...> +inline std::string operator +(const Tuple &lhs, IntegralType rhs) { return tupleToString(std::tuple_cat(lhs, std::make_tuple(rhs))); } diff --git a/tests/conversiontests.cpp b/tests/conversiontests.cpp index b81e47e..b10ace2 100644 --- a/tests/conversiontests.cpp +++ b/tests/conversiontests.cpp @@ -307,12 +307,12 @@ string functionTakingString(const string &str) void ConversionTests::testStringBuilder() { // conversion of string-tuple to string (the actual string builder) - const tuple tuple("string1", "string2", "string3"); - CPPUNIT_ASSERT_EQUAL(string("string1string2string3"), tupleToString(tuple)); + const tuple tuple("string1", "string2", 1234, "string3"); + CPPUNIT_ASSERT_EQUAL(string("string1string21234string3"), tupleToString(tuple)); CPPUNIT_ASSERT_EQUAL(string("foobarfoo2bar2"), tupleToString(string("foo") % "bar" % string("foo2") % "bar2")); // construction of string-tuple and final conversion to string works - CPPUNIT_ASSERT_EQUAL_MESSAGE("result can be passed to any function taking a std::string", string("1234567"), functionTakingString("12" % string("34") % '5' + "67")); + CPPUNIT_ASSERT_EQUAL_MESSAGE("result can be passed to any function taking a std::string", string("123456789"), functionTakingString("12" % string("34") % '5' % 67 + "89")); constexpr double velocityExample = 27.0; CPPUNIT_ASSERT_EQUAL_MESSAGE("real-word example", string("velocity: 27 km/h (7.5 m/s)"), functionTakingString("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)")); CPPUNIT_ASSERT_EQUAL_MESSAGE("regular + operator still works (no problems with ambiguity)", string("regular + still works"), string("regular") + " + still works"); diff --git a/tests/stringbuilder-bench.cpp b/tests/stringbuilder-bench.cpp index 3a1115c..159241d 100644 --- a/tests/stringbuilder-bench.cpp +++ b/tests/stringbuilder-bench.cpp @@ -25,7 +25,7 @@ int main() for(unsigned int r = 0; r < iterations2; ++r) { for(unsigned int i = 0; i < iterations; ++i) { stringstream ss; - v1.emplace_back("left. " + numberToString(i + 1) + "; right: " + numberToString(i + 2) + "; top: " + numberToString(i + 3) + "; bottom: " + numberToString(i + 4) + ';'); + v1.emplace_back("left. "s + numberToString(i + 1) + "; right: "s + numberToString(i + 2) + "; top: "s + numberToString(i + 3) + "; bottom: "s + numberToString(i + 4) + ';'); } v1.clear(); } @@ -36,7 +36,7 @@ int main() for(unsigned int r = 0; r < iterations2; ++r) { for(unsigned int i = 0; i < iterations; ++i) { stringstream ss; - ss << "left: " << (i + 1) << "; right: " << (i + 2) << "; top: " << (i + 3) << "; bottom: " << (i + 4) << ';'; + ss << "left: "s << (i + 1) << "; right: "s << (i + 2) << "; top: "s << (i + 3) << "; bottom: "s << (i + 4) << ';'; v1.emplace_back(ss.str()); } v1.clear(); @@ -48,7 +48,7 @@ int main() t1 = DateTime::now(); for(unsigned int r = 0; r < iterations2; ++r) { for(unsigned int i = 0; i < iterations; ++i) { - v2.emplace_back("left. " % numberToString(i + 1) % "; right: " % numberToString(i + 2) % "; top: " % numberToString(i + 3) % "; bottom: " % numberToString(i + 4) + ';'); + v2.emplace_back("left. "s % (i + 1) % "; right: "s % (i + 2) % "; top: "s % (i + 3) % "; bottom: "s % (i + 4) + ';'); } v2.clear(); } diff --git a/tests/stringbuilder-bench.md b/tests/stringbuilder-bench.md index 641f2d1..edc07ec 100644 --- a/tests/stringbuilder-bench.md +++ b/tests/stringbuilder-bench.md @@ -19,31 +19,31 @@ g++ -O3 stringbuilder-bench.cpp -o stringbuilder-bench-O3 -Wl,-rpath /lib/path - Results with -03: ``` -plus operator: 00:00:17 +plus operator: 00:00:19 stringstream: 00:00:16 -string builder: 00:00:07 -diff (stringstream minus string builder): 00:00:09 -factor (stringstream / string builder): 2.28571 +string builder: 00:00:05 +diff (stringstream minus string builder): 00:00:11 +factor (stringstream / string builder): 3.2 ``` However, with -O0 4 times *slower*: ``` -plus operator: 00:00:29 -stringstream: 00:00:16 -string builder: 00:01:04 -diff (stringstream minus string builder): - 00:35:22 -factor (stringstream / string builder): 0.25 +plus operator: 00:00:34 +stringstream: 00:00:23 +string builder: 00:01:02 +diff (stringstream minus string builder): - 00:35:31 +factor (stringstream / string builder): 0.370968 ``` -Still 1.45 times faster than stringstream with -O2: +Still 2.42 times faster than stringstream with -O2: ``` plus operator: 00:00:21 -stringstream: 00:00:16 -string builder: 00:00:11 -diff (stringstream minus string builder): 00:00:05 -factor (stringstream / string builder): 1.45455 +stringstream: 00:00:17 +string builder: 00:00:07 +diff (stringstream minus string builder): 00:00:10 +factor (stringstream / string builder): 2.42857 ``` So this basic tests show that string builder is up to 2 times faster when using full optimization