20

ロギングシステムのフロントエンドであるC++クラスがあります。そのロギング機能は、C++11の可変個引数テンプレートを使用して実装されます。

template <typename... Args>
void Frontend::log(const char *fmt, Args&&... args) {
  backend->true_log(fmt, std::forward<Args>(args)...);
}

各ロギングバックエンドは、独自のバージョンのを実装します。true_logこれは、特に、転送されたパラメータを使用してを呼び出しますvsnprintf。例えば:

void Backend::true_log(const char *fmt, ...) {
  // other stuff..
  va_list ap;
  va_start(ap, fmt);
  vsnprintf(buffer, buffer_length, fmt, ap);
  va_end(ap);
  // other stuff..
}

すべてがうまく機能し、私は幸せです。

ここで、パラメーターに静的チェックを追加しlog()ます。具体的には、GCCのprintfフォーマット属性を使用します。

log()関数にタグを付けることから始めました__attribute__ ((format (printf, 2, 3)))this最初の「非表示」パラメーターと同様に、パラメーターのインデックスを1つシフトする必要があります)。コンパイルエラーで失敗した場合、これは機能しません。

error: args to be formatted is not ‘...’

次に、同じ属性をtrue_log()関数に追加しようとしました。コンパイルされますが、実際にはエラーチェックは実行されません。いくつかの無効な形式/変数の組み合わせを渡そうとしましたがlog()、警告は発行されませんでした。たぶん、この種のチェックは「遅すぎる」、つまり、変数に関する情報が一連の呼び出しで失われたのでしょうか。

最後の手段として、で注釈を付けるlog()__attribute__ ((format (printf, 2, 0)))、間違った形式の文字列に関する警告が表示されますが、無効な形式と変数の組み合わせに対する診断は発行されません。

問題の要約:C ++ 11の可変個引数テンプレートを使用する場合、GCCから完全な形式をチェックするにはどうすればよいですか?

4

3 に答える 3

3

私はあなたができるとは思わない。それがリテラルの場合、GCCはフォーマット文字列のみを検証するに違いありません。これが、format属性を on にtrue_log設定しても機能しない理由です。その関数は、(構文的に) 実行時に決定される文字列のように見えるもので呼び出されます。直接配置するとそれlogを回避できますが、format属性が可変長テンプレートをサポートする必要がありますが、そうでないことが証明されました。

書式設定された出力を行う C++ 風の方法を検討することをお勧めします。たとえば、boost::formatprintf のように機能するものがありますが、パラメーターの型の数と型が書式文字列と一致することを動的に検証します。ただし、可変個引数テンプレートは使用しませんが、代わりに (演算子 % を介して) 1 つずつ渡されたパラメーターを消費します。

于 2012-09-29T13:22:26.157 に答える
3

記録のために、私は C++11 可変個引数テンプレートを完全に削除し、従来のva_list.

__attribute__((format(printf, 2, 3)))
void Frontend::log(const char *fmt, ...) {
  va_list ap;
  va_start(ap, fmt);
  backend->true_log(fmt, ap);
  va_end(ap);
}

void Backend::true_log(const char *fmt, va_list ap) {
  // log the message somehow
}
于 2014-04-01T22:10:41.000 に答える