【no more クラッシュ】thisをbindするのは危険！weak_ptrでやろう。


C++でObserverパターン（何か起こったら通知してもらうやつ）を実装する場合、関数オブジェクトとして、ハンドラを登録しておく方法を取ることが多いと思います。

そんな時、「自分のこのメソッドを呼んで！」という意味で、

`AddHandler( std::bind(&HogeClass::HogeMethod, this) );`

こんな感じで書いたりしていませんか？　でもちょっと待って！このthisは、ポインタ値としてハンドラ側に保持されます。
実際にハンドラが呼ばれる際に、有効かどうかわかりません！

こんな時はどうしたら良いのか、解説します。
まずはNGパターンから見てみましょう。

```C++
#include <iostream>
#include <vector>
#include <functional>
#include <memory>

using namespace std;

//ハンドラを呼ぶ側
class Subject {
    std::vector<std::function<void()>> m_handlers;

private:

    //ハンドラを呼ぶ
    void Notify() {
        for( auto& o : m_handlers ) {
            o();
        }
    }
    
public:
    //ハンドラを登録
    void AddHandler(std::function<void()> handler) {
        m_handlers.push_back(handler);
    }
    
    void Update() {
        //if( hoge ) {    //何かのイベントが発生したら、Notifyする
            Notify();
        //}
    }
};


//ハンドラ呼ばれる側
class Observer : public std::enable_shared_from_this<Observer>
{
public:
    Observer() {
    }
    virtual ~Observer() {
        cout << "destructed" << endl;
    }
    void Setup( Subject& sbj ) {
        sbj.AddHandler(std::bind(&Observer::OnNotify1, this));

    }
    void OnNotify1() {
        cout << "notified" << endl;
    }
};

int main() {
    Subject sbj;
    {
        //Observerインスタンスは実際は別の場所で管理されている想定
        auto obs = std::make_shared<Observer>();
        obs->Setup(sbj);
        sbj.Update();
    }
    
    //obsが破棄された後にUpdateが呼ばれた！
    sbj.Update();
}
```
https://repl.it/@mas_yo/bind-weak-pointer-1

repl.itのリンク先で、実行してみてください。
Observerインスタンスが破棄された後に、ハンドラが呼ばれてしまいましたね。運が良ければ、問題なく動作しますが、運が悪いと、Segmentation Faultになるでしょう。
謎の強制終了バグに頭を悩ませる未来が想像できます。

C++11からは、原則生ポインタを使うのは避けるべきです。スマートポインタを使いましょう。

ここでは、Observerのインスタンスで、shared_ptrで管理されているとします。そして、ハンドラは weak_ptr 経由で呼ぶようにします。

なぜshared_ptrでなくweak_ptrを使うかといと、shared_ptrを使うと、Observerインスタンスがハンドラ側から参照されつづけ、メモリリークになってしまうためです。

さて、weak_ptr経由でメソッドを呼ぶ関数を考えます。

```C++
void CallFromWeak(std::weak_ptr<HogeClass> w)
{
    std::shared_ptr<HogeClass> s = w.lock();
    if( s ) { //ポインタが有効か？
        s->HogeMethod();
    }
}
```

こうすれば、ポインタが無効の時は、HogeMethodが呼ばれません。
ではこのCallFromWeak関数を、ハンドラに登録すればいいんですね！

・・・それでもいいのですが、このCallFromWeakは、HogeClassのHogeMethod限定になっています。別のクラスや別のメソッドを登録したいと思ったら、また似たような CallFromWeak を作らねばなりません。

さてどうしたものか・・・汎用的なCallFromWeakが欲しい。
そうだ、テンプレートにしよう。

```C++
//メンバー関数型
template<typename T, typename... Args>
using FuncType = void (T::*)(Args...);

//weak_ptr経由で呼ぶ
template<typename T, typename... Args>
static void CallFromWeak( std::weak_ptr<T> weak, FuncType<T, Args...> func, Args... args ) {
    std::shared_ptr<T> shared = weak.lock();
    if( shared ) {
        std::mem_fun(func)( shared.get(), args... );
    }
}
```

これを使って、最初のNGプログラムを修正してみます。


```C++

.......省略

//ハンドラ呼ばれる側
class Observer : public std::enable_shared_from_this<Observer>
{
public:
    Observer() {
    }
    virtual ~Observer() {
        cout << "destructed" << endl;
    }
    void Setup( Subject& sbj ) {
        auto shared = shared_from_this();
        std::weak_ptr<Observer> weak = shared;
        
        //いろんなメソッドが登録できる！
        sbj.AddHandler(std::bind(&CallFromWeak<Observer>, weak, &Observer::OnNotify1));
        sbj.AddHandler(std::bind(&CallFromWeak<Observer,int>, weak, &Observer::OnNotify2, 1));
    }
    void OnNotify1() {
        cout << "notified" << endl;
    }
    void OnNotify2(int i) {
        cout << "notified " << i << endl;
    }
};

int main() {
    Subject sbj;
    {
        auto obs = std::make_shared<Observer>();
        obs->Setup(sbj);
        sbj.Update();
    }
    sbj.Update();    
}
```
https://repl.it/@mas_yo/bind-weak-pointer

これで、安全にハンドラを呼び出せますね！