# ScopedClosureRunner in Chromium [ScopedClosureRunner](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/callback_helpers.h;l=166;drc=9e8eb208d7640a9376015fb1a1bb0d377be77d6c)というやつについてreviewでコメントをもらったので、こいつが誰か確認する。 ## Overview 名前から予想がつく通りScopeを抜けるとRunがtriggerされるヘルパークラス。 ```cpp= { base::ScopedClosureRunner deferred_runner; if (on_camera_list_received_for_test_) { deferred_runner.ReplaceClosure( std::move(on_camera_list_received_for_test_)); } ManyManyThings(); } // deferred_runner.Run() runs ``` ここで走るClosureは[base::OnceClosure](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/callback_forward.h;l=19;drc=b5fa1427d7ab0f37a5ea6f61fae9eec9fbda460d)型、すなわち何も返さないやつ `OnceCallback<void()>` に限られる。 スコープを抜けるときに走って捨てられる関数なので、戻り値があっても受け取れないからだと思う。 走らせたいClosureは上の例のように[ReplaceClosure](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/callback_helpers.h;l=184;drc=9e8eb208d7640a9376015fb1a1bb0d377be77d6c)ですでにできているScopedClosureRunnerにセットするか、以下のようにconstructorで指定するかのどちらか。 ```cpp= base::ScopedClosureRunner deferred_cursor_updater(base::BindOnce( &CaptureModeSession::UpdateCursor, weak_ptr_factory_.GetWeakPtr(), screen_location, is_touch)); ``` スコープを抜ける際にRunする他、[RunAndReset](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/callback_helpers.h;l=181;drc=9e8eb208d7640a9376015fb1a1bb0d377be77d6c)で明示的に走らせてリセットすることもでき、また呼ばずにリセットだけする[Release](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/callback_helpers.h;l=187;drc=9e8eb208d7640a9376015fb1a1bb0d377be77d6c)もある。 ## 実装 [`closure_`](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/callback_helpers.h;l=190;drc=9e8eb208d7640a9376015fb1a1bb0d377be77d6c) に走らせたいClosureが登録されている。 destructorの中でRunするので、スコープを抜けると走る仕組み。 ```cpp= ScopedClosureRunner::~ScopedClosureRunner() { RunAndReset(); } void ScopedClosureRunner::RunAndReset() { if (closure_) std::move(closure_).Run(); } ``` めちゃくちゃよく見るScopeデザインだった。 1つ非自明な挙動をしていたのがmove assign operator ```cpp= ScopedClosureRunner& ScopedClosureRunner::operator=( ScopedClosureRunner&& other) { if (this != &other) { RunAndReset(); ReplaceClosure(other.Release()); } return *this; } ``` move assignされたとき、代入前のRunnerが異なっていた場合RunAndResetが走ってからReplaceClosureが呼ばれる。 つまり、moveする前に昔のCallbackは一旦走っている。 これはuqnieu_ptrがmove assign operatorで保持しているポインタを捨てるやり方に類似させているらしい。 もし昔のCallbackを走らせずにただ再代入だけしたい場合は、[ReplaceClosure](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/callback_helpers.h;l=184;drc=9e8eb208d7640a9376015fb1a1bb0d377be77d6c)を使えとのこと。 ## Note コードを書く上で同じ処理は関数に切り出して呼ぶと読みやすいしコードの機動力が上がる。 [callback_helpers.cc](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/callback_helpers.cc)ではそれがちゃんとされていていい。 例えば上記の move assign op の実装について、 ```cpp= std::move(callback).Run(); closure_ = std::move(closure); ``` と書かずにちゃんと関数を呼んでいて偉い。 これをしていると、例えばCHECKを追加したくなったときにかんたんに全域に反映できるようにかける。 関数呼び出しが無駄に増えるとパフォーマンスが落ちるという話もあるが、最近のコンパイラは賢くこの程度ならいい感じに省略してくれそう <- 本当に?