# 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のアロケーション・コピーを防げるため。