コールバックとバインド

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

Page last modified on July 07, 2018, at 04:16 PM
Powered by PmWiki