# StrCat in Chromium StrCatは複数のstringを合体するメソッドの通称。 いろんなところで実装されているので確認する。 ## cstring c++ ではcstringライブラリの中で定義されている。 ```cpp= char *strcat( char *dest, const char *src ); ``` これは1つ目の引数に渡された`dest`に`src`の内容をappendして返す。`dest`を上書きする。 このメソッドはChromiumでは使われていない。 おそらく副作用のあるタイプの関数を嫌っているから。 ## absl:StrCat [absl::StrCat](https://abseil.io/docs/cpp/guides/strings#abslstrcat-and-abslstrappend-for-string-concatenation) はabseilライブラリ版のStrCat。 文字列の足し算はかなり長いことがよくあるのでパフォーマンスに気をつけないといけない。 雑にcopyとかが入ると良くない。 例えば以下の例を見てみると ```cpp= std::string s1 = "A string"; s1 = s1 + " another string"; ``` `s1`をtemp stringのコピーし、temp stringと" another string"をくっつけて`s1`にアサインする。 それより `s1+= " another string"` とするとコピーがない。 (コンパイラ最適化でこの程度のコードなら解決してくれそう) こういう感じのことを防ぐためにabsl::StrCatやabsl::StrStrAppendという賢くstringの足し算をするメソッドを提供している。 absl::StrCatは任意の数のstringを足せる。 ```cpp= std::string s1; s1 = absl::StrCat("A string ", " another string", "yet another string"); ``` またstd::string, absl::string_viewなどいろんな型をサポートしている。 ただし、これもChromiumではbanされている。 [PRESUBMIT.py](https://source.chromium.org/chromium/chromium/src/+/main:PRESUBMIT.py;l=998-1005;drc=431239a32d113c52b1a0e4ca5e752bb76f55142c): ```python= BanRule( r'/\babsl::(StrSplit|StrJoin|StrCat|StrAppend|Substitute|StrContains)\b', ( 'Abseil string utilities are banned. Use base/strings instead.', ), True, [_THIRD_PARTY_EXCEPT_BLINK], # Not an error in third_party folders. ), ``` ## base::StrCat base/stringsの中で定義される[base::StrCat](https://source.chromium.org/chromium/chromium/src/+/main:base/strings/strcat.h)。 これがChromiumで標準に使われているStrCat. std::string版とstd::u16string版が用意されている。引数はspanなので複数のstringを渡せる。 ```cpp= BASE_EXPORT std::string StrCat(span<const StringPiece> pieces); ``` またconst StringPieceとして渡すので、副作用はない。返り値として結果を返すので、コピーのコストはかかる。 内部実装は[internal::StrAppendT](https://source.chromium.org/chromium/chromium/src/+/main:base/strings/strcat_internal.h;l=44;drc=641d3b47c0dcd3fc1231ebf0b31722e0a9299b1d)にある。 ```cpp= const size_t initial_size = dest.size(); size_t total_size = initial_size; for (const auto& cur : pieces) total_size += cur.size(); Resize(dest, total_size, priority_tag<1>()); CharT* dest_char = &dest[initial_size]; for (const auto& cur : pieces) { std::char_traits<CharT>::copy(dest_char, cur.data(), cur.size()); dest_char += cur.size(); } ``` 実装は、まず`total_size`を計算する。 Resizeは[base_string::resize](https://cpprefjp.github.io/reference/string/basic_string/resize.html)。 ただ長さを変更するだけ。 その後、`dest`の`initial_size`番目のところからappend先がくっつくので、ここに`pieces`の各要素をcopyする。 特にパフォーマンスがめっちゃいい工夫とかは見られなかった。 abslよりこっちを使えというのは - initializer list というよりシンプルなメソッドで書ける - abslは数字をそのまま取るが、baseではstatic bufferにフォーマットすることでパフォーマンスが改善されている - abslのhelper classのほうがちょっとコードサイズがデカイ。 ### Note 以下のような場合、StrAppendの方が推奨されているとのこと。 ```cpp= StrAppend(&foo, ...); foo += StrCat(...); ``` temp stringのアロケーション・コピーを防げるため。