9

文字列操作の一部をショートカットするために探しているこのロギングシステムがあります。

ロギングシステムは、機能マクロを介して使用され、その後、単一の関数呼び出しに転送されます。例#define Warning(...) LogMessage(eWarning, __VA_ARGS__);

次に、LogMessageはsnprintf新しいバッファに対してを実行し、インストールされているログターゲットにそのメッセージを表示します。printf、OutputDebugStringなど。

残念ながら、私たちが持っているバッファが十分に大きくないという問題に遭遇したので、出力が切り捨てられます。また、snprintfがva_argsを処理しようとするため、出力メッセージにパーセント記号が含まれている場合、このメソッドは失敗することに気付きました。最後に、ログメッセージの大部分はva_argsを使用しないため、文字列をコピーしてロガーに提示するのはばかげているようです。

それで、私の関数プロトタイプを考えると、楕円の存在に基づいてオーバーロードできるはずですか?言い換えれば、私は次のようなことができると仮定できるはずです:

LogMessage(LogLevel, const char* message, ...);
LogMessage(LogLevel, const char* message);

私のグーグルの試みは特に有用なものを何も生み出しませんでした(他に何も一致しない場合は省略記号が一致することを示し、何も一致しないという私の要件とは異なります)、実装での最初の刺し傷はあいまいな関数呼び出しエラーを私に与えました。

エラーがあるので、これはできないことを受け入れる必要がありますが、それが使用しているコンパイラだけなのか、それとも間違っているのか疑問に思っています。私は同様の効果を達成することができます

// edited version of what I really have to remove our local APIs,
// please excuse minor errors
const char* message = NULL;
char buffer[512];

va_list args;
va_start(args, format);

if(strcmp(format, "%s") == 0) {
    message = va_arg(args, const char*);
}
else if (strchr(format, '%') == NULL) {
    message = format;
}
else {
    vsnprintf(buffer, 512, format, args);
    message = buffer;
}

va_end(args);

...しかし、これは、渡されるパラメータの数だけで知ることができる典型的なケースでは無駄に思えます。たとえば、省略記号が何にも一致しない場合は、他の関数を選択しますか?これが機能しない場合、どの関数を呼び出すかをユーザーがマクロ名で決定する必要がない、私が試すことができる別の方法はありますか?正直なところ、誰かError("Buffer not 100% full");がログメッセージで無計画に言って、結果として「Buffer not 1007.732873e10ull」を受け取った場合、「無駄」についてはそれほど重要ではありません。

編集:私の例は「それをしないでください」によって答えられましたが、質問自体は答えることができますか?

4

4 に答える 4

3

また、snprintfがva_argsを処理しようとするため、出力メッセージにパーセント記号が含まれている場合、このメソッドは失敗することに気付きました。

その後、発信者は注意してください。関数がprintfスタイルのフォーマット文字列を使用するように文書化されている場合、パーセント記号をエスケープするのは呼び出し元の責任です。無効なフォーマット文字列を処理しようとするのは、実際にはあなたの仕事ではありません。

正直なところ、誰かError("Buffer not 100% full");がログメッセージで無計画に言って、結果として「Buffer not 1007.732873e10ull」を受け取った場合、「無駄」についてはそれほど重要ではありません。

C++の精神に沿ったほうがいいと思います。Javaメソッドでは通常、有効な引数をチェックし、無効な値が渡されると例外をスローします。C ++では、発信者に自分の足を撃たせるだけです。100%%関数を適切に呼び出す方法を学習しないように保護するために、フープを飛び越えて書くよりも、彼らに書いてもらうほうがよいでしょう。

于 2010-09-02T00:23:47.423 に答える
2

C++11 では、引数が 1 つの場合に明示的に特化した可変個引数テンプレートを使用できます。

void bar(int a, ...) {
  // va_list stuff
}

template <typename... T>
void foo(int a, T... args) { // (1)
  bar(a, args...); // or do all the vararg stuff here directly
}

template <>
void foo(int a) {            // (2)
  printf("single\n");
}

それで:

//foo();    // compile error, as expected
foo(1);     // uses (2)
foo(2,1);   // uses (1)
foo(3,1,"asdf"); // uses (1)
...
于 2012-09-12T02:22:26.387 に答える
1

わかりました、質問の解決策を思いついたと思います。

実際には、楕円のパラメーターがあるかどうかだけに基づいてオーバーロードすることはできません。つまり、楕円の存在によってのみ変化するシグネチャを持つ関数を持つことはできません。

ただし、楕円プロトタイプからパラメーターを削除すると、私が求めていたようなことを行うことができます。const char*いえ

LogMessage(LogLevel, ...);
LogMessage(LogLevel, const char* message);

は明確ですが、最初のパラメーターが であると仮定する必要があるという事実と戦いますが、const char*そうではない可能性があります。John Kugelman のアドバイスを参考にしてください。許可されているパラメーターを文書化し、ユーザーは注意してください。のみがある場合は非楕円関数が呼び出され、文書化された後にいくつかのパラメーターが続くconst char*ものを含む何かがある場合は、楕円関数が呼び出されます。const char*

残念ながら、これは va_args を子関数に渡すことができる解決策の範囲のようです。私の例ではvsnprintf.

それが提示された質問に答えるものであっても、私自身の答えを受け入れるのはおそらく悪い形です。

于 2010-09-03T17:42:59.070 に答える