https://chromium.googlesource.com/chromium/src/+/master/docs/callback.md
イントロ
テンプレートのCallback<>クラスは一般化された関数オブジェクトである。Bind()と一緒にタイプセーフなメソッドを提供し、関数の部分的適用を実演する。(performing partial application of functions)
部分的適用(またはカリー化)は、関数の引数のサブセットを束縛するプロセスで、より引数の少ない関数を作成する。これにより遅延実行のユニットを持ちまわることができる、多言語のレキシカルクロージャようなもの。Chromiumでは異なるMessageLoopにおいてタスクをスケジュールするのにつかわれる。
拘束なしの入力パラメタコールバック(Callback<void()>)はクロージャと呼ばれる。これは多言語で言うところのクロージャと同じではない。囲っている環境の参照を保持しない。
OnceCallback<>とRepeatingCallback<>
OnceCallback<>とRepeatingCallback<>は現在開発中の次世代コールバッククラスである。
OnceCallback<>はBindOnce()により作成される。これはコールバックの一種で、ムーブオンリータイプあり、一度しか実行されない。これは内部ストレージから拘束パラメータを拘束関数へムーブする、ムーブ可能なタイプで使いやすい。これは好ましいコールバックタイプである。ライフタイムが鮮明になり、スレッド間で渡されるコールバックがいつ破壊されたのか理解しやすくする。
RepeatingCallback()はBindRepeating()で作成される。これはコールバックの一種で、コピー可能で何度でも実行できる。内部の参照カウンタを使うのでコピーは軽い。しかし、所有は共有されるので、コールバックや拘束状態がいつ破壊されのか推論しにくい、特にスレッド間で渡されるときにはそうだ。
レガシーなCallback<>は現在ではRepeatingCallback<>にエリアスされている。新規コードでは可能な限りOnceCallback<>を用いること。移行完了後はエリアスは消去され、OnceCallback<>がCallback<>にリネームされる。これが好ましいことを強調するため。
RepeatingCallback<>はOnceCallback<>に暗黙変換が可能。
メモリ管理とデータ渡し
所有権が移転されるなら値渡しでCallbackオブジェクトを渡す、そうでないならコンスト参照で渡す。
// |Foo| just refers to |cb| but doesn't store it nor consume it. bool Foo(const OnceCallback<void(int)>& cb) { return cb.is_null(); } // |Bar| takes the ownership of |cb| and stores |cb| into |g_cb|. OnceCallback<void(int)> g_cb; void Bar(OnceCallback<void(int)> cb) { g_cb = std::move(cb); } // |Baz| takes the ownership of |cb| and consumes |cb| by Run(). void Baz(OnceCallback<void(int)> cb) { std::move(cb).Run(42); } // |Qux| takes the ownership of |cb| and transfers ownership to PostTask(), // which also takes the ownership of |cb|. void Qux(OnceCallback<void(int)> cb) { PostTask(FROM_HERE, base::BindOnce(std::move(cb), 42)); }
Callbackオブジェクトを関数パラメータに渡すとき、参照を保持する必要がないならstd::move()を使う。そうでないなら直接渡す。関数が排他的所有権を要求するならムーブで渡さないとコンパイルエラーになる。ムーブされたCallbackはnullになることに注意すること。あたかもReset()メソッドが呼ばれたようになり、is_null()はtrueを返す。
基本事項のクイックリファレンス
生の関数を拘束
int Return5() { return 5; } OnceCallback<int()> func_cb = BindOnce(&Return5); LOG(INFO) << std::move(func_cb).Run(); // Prints 5.
int Return5() { return 5; } RepeatingCallback<int()> func_cb = BindRepeating(&Return5); LOG(INFO) << func_cb.Run(); // Prints 5.
キャプチャなしのラムダを拘束
Callback<int()> lambda_cb = Bind([] { return 4; }); LOG(INFO) << lambda_cb.Run(); // Print 4. OnceCallback<int()> lambda_cb2 = BindOnce([] { return 3; }); LOG(INFO) << std::move(lambda_cb2).Run(); // Print 3.
クラスメソッドを拘束
bindへの最初の引数は呼び出すメンバ関数、二番目は呼び出すオブジェクト
class Ref : public RefCountedThreadSafe<Ref> { public: int Foo() { return 3; } }; scoped_refptr<Ref> ref = new Ref(); Callback<void()> ref_cb = Bind(&Ref::Foo, ref); LOG(INFO) << ref_cb.Run(); // Prints out 3.
デフォルトではオブジェクトはRefCountedをサポートしたいとコンパイルエラーになる。スレッド間で渡すなら、RefCountedThreadSafeを使うこと!参照カウンタを使いたくないなら下の「メンバ関数拘束の詳細」を見ること。
コールバックの実行
TODO