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
|
||||
#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)));
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue