---
# System prepended metadata

title: '【C】C++ std::bind 函數綁定'

---

# 【C】C++ std::bind 函數綁定


## 簡介
在工作時使用開源專案 [**rpicam-apps**](https://github.com/raspberrypi/rpicam-apps) 來進行影像抓取，但由於我們需要將 image buffer 抓下來，需要修改原始程式碼來將資料放到 Share Memory Buffer，rpicam-apps 中有一段程式碼如下


[rpicam-apps/blob/main/apps/rpicam_vid.cpp](https://github.com/raspberrypi/rpicam-apps/blob/main/apps/rpicam_vid.cpp)
```cpp!
std::unique_ptr<Output> output = std::unique_ptr<Output>(Output::Create(options));
app.SetEncodeOutputReadyCallback(std::bind(&Output::OutputReady, output.get(), _1, _2, _3, _4));
app.SetMetadataReadyCallback(std::bind(&Output::MetadataReady, output.get(), _1));
```

首先 `App` 類別為應用層所設計的類別，負責輸出影像包含檔案、影片或串流，所以在 Terminal 下指令的時候就會依照對應的 `App` 執行檔案執行。

`SetEncodeOutputReadyCallback` 為一個 Callback 函數設定，代表著在影像 encode 完後要怎麼輸出，然而這段程式碼告訴了我們，設計者透過函數綁定方式來動態替換輸出的格式，那麼我們先了解函數綁定是甚麼


## 函數綁定
```cpp!
std::bind(函式位址 ,placeholders::_1, placeholders::_2, ..., placeholders::_N);
```

`std::bind` 可以將函數與部分參數綁定，生成一個新函數，方便後續呼叫，有點像在麥當勞點套餐，你買套餐，配餐部分就會有 A 、 B 、 C 等等給你選，所以 `std::bind` 比較像固定某個菜色，後面配菜給你選並形成一個套餐，以下是一個簡單例子

```cpp
int add_n(int x, int n) {
    return x + n;
};

int main() {
    int x = 1;
    int y = add_n(1, 2);

    std::cout << "y : " << y << "\n";
    return 0;
}
```

那如果今天我想要讓某個函數加上 2 呢 ? 我們可以透過 `std::bind` 來創建一個 `std::function` 的泛型函數容器，但多半都用 `auto` 來表達，所以呢我們將 2 先行帶入，並透過 `std::bind` 來將參數與函數綁定後，形成一個新的函數給我們使用
* `placeholders` : 為形成新的函數後要使用的參數，並依照順序填入，它告訴 `std::bind` 這個位置的參數在呼叫新函數時再傳入
	* `_1` : 對應的是新函數呼叫時的第一個參數
	* `_2` : 對應第二個參數

```cpp
using namespace std::placeholders;

int add_n(int x, int n) {
    return x + n;
};

int main() {
    int x = 1;
    auto add_2 = std::bind(add_n, _1, 2);
    // std::function<int(int)> add_2 = std::bind(add_n, _1, 2);
    int y = add_2(x);
    std::cout << "y : " << y << "\n";
    return 0;
}
```


## 重新回顧
```cpp!
std::unique_ptr<Output> output = std::unique_ptr<Output>(Output::Create(options));
app.SetEncodeOutputReadyCallback(std::bind(&Output::OutputReady, output.get(), _1, _2, _3, _4));
```

再回來看這段程式碼，我們就會知道其要被綁定函數為 `&Output::OutputReady`，但這邊要注意它是一個**成員函數指標** (pointer-to-member-function)，型態為 `Return_Type (Output::*)(Args...)`，不像一般函數指標，它需要知道「要在哪個物件上呼叫」，也就是他需要知道物件指標，所以在綁定時需要額外提供一個對應的物件指標給他，所以透過 `output.get()` 取得智慧指標所擁有的原生指標給他使用

接著我們看到 `SetEncodeOutputReadyCallback` 函數

```cpp!
typedef std::function<void(void *, size_t, int64_t, bool)> EncodeOutputReadyCallback;
void SetEncodeOutputReadyCallback(EncodeOutputReadyCallback callback) { encode_output_ready_callback_ = callback; }
```

程式碼往裡面點，越看越霧煞煞，當然我們已經知道他要綁定一個函數，那這個函數是甚麼呢 ? ， 這邊看到 `std::function`，而 `std::function` 是一個容器，這個容器裡面可以將泛型函數放入，所以被稱做**泛型函式封裝器**，接著 `<void(void *, size_t, int64_t, bool)>` 是表示這個函數回傳與參數，所以這個函數回傳是 `void` ，參數為 `void*`、 `size_t` .... 這樣的函數，如果我今天要將類別成員方法進行綁定，就會需要先綁定物件，所以要透過 `std::bind` 來達成

:::info
在 C++ 中，`std::function` 可以接收
* 普通函數指標
* 成員函數 : 但必須先綁物件，所以用 `std::bind` 或 lambda
* lambda : 有對應的 operator()
* functor : 類別重載 operator()
:::

## 總結
其實這樣函數寫法很容易讓人搞混，當初也是看不太懂，現在才開始慢慢了解，由於 Callback 函數往往要使用不同功能的函數，上面這個 Case 就是要接住一個**類別成員函數**才會造成閱讀上的困難，不然其實用函數指標就解決了