スコープが終わった際に処理をしたい

QMutexLocker, QReadLocker, QWriteLocker のようなクラスは、スコープ内でインスタンスを作っておけば、break や return などでそのスコープを抜けた際に自動で処理をしてくれますが、同じようなもう少し複雑なようなことをしたい場合がたまにありますよね。

特に、C のライブラリなんかを使っているとエラー時のリソースの開放周りでそういうパターンが多いと思います。

abc a = 0;
abc_context c = 0;
abc_data d = 0;

a = abc_init();
if (a < 0)
    return;

c = abc_create_context(a);
if (c < 0) {
    abc_cleanup(a);
    return;
}

d = abc_create_data(c);
if (d < 0) {
    abc_destroy_context(c);
    abc_cleanup(a);
    return;
}
...
abc_destroy_data(d);
abc_destroy_context(c);
abc_cleanup(a);

イマドキ goto cleanup; なんてのもダサいので地道に書くしかないかなと思っていましたが、Qt 5.12 で導入された qScopeGuard を利用するとそんなコードが綺麗に書けるようになります。

#include <QtCore/QScopeGuard>

abc a = 0;
abc_context c = 0;
abc_data d = 0;

auto cleanup = qScopeGuard([&] {
    abc_destroy_data(d);
    abc_destroy_context(c);
    abc_cleanup(a);
});

a = abc_init();
if (a < 0)
    return;

c = abc_create_context(a);
if (c < 0)
    return;

d = abc_create_data(c);
if (d < 0)
    return;

...

もしくは

#include <QtCore/QScopeGuard>

abc a = abc_init();
if (a < 0)
    return;
auto cleanup_a = qScopeGuard([&] {
    abc_cleanup(a);
});

abc_context c = abc_create_context(a);
if (c < 0)
    return;
auto cleanup_c = qScopeGuard([&] {
    abc_destroy_context(c);
});

abc_data d = abc_create_data(c);
if (d < 0)
    return;
auto cleanup_d = qScopeGuard([&] {
    abc_destroy_data(d);
});

...

ということで、

auto cleanup = qScopeGuard([&] {
    // スコープの終了時の処理をここに書く
});

という形でお使いください。便利!

あわせて読みたい