String: Add support for concatenation by operator+

Use expression templates to collect the entire expression and
pre-allocate a string with the final length before concatenating
the pieces.
This commit is contained in:
Brad King
2018-10-01 15:45:02 -04:00
parent ff69763ca0
commit 80802a002c
2 changed files with 202 additions and 0 deletions

View File

@@ -662,6 +662,107 @@ operator>=(L&& l, R&& r)
std::ostream& operator<<(std::ostream& os, String const& s);
std::string& operator+=(std::string& self, String const& s);
template <typename L, typename R>
struct StringOpPlus
{
L l;
R r;
#if defined(__SUNPRO_CC)
StringOpPlus(L in_l, R in_r)
: l(in_l)
, r(in_r)
{
}
#endif
operator std::string() const;
std::string::size_type size() const { return l.size() + r.size(); }
};
template <typename T>
struct StringAdd
{
static const bool value = AsStringView<T>::value;
typedef string_view temp_type;
template <typename S>
static temp_type temp(S&& s)
{
return AsStringView<T>::view(std::forward<S>(s));
}
};
template <typename L, typename R>
struct StringAdd<StringOpPlus<L, R>> : std::true_type
{
typedef StringOpPlus<L, R> const& temp_type;
static temp_type temp(temp_type s) { return s; }
};
template <typename L, typename R>
StringOpPlus<L, R>::operator std::string() const
{
std::string s;
s.reserve(size());
s += *this;
return s;
}
template <typename L, typename R>
std::string& operator+=(std::string& s, StringOpPlus<L, R> const& a)
{
s.reserve(s.size() + a.size());
s += a.l;
s += a.r;
return s;
}
template <typename L, typename R>
String& operator+=(String& s, StringOpPlus<L, R> const& a)
{
std::string r;
r.reserve(s.size() + a.size());
r.assign(s.data(), s.size());
r += a.l;
r += a.r;
s = std::move(r);
return s;
}
template <typename L, typename R>
std::ostream& operator<<(std::ostream& os, StringOpPlus<L, R> const& a)
{
return os << a.l << a.r;
}
template <typename L, typename R>
struct IntoString<StringOpPlus<L, R>> : std::true_type
{
static std::string into_string(StringOpPlus<L, R> const& a) { return a; }
};
template <typename L, typename R>
typename std::enable_if<StringAdd<L>::value && StringAdd<R>::value,
StringOpPlus<typename StringAdd<L>::temp_type,
typename StringAdd<R>::temp_type>>::type
operator+(L&& l, R&& r)
{
return { StringAdd<L>::temp(std::forward<L>(l)),
StringAdd<R>::temp(std::forward<R>(r)) };
}
template <typename LL, typename LR, typename R>
typename std::enable_if<AsStringView<R>::value, bool>::type operator==(
StringOpPlus<LL, LR> const& l, R&& r)
{
return std::string(l) == AsStringView<R>::view(std::forward<R>(r));
}
template <typename L, typename RL, typename RR>
typename std::enable_if<AsStringView<L>::value, bool>::type operator==(
L&& l, StringOpPlus<RL, RR> const& r)
{
return AsStringView<L>::view(std::forward<L>(l)) == std::string(r);
}
} // namespace cm
namespace std {

View File

@@ -11,6 +11,7 @@
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#define ASSERT_TRUE(x) \
@@ -942,6 +943,103 @@ static bool testMethod_find_last_not_of()
return true;
}
static bool testAddition()
{
std::cout << "testAddition()\n";
{
ASSERT_TRUE(cm::String("a") + "b" == "ab");
ASSERT_TRUE("ab" == "a" + cm::String("b"));
ASSERT_TRUE("a" + cm::String("b") + "c" == "abc");
ASSERT_TRUE("abc" == "a" + cm::String("b") + "c");
ASSERT_TRUE("a" + (cm::String("b") + "c") + "d" == "abcd");
ASSERT_TRUE("abcd" == "a" + (cm::String("b") + "c") + "d");
}
{
const char* a = "a";
const char* b = "b";
const char* ab = "ab";
ASSERT_TRUE(cm::String(a) + b == ab);
ASSERT_TRUE(ab == a + cm::String(b));
const char* c = "c";
const char* abc = "abc";
ASSERT_TRUE(a + cm::String(b) + c == abc);
ASSERT_TRUE(abc == a + cm::String(b) + c);
const char* d = "d";
const char* abcd = "abcd";
ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd);
ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d);
}
{
ASSERT_TRUE(cm::String('a') + 'b' == "ab");
ASSERT_TRUE("ab" == 'a' + cm::String('b'));
ASSERT_TRUE('a' + cm::String('b') + 'c' == "abc");
ASSERT_TRUE("abc" == 'a' + cm::String('b') + 'c');
ASSERT_TRUE('a' + (cm::String('b') + 'c') + 'd' == "abcd");
ASSERT_TRUE("abcd" == 'a' + (cm::String('b') + 'c') + 'd');
}
{
std::string a = "a";
std::string b = "b";
std::string ab = "ab";
ASSERT_TRUE(cm::String(a) + b == ab);
ASSERT_TRUE(ab == a + cm::String(b));
std::string c = "c";
std::string abc = "abc";
ASSERT_TRUE(a + cm::String(b) + c == abc);
ASSERT_TRUE(abc == a + cm::String(b) + c);
std::string d = "d";
std::string abcd = "abcd";
ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd);
ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d);
}
{
cm::string_view a("a", 1);
cm::string_view b("b", 1);
cm::string_view ab("ab", 2);
ASSERT_TRUE(cm::String(a) + b == ab);
ASSERT_TRUE(ab == a + cm::String(b));
cm::string_view c("c", 1);
cm::string_view abc("abc", 3);
ASSERT_TRUE(a + cm::String(b) + c == abc);
ASSERT_TRUE(abc == a + cm::String(b) + c);
cm::string_view d("d", 1);
cm::string_view abcd("abcd", 4);
ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd);
ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d);
}
{
cm::String a = "a";
cm::String b = "b";
cm::String ab = "ab";
ASSERT_TRUE(a + b == ab);
ASSERT_TRUE(ab == a + b);
cm::String c = "c";
cm::String abc = "abc";
ASSERT_TRUE(a + cm::String(b) + c == abc);
ASSERT_TRUE(abc == a + cm::String(b) + c);
cm::String d = "d";
cm::String abcd = "abcd";
ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd);
ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d);
}
{
cm::String str;
str += "a" + cm::String("b") + 'c';
ASSERT_TRUE(str == "abc");
}
{
std::string s;
s += "a" + cm::String("b") + 'c';
ASSERT_TRUE(s == "abc");
}
{
std::ostringstream ss;
ss << ("a" + cm::String("b") + 'c');
ASSERT_TRUE(ss.str() == "abc");
}
return true;
}
int testString(int /*unused*/, char* /*unused*/ [])
{
if (!testConstructDefault()) {
@@ -1106,5 +1204,8 @@ int testString(int /*unused*/, char* /*unused*/ [])
if (!testMethod_find_last_not_of()) {
return 1;
}
if (!testAddition()) {
return 1;
}
return 0;
}