string builder: Support integral types directly
Not only convenient but also performance improvement
This commit is contained in:
parent
df1605c9b1
commit
17fe42e0ad
|
@ -1,6 +1,7 @@
|
||||||
#ifndef CONVERSION_UTILITIES_STRINGBUILDER_H
|
#ifndef CONVERSION_UTILITIES_STRINGBUILDER_H
|
||||||
#define CONVERSION_UTILITIES_STRINGBUILDER_H
|
#define CONVERSION_UTILITIES_STRINGBUILDER_H
|
||||||
|
|
||||||
|
#include "./stringconversion.h"
|
||||||
#include "../misc/traits.h"
|
#include "../misc/traits.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -36,6 +37,22 @@ constexpr std::size_t computeTupleElementSize(CharType)
|
||||||
return 1;
|
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> >...>
|
template<class StringType, Traits::EnableIf<std::is_class<StringType> >...>
|
||||||
void append(StringType &target, const StringType *str)
|
void append(StringType &target, const StringType *str)
|
||||||
{
|
{
|
||||||
|
@ -60,6 +77,28 @@ void append(StringType &target, CharType c)
|
||||||
target += 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>
|
template<class StringType, class Tuple, std::size_t N>
|
||||||
struct TupleToString {
|
struct TupleToString {
|
||||||
static std::size_t precomputeSize(const Tuple &tuple)
|
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.
|
* \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3.
|
||||||
*/
|
*/
|
||||||
template<class Tuple>
|
template<class Tuple, typename IntegralType, Traits::EnableIf<std::is_integral<IntegralType> >...>
|
||||||
constexpr auto operator %(const Tuple &lhs, char rhs) -> decltype(std::tuple_cat(lhs, std::make_tuple(rhs)))
|
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));
|
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)"));
|
* 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)
|
inline std::string operator +(const Tuple &lhs, const std::string &rhs)
|
||||||
{
|
{
|
||||||
return tupleToString(std::tuple_cat(lhs, std::make_tuple(&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)"));
|
* 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)
|
inline std::string operator +(const Tuple &lhs, const char *rhs)
|
||||||
{
|
{
|
||||||
return tupleToString(std::tuple_cat(lhs, std::make_tuple(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)"));
|
* 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, typename IntegralType, Traits::EnableIf<Traits::IsSpecializationOf<Tuple, std::tuple>, std::is_integral<IntegralType> >...>
|
||||||
inline std::string operator +(const Tuple &lhs, char rhs)
|
inline std::string operator +(const Tuple &lhs, IntegralType rhs)
|
||||||
{
|
{
|
||||||
return tupleToString(std::tuple_cat(lhs, std::make_tuple(rhs)));
|
return tupleToString(std::tuple_cat(lhs, std::make_tuple(rhs)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,12 +307,12 @@ string functionTakingString(const string &str)
|
||||||
void ConversionTests::testStringBuilder()
|
void ConversionTests::testStringBuilder()
|
||||||
{
|
{
|
||||||
// conversion of string-tuple to string (the actual string builder)
|
// conversion of string-tuple to string (the actual string builder)
|
||||||
const tuple<const char *, string, const char *> tuple("string1", "string2", "string3");
|
const tuple<const char *, string, int, const char *> tuple("string1", "string2", 1234, "string3");
|
||||||
CPPUNIT_ASSERT_EQUAL(string("string1string2string3"), tupleToString(tuple));
|
CPPUNIT_ASSERT_EQUAL(string("string1string21234string3"), tupleToString(tuple));
|
||||||
CPPUNIT_ASSERT_EQUAL(string("foobarfoo2bar2"), tupleToString(string("foo") % "bar" % string("foo2") % "bar2"));
|
CPPUNIT_ASSERT_EQUAL(string("foobarfoo2bar2"), tupleToString(string("foo") % "bar" % string("foo2") % "bar2"));
|
||||||
|
|
||||||
// construction of string-tuple and final conversion to string works
|
// 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;
|
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("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");
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("regular + operator still works (no problems with ambiguity)", string("regular + still works"), string("regular") + " + still works");
|
||||||
|
|
|
@ -25,7 +25,7 @@ int main()
|
||||||
for(unsigned int r = 0; r < iterations2; ++r) {
|
for(unsigned int r = 0; r < iterations2; ++r) {
|
||||||
for(unsigned int i = 0; i < iterations; ++i) {
|
for(unsigned int i = 0; i < iterations; ++i) {
|
||||||
stringstream ss;
|
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();
|
v1.clear();
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ int main()
|
||||||
for(unsigned int r = 0; r < iterations2; ++r) {
|
for(unsigned int r = 0; r < iterations2; ++r) {
|
||||||
for(unsigned int i = 0; i < iterations; ++i) {
|
for(unsigned int i = 0; i < iterations; ++i) {
|
||||||
stringstream ss;
|
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.emplace_back(ss.str());
|
||||||
}
|
}
|
||||||
v1.clear();
|
v1.clear();
|
||||||
|
@ -48,7 +48,7 @@ int main()
|
||||||
t1 = DateTime::now();
|
t1 = DateTime::now();
|
||||||
for(unsigned int r = 0; r < iterations2; ++r) {
|
for(unsigned int r = 0; r < iterations2; ++r) {
|
||||||
for(unsigned int i = 0; i < iterations; ++i) {
|
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();
|
v2.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,31 +19,31 @@ g++ -O3 stringbuilder-bench.cpp -o stringbuilder-bench-O3 -Wl,-rpath /lib/path -
|
||||||
Results with -03:
|
Results with -03:
|
||||||
|
|
||||||
```
|
```
|
||||||
plus operator: 00:00:17
|
plus operator: 00:00:19
|
||||||
stringstream: 00:00:16
|
stringstream: 00:00:16
|
||||||
string builder: 00:00:07
|
string builder: 00:00:05
|
||||||
diff (stringstream minus string builder): 00:00:09
|
diff (stringstream minus string builder): 00:00:11
|
||||||
factor (stringstream / string builder): 2.28571
|
factor (stringstream / string builder): 3.2
|
||||||
```
|
```
|
||||||
|
|
||||||
However, with -O0 4 times *slower*:
|
However, with -O0 4 times *slower*:
|
||||||
|
|
||||||
```
|
```
|
||||||
plus operator: 00:00:29
|
plus operator: 00:00:34
|
||||||
stringstream: 00:00:16
|
stringstream: 00:00:23
|
||||||
string builder: 00:01:04
|
string builder: 00:01:02
|
||||||
diff (stringstream minus string builder): - 00:35:22
|
diff (stringstream minus string builder): - 00:35:31
|
||||||
factor (stringstream / string builder): 0.25
|
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
|
plus operator: 00:00:21
|
||||||
stringstream: 00:00:16
|
stringstream: 00:00:17
|
||||||
string builder: 00:00:11
|
string builder: 00:00:07
|
||||||
diff (stringstream minus string builder): 00:00:05
|
diff (stringstream minus string builder): 00:00:10
|
||||||
factor (stringstream / string builder): 1.45455
|
factor (stringstream / string builder): 2.42857
|
||||||
```
|
```
|
||||||
|
|
||||||
So this basic tests show that string builder is up to 2 times faster when using full optimization
|
So this basic tests show that string builder is up to 2 times faster when using full optimization
|
||||||
|
|
Loading…
Reference in New Issue