string builder: Support integral types directly

Not only convenient but also performance improvement
This commit is contained in:
Martchus 2017-01-30 00:11:33 +01:00
parent df1605c9b1
commit 17fe42e0ad
4 changed files with 65 additions and 26 deletions

View File

@ -1,6 +1,7 @@
#ifndef CONVERSION_UTILITIES_STRINGBUILDER_H
#define CONVERSION_UTILITIES_STRINGBUILDER_H
#include "./stringconversion.h"
#include "../misc/traits.h"
#include <string>
@ -36,6 +37,22 @@ constexpr std::size_t computeTupleElementSize(CharType)
return 1;
}
template <class StringType, typename IntegralType, Traits::EnableIf<Traits::Not<std::is_same<typename StringType::value_type, IntegralType> >, std::is_integral<IntegralType>, std::is_unsigned<IntegralType> >...>
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 <class StringType, typename IntegralType, Traits::EnableIf<Traits::Not<std::is_same<typename StringType::value_type, IntegralType> >, std::is_integral<IntegralType>, std::is_signed<IntegralType> >...>
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<class StringType, Traits::EnableIf<std::is_class<StringType> >...>
void append(StringType &target, const StringType *str)
{
@ -60,6 +77,28 @@ void append(StringType &target, CharType c)
target += c;
}
template <class StringType, typename IntegralType, Traits::EnableIf<Traits::Not<std::is_same<typename StringType::value_type, IntegralType> >, std::is_integral<IntegralType>, std::is_unsigned<IntegralType> >...>
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<typename StringType::value_type>(number % base));
}
}
template <class StringType, typename IntegralType, Traits::EnableIf<Traits::Not<std::is_same<typename StringType::value_type, IntegralType> >, std::is_integral<IntegralType>, std::is_signed<IntegralType> >...>
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<typename StringType::value_type>(number % base));
}
}
template<class StringType, class Tuple, std::size_t N>
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<class Tuple>
constexpr auto operator %(const Tuple &lhs, char rhs) -> decltype(std::tuple_cat(lhs, std::make_tuple(rhs)))
template<class Tuple, typename IntegralType, Traits::EnableIf<std::is_integral<IntegralType> >...>
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<class Tuple, Traits::DisableIfAny<std::is_same<Tuple, std::string>, std::is_same<Tuple, const char *>, std::is_array<Tuple>, std::is_same<Tuple, char> >...>
template<class Tuple, Traits::EnableIf<Traits::IsSpecializationOf<Tuple, std::tuple> >...>
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<class Tuple, Traits::DisableIfAny<std::is_trivial<Tuple>, std::is_same<Tuple, std::string>, std::is_same<Tuple, const char *>, std::is_array<Tuple>, std::is_same<Tuple, char> >...>
template<class Tuple, Traits::EnableIf<Traits::IsSpecializationOf<Tuple, std::tuple> >...>
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<class Tuple, Traits::DisableIfAny<std::is_trivial<Tuple>, std::is_same<Tuple, std::string>, std::is_same<Tuple, const char *>, std::is_array<Tuple>, std::is_same<Tuple, char> >...>
inline std::string operator +(const Tuple &lhs, char rhs)
template<class Tuple, typename IntegralType, Traits::EnableIf<Traits::IsSpecializationOf<Tuple, std::tuple>, std::is_integral<IntegralType> >...>
inline std::string operator +(const Tuple &lhs, IntegralType rhs)
{
return tupleToString(std::tuple_cat(lhs, std::make_tuple(rhs)));
}

View File

@ -307,12 +307,12 @@ string functionTakingString(const string &str)
void ConversionTests::testStringBuilder()
{
// conversion of string-tuple to string (the actual string builder)
const tuple<const char *, string, const char *> tuple("string1", "string2", "string3");
CPPUNIT_ASSERT_EQUAL(string("string1string2string3"), tupleToString(tuple));
const tuple<const char *, string, int, const char *> 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");

View File

@ -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();
}

View File

@ -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