2013-07-18 8 views
11

典型的な例:例外が発生しやすいコードでva_listを使用することは安全ですか?

void foo(const char *fmt, ...) 
{ 
    va_list args; 
    va_start(args, fmt); 

    // might throw, might not. who knows. 
    bar(fmt, args); 

    // uh-oh... 
    va_end(args); 
} 

これはつまりは、C++でva_listを使用することが一般的ではない、悪い考えますか?私がbarをtry-catchでラップすると、それは役に立ちますか?いくつかの選択肢は何ですか?

+0

IMHO可変長パラメータリストは悪い考えです。合理的な解決策のために 'iostream'モデルを見てください。 –

+0

ちょうどそれをtry/catchで囲み、 "安全"でなければなりません。 – jmucchiello

+0

@EdHeal Variableテンプレートを使用してそれらを実装する限り、可変長パラメータのリストは問題ありません。 –

答えて

4

C++標準は、va_startなどの仕様のC標準を守っています。

7.15.1p1 ... va_startマクロとva_copyマクロのそれぞれの呼び出しは、同じ関数内の対応するva_endマクロの呼び出しと一致しなければなりません。

したがって、va_startを呼び出してからva_endの前に関数を終了すると、プログラムは未定義の動作を示します。

はい、bartry/catchに配置すると役立ちます。

+0

これはもちろん100%真実であり、特定の実装を前提としたコードを書いているわけではありませんが、私は 'va_end'で見た3つの実装のすべてが' ap =(va_list)0; '、つまり、リストをnullにします。 'va_end'を呼び出すことは技術的にUBになるでしょうが、実際はおそらくそうではありません。ここでも間違ったコードを書くことを主張していないし、3のサンプルサイズはあまり大きくない。標準は 'va_list'とそれに関連する関数の使い方に固有のものでなければなりませんが、実際には問題ではないでしょう。 –

3

C++標準はこれをC標準に委ねます。 va_startとva_copyのマクロの各呼び出しは によって同じ機能にva_endマクロの対応する呼び出しに一致しなければならない

C99(ドラフト)7.15.1/1ということを教えてくれる。 barがスローした場合

はこのように、あなたはva_endを実行に失敗し、プログラムの動作は未定義ました。 try/catchを追加して、va_endが常に必要なものとして呼び出されていることを確認したら、うまくいくはずです。しかし、非PODをvarargsとして渡すことはできないので、それらを処理する必要がある場合は、代わりに別のメカニズムが必要になることを覚えておいてください。

もっと多くのC++のような選択肢は、言語によって提供されるさまざまなiostreamに見られるように、挿入演算子(operator<<)でしょう。

+9

...またはC++ 11バリデーションテンプレート。それでは、私たちは恐ろしいiostream連鎖オペレータのハッキングをもう必要としません。 –

0

上記のように、cの標準では未定義の動作です。 あなたのプラットフォームに応じて、マルコは異なるものにコンパイルすることができます。例えば、私はargs = 0を実行します。そして、va_listはchar *です。どの場合には、終了マクロが何か重要なことをしていないようです。誰がargsの割り当てを解除するのかわからない以外は何も悪いことはありませんが、最初はどこに割り当てられているのかわかりません。

私はこれを使用することをお勧めしませんが、時にはレガシーコードをサポートするために狂ったことが必要になることがあります。あなたが試してキャッチを使用することができれば、それは忘れないでください。

関連する問題