Chromiumにおける型安全な関数オブジェクトと部分適用の仕組み

base::Callback<> および base::Bind() は、Chromium の基盤ライブラリで提供される型安全な汎用関数オブジェクト機構です。これらを組み合わせることで、関数の一部引数を事前に束縛し、残りの引数を受け取る新たな関数を生成する「部分適用」が可能になります。これは遅延実行の単位として利用され、他の言語におけるクロージャに近い役割を果たします。

一回限り実行型と再利用可能型

base::OnceCallback<>base::BindOnce() で生成され、移動専用で一度しか実行できません。束縛された引数は内部から関数へと移動されるため、ムーブ可能な型との相性が良好です。スレッド間で渡す場合でも、所有権の明確さからライフタイム管理が容易です。

base::RepeatingCallback<>base::BindRepeating() で生成され、複数回実行可能なコピー可能な型です。内部で参照カウントを使用してコピーを効率化しますが、所有権が共有されるため破棄タイミングの推論が難しくなります。新規コードでは可能な限り OnceCallback を優先し、必要に応じて RepeatingCallback を使用してください。

所有権と引数の渡し方

コールバックの所有権を転送する場合は値渡し、参照のみ必要な場合は const 参照で受け取ります。

bool CheckIfValid(const base::OnceCallback<void(int)>& cb) {
  return !cb.is_null();
}

base::OnceCallback<void(int)> global_cb;
void StoreCallback(base::OnceCallback<void(int)> cb) {
  global_cb = std::move(cb);
}

void ExecuteAndConsume(base::OnceCallback<void(int)> cb) {
  std::move(cb).Run(99);
}

基本的な使用例

通常関数の束縛

int GetMagicNumber() { return 42; }
auto task = base::BindOnce(&GetMagicNumber);
LOG(INFO) << std::move(task).Run(); // 出力: 42

ラムダ式の束縛

auto lambda_task = base::BindOnce([]{ return "Hello"; });
LOG(INFO) << std::move(lambda_task).Run(); // 出力: Hello

メンバ関数の束縛

class DataProcessor : public base::RefCountedThreadSafe<DataProcessor> {
 public:
  std::string Process() { return "processed"; }
};
scoped_refptr<DataProcessor> processor = new DataProcessor();
auto member_task = base::BindRepeating(&DataProcessor::Process, processor);
LOG(INFO) << member_task.Run(); // 出力: processed

実行時の注意点

OnceCallback は実行時に必ず std::move() で消費されます。実行後は無効(is_null() が true)になります。

void Trigger(base::OnceCallback<void(std::string)> action) {
  std::move(action).Run("event");
}

何もしないコールバック

テストやダミー処理には base::DoNothing() を使用します。デフォルト構築したコールバックは null であり、実行するとクラッシュします。

using VoidCallback = base::OnceCallback<void()>;
VoidCallback dummy = base::DoNothing();
std::move(dummy).Run(); // 安全に何もしない

引数の束縛と部分適用

引数は束縛時または実行時に指定可能です。部分適用により、特定の引数を固定した関数を生成できます。

void LogMessage(const std::string& prefix, int value) {
  LOG(INFO) << prefix << value;
}

base::OnceCallback<void(int)> logger = base::BindOnce(&LogMessage, "Result: ");
std::move(logger).Run(100); // 出力: Result: 100

所有権管理の高度なテクニック

弱参照による自動キャンセル

base::BindRepeating(&MyClass::Update, weak_factory_.GetWeakPtr());
// オブジェクト破棄時は自動的に無効化(ただしスレッド非安全)

所有権の明示的放棄

base::BindOnce(&MyClass::Cleanup, base::Unretained(this));
// this の生存を保証する責任は開発者に

コールバックによるオブジェクト所有

auto obj = std::make_unique<Resource>();
auto cleanup = base::BindOnce(&Resource::Release, std::move(obj));
// cleanup 実行時に obj が解放される

参照渡しと特殊ラッパー

通常、引数はコピーされますが、base::ConstRef() を使うと参照を保持します。ただし、参照元の生存期間に注意が必要です。

void PrintValue(const int& val) { LOG(INFO) << val; }

int counter = 5;
auto print_copy = base::BindRepeating(&PrintValue, counter);
auto print_ref = base::BindRepeating(&PrintValue, base::ConstRef(counter));

counter = 10;
print_copy.Run(); // 出力: 5(コピー)
print_ref.Run();  // 出力: 10(参照)

タグ: Chromium base::Callback base::Bind C++11 部分適用

6月1日 04:23 投稿