6

次のセクションでロガーを作成しています。

// #define LOG(x) // for release mode
#define LOG(x) log(x)

log(const string& str);
log(const ostream& str);

するという考えで:

LOG("Test");
LOG(string("Testing") + " 123");
stringstream s;
LOG(s << "Testing" << 1 << "two" << 3);

これはすべて意図したとおりに機能しますが、そうすると:

LOG(stringstream() << "Testing" << 1 << "two" << 3);

それは動作しません:

void log(const ostream& os)
{
  std::streambuf* buf = os.rdbuf();
  if( buf && typeid(*buf) == typeid(std::stringbuf) )
  {
    const std::string& format = dynamic_cast<std::stringbuf&>(*buf).str();
    cout << format << endl;
  }
}

通常の正しい文字列ではなく、ジャンク データを含む 'format' になります。

これは、 << 演算子によって返された一時的な ostream が、元の文字列ストリームよりも長生きするためだと思います。

それとも私が間違っていますか?

(string() がこのように機能するのはなぜですか?それは、それ自体への参照を返すためですか?私はそう仮定しています。)

リリースモードでログインするときに追加の割り当てを排除するので、私は本当にこの方法でやりたいと思っています。

この方法でそれを行うための指針やコツは大歓迎です。私の実際のソリューションでは、さまざまなログ関数があり、それらはすべてこれよりも複雑です。したがって、これを何らかの方法で呼び出し元のコードに実装することをお勧めします。(可能であれば #define を変更しないでください)

アイデアを与えるために、私の実際の #defines の 1 つの例を示します。

#define LOG_DEBUG_MSG(format, ...) \
  LogMessage(DEBUG_TYPE, const char* filepos, sizeof( __QUOTE__( @__VA_ARGS__ )), \
  format, __VA_ARGS__)

これは、char*、string()、および ostream() を取る可変引数の printf のようなログ関数と、string()、exception()、および HRESULT を取る非可変引数関数に一致します。

4

2 に答える 2

7

何が起こっているかがわかると思います。これにより、期待される出力が生成されます。

log(std::stringstream() << 1 << "hello");

これはしませんが:

log(std::stringstream() << "hello" << 1);

(16 進数の後に "1" の数字が続きます)

説明のためのいくつかの要素:

  • 右辺値を非 const 参照にバインドすることはできません
  • 一時的にメンバー関数を呼び出しても問題ありません
  • std::ostream にはメンバー operator<<(void*) があります
  • std::ostream にはメンバー operator<<(int) があります
  • char* の場合、演算子はメンバーではなく、operator<<(std::ostream&, const char*) です。

上記のコードでは、std::stringstream() は一時 (右辺値) を作成します。宣言されている完全な式の間 (つまり、log() の呼び出しが戻るまで) 持続する必要があるため、その寿命は問題ではありません。

最初の例では、メンバー operator<<(int) が最初に呼び出され、返された参照を operator<<(ostream&, const char*) に渡すことができるため、すべてが正常に機能します。

2 番目の例では、「std::stringstream()」を第 1 引数として operator<<( を呼び出すことはできません。これは、非 const 参照にバインドする必要があるためです。ただし、メンバー operator<<(void *)会員なのでOKです。

ところで: log() 関数を次のように定義しない理由:

void log(const std::ostream& os)
{
    std::cout << os.rdbuf() << std::endl;
}
于 2009-10-09T18:01:56.843 に答える
6

マクロLOG()を次のように変更します。

#define LOG(x) do { std::stringstream s; s << x; log(s.str()); } while(0)

これにより、デバッグ ログで次の構文を使用できるようになるため、文字列ストリームを手動で作成する必要がなくなります。

LOG("Testing" << 1 << "two" << 3);

次に、リリースのために何も定義しないと、余分な割り当てがなくなります。

于 2009-10-08T22:34:46.487 に答える