Browse Source

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.
Brad King 7 years ago
parent
commit
80802a002c
2 changed files with 202 additions and 0 deletions
  1. 101 0
      Source/cmString.hxx
  2. 101 0
      Tests/CMakeLib/testString.cxx

+ 101 - 0
Source/cmString.hxx

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

+ 101 - 0
Tests/CMakeLib/testString.cxx

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