# fmt vs sprintf vs stringstream 速度比較

## 目標

**粗略**的比較這些字串拼接函數

*   `fmt::format`: 並不是 `c++20` 的 std::format ，是在 ubuntu20.04 的 apt repo 裡面可以直接安裝的 libfmt-dev: 6.1.2
*   `std::sprintf`: c 本來就有的函數
*   `std::stringstream`: c++ 的另一種字串拼接方式

由於我是在嘗試一個把混有字串和數字的 tuple 轉換成 string 的方式， example:

```cpp
struct AreaData {
    std::string stateName;
    int typeID;
};
```

我希望能夠有「`toString(a1) == toString(a2)` => `a1 == a2`」，所以我會用這種方式把 string 拼進去：

    {str.length}\t{str.data}\t

這樣就做到那件事。

而測試時也只會測試把一個字串和一個數字披在一起：

    {stateName.size}\t{stateName.data}\t{typeID}\t

## Code

### `fmt::format`

這不需要什麼特別注意的地方，不過為了安全，我在拼接 string 時是用指定 (data, size) 的方式：`{:.{}}`

```cpp
fmt::format("{}\t{:.{}}\t{}\t", strv1.length(), strv1.data(), strv1.length(), intv);
```

### Sprintf

本來的 sprintf 並不安全，因為他是吃一個指標，並且沒有長度的檢查。所以要用 snprintf

*   [https://stackoverflow.com/questions/2342162/stdstring-formatting-like-sprintf](https://stackoverflow.com/questions/2342162/stdstring-formatting-like-sprintf)

```cpp
template<typename ... Args>
std::string string_format( const std::string& format, Args ... args )
{
    int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
    if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
    auto size = static_cast<size_t>( size_s );
    std::unique_ptr<char[]> buf( new char[ size ] );
    std::snprintf( buf.get(), size, format.c_str(), args ... );
    return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
```

然後根據 [Using printf with a non-null terminated string](https://stackoverflow.com/questions/3767284/using-printf-with-a-non-null-terminated-string) 做出的 format string:

```cpp
string_format("%d\t%.*s\t%d\t", strv.length(), strv.length(), strv.data(), intv);
```

### `std::stringstream`

```cpp
std::stringstream ss;
ss << strv1.length() << "\t";
ss.write(strv1.data(), strv1.length()) << "\t";
ss << intv << "\t";
auto val = ss.str();
```

## 結果與結論


在 ubuntu 20.04 環境，每個都跑 10^8 次數下的結果：

*   `fmt::format`: 15 秒
*   `sprintf`: 39 秒
*   `std::stringstream`: 60 秒 

節論： `fmt::format` 是個不錯的選擇，不過假如**在不需要考慮速度，並且也不太希望增加依賴**的情況其實也沒有很必要？
