ScopeGuard:Exception-Safeコードの作成
14409 ワード
C++プログラムを開発する際に異常処理コードを記述することがしばしば必要であり、異常処理メカニズムの利点は、低層のエラーを呼び出しスタックに沿ってキャプチャされ処理されるまで伝達できることであり、try/catchコードブロックの参照によりコード構造が複雑になり、処理フローが不明確になり、コードのメンテナンスコストも増加するという欠点がある.Generic<Programming>: Change the Way You Write Exception-Safe Code Foreverはtry/catchの束縛から脱し、コードの読み取りをより簡潔で優雅にする巧みな方法を紹介した.
本文は原文の中の解決方法を余計に述べないで、重点的にその実現メカニズムを紹介します.
上記のコードから、ベースクラスScopeGuardImplBaseは、仮想解析関数によってマルチステートを実現するのではなく、ScopeGuardをScopeGuardImplBaseの一般的な参照タイプとして定義することによって、C++標準:一時変数を使用して参照を初期化し、参照のライフサイクルが終了するまで変数が存在することをテクニックとしています.
次の例で説明します.
MakeGuard関数は、「ScopeGuardImpl 1」というタイプの一時変数を生成し、通常の参照closeItに値を割り当てます.
closeItライフサイクルが終了すると、実際のタイプの構造関数が実行され、std::fclose(topSecret)が呼び出されます.
上記のコードでは、1つのパラメータを受け入れるScopeGuardImpl 1オブジェクトタイプのみが提供されており、0個、2個以上のパラメータを受け入れる派生タイプ(ScopeGuardImpl 0、ScopeGuardImpl 2...)は、ひょうたんであれば容易に実現できる.また、異なるパラメータ個数実装バージョンをサポートするためにhelper関数MakeGuardを再ロードする必要があります.
本文では、オブジェクトメンバー関数を処理関数として使用するバージョンも提供され、実装原理は上記の一般的な関数バージョンと一致している.
bootバージョン
boostに詳しい同級生はboost::shared_ptrとboost::bindはこのScopeGuardツールを実現することができます.詳細は:Using shared_ptr to execute code on block exitを参照してください.
本文は原文の中の解決方法を余計に述べないで、重点的にその実現メカニズムを紹介します.
1
/*
*
2
* ScopeGuardImplBase dismissed_ ,dismissed_
3
*/
4
class
ScopeGuardImplBase {
5
public
:
6
void
Dismiss()
const
throw
() {
7
dismissed_
=
true
;
8
}
9
10
protected
:
11
ScopeGuardImplBase() : dismissed_(
false
) {}
12
13
ScopeGuardImplBase(
const
ScopeGuardImplBase
&
other) : dismissed_(other.dismissed_) {
14
other.Dismiss();
15
}
16
17
//
18
~
ScopeGuardImplBase() {}
19
20
mutable
bool
dismissed_;
21
22
private
:
23
//
24
ScopeGuardImplBase
&
operator
=
(
const
ScopeGuardImplBase
&
);
25
};
26
27
28
/*
*
29
* ScopeGuardImpl1 1
30
*/
31
template
<
typename Fun, typename Parm
>
32
class
ScopeGuardImpl1 :
public
ScopeGuardImplBase {
33
public
:
34
ScopeGuardImpl1(
const
Fun
&
fun,
const
Parm
&
parm)
35
: fun_(fun), parm_(parm)
36
{}
37
38
//
39
~
ScopeGuardImpl1() {
40
if
(
!
dismissed_) fun_(parm_);
41
}
42
43
private
:
44
Fun fun_;
45
const
Parm parm_;
46
};
47
48
/*
*
49
* ScopeGuardImpl1 helper
50
*/
51
template
<
typename Fun, typename Parm
>
52
ScopeGuardImpl1
<
Fun, Parm
>
MakeGuard(
const
Fun
&
fun,
const
Parm
&
parm) {
53
return
ScopeGuardImpl1
<
Fun, Parm
>
(fun, parm);
54
}
55
56
/*
*
57
* ScopeGuard ScopeGuardImplBase
58
*/
59
typedef
const
ScopeGuardImplBase
&
ScopeGuard;
上記のコードから、ベースクラスScopeGuardImplBaseは、仮想解析関数によってマルチステートを実現するのではなく、ScopeGuardをScopeGuardImplBaseの一般的な参照タイプとして定義することによって、C++標準:一時変数を使用して参照を初期化し、参照のライフサイクルが終了するまで変数が存在することをテクニックとしています.
次の例で説明します.
FILE
*
topSecret
=
std::fopen(
"
cia.txt
"
);
ScopeGuard closeIt
=
MakeGuard(std::fclose, topSecret);
MakeGuard関数は、「ScopeGuardImpl 1
closeItライフサイクルが終了すると、実際のタイプの構造関数が実行され、std::fclose(topSecret)が呼び出されます.
上記のコードでは、1つのパラメータを受け入れるScopeGuardImpl 1オブジェクトタイプのみが提供されており、0個、2個以上のパラメータを受け入れる派生タイプ(ScopeGuardImpl 0、ScopeGuardImpl 2...)は、ひょうたんであれば容易に実現できる.また、異なるパラメータ個数実装バージョンをサポートするためにhelper関数MakeGuardを再ロードする必要があります.
本文では、オブジェクトメンバー関数を処理関数として使用するバージョンも提供され、実装原理は上記の一般的な関数バージョンと一致している.
bootバージョン
boostに詳しい同級生はboost::shared_ptrとboost::bindはこのScopeGuardツールを実現することができます.詳細は:Using shared_ptr to execute code on block exitを参照してください.