【问题标题】:stringstream temporary ostream return problemstringstream 临时 ostream 返回问题
【发布时间】:2009-10-08 22:20:14
【问题描述】:

我正在创建一个包含以下部分的记录器:

// #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;
  }
}

导致“格式”包含垃圾数据而不是通常的正确字符串。

我认为这是因为

还是我错了?

(为什么 string() 会以这种方式工作?是因为它返回对自身的引用吗?我假设是的。)

我真的很想这样做,因为我会在登录发布模式时消除额外的分配。

任何以这种方式完成它的指针或技巧都将受到欢迎。在我的实际解决方案中,我有许多不同的日志功能,它们都比这更复杂。所以我希望在调用代码中以某种方式实现它。 (如果可能,不要修改我的#define)

只是给出一个想法,一个我的实际#defines的例子:

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

它匹配采用 char*、string() 和 ostream() 的 varargs printf-like log 函数以及采用 string()、exception() 和 HRESULT 的非 vararg 函数。

【问题讨论】:

  • “它不起作用”是什么意思?
  • 你是对的,你应该复制一个字符串,所以format应该是std::string类型而不是const std::string &amp;类型。但是,您可以将 dynamic_cast 放入 cout 表达式并完全丢失变量。
  • 不,我不需要复制。 str() 返回的字符串的内容保证在后续调用这些方法之间保持不变(与 string::c_str() 的方式非常相似)。至于我为什么这样做,我需要格式字符串,因为我实际上想将它传递给采用字符串的单个参数函数,或者传递给采用 char* 的 VARARGS 方法,具体取决于接收到的其他参数。 (但这一切都超出了我的问题的范围 - 得到了满意的回答)
  • 澄清一下,我现在的解决方案是log(stringstream().flush() &lt;&lt; "hello " &lt;&lt; 1);,但感谢所有其他想法。

标签: c++ iostream stringstream temporary


【解决方案1】:

认为我知道发生了什么。这会产生预期的输出:

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

虽然没有:

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

(它写入一个十六进制数字,后跟“1”数字)

解释的几个要素:

  • 右值不能绑定到非常量引用
  • 可以临时调用成员函数
  • std::ostream 有一个成员操作符
  • std::ostream 有一个成员操作符
  • 对于 char*,操作符不是成员,它是操作符

在上面的代码中,std::stringstream() 创建了一个临时的(一个右值)。它的生命周期没有问题,因为它必须持续到它被声明为的整个完整表达式(即,直到对 log() 的调用返回)。

在第一个示例中,一切正常,因为首先调用了成员 operator

在第二个示例中,不能使用“std::stringstream()”作为第一个参数调用 operator

顺便说一句:为什么不将 log() 函数定义为:

void log(const std::ostream& os)
{
    std::cout << os.rdbuf() << std::endl;
}

【讨论】:

  • 感谢您的信息!这使我找到了首选的解决方案……我知道我走在正确的轨道上。因此,我让它始终如一地工作的最佳方法如下:log(stringstream().flush() &lt;&lt; "hello " &lt;&lt; 1);
  • PS:BYT是什么意思?你对log() 函数的定义与我有何不同?
  • 哎呀!这是一个错字。我想写“顺便说一句”。已更正
  • 啊啊!回想起来,完整地写它会更短:)
【解决方案2】:

将您的 LOG() 宏更改为:

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

这将使您可以在调试日志中使用以下语法,因此您不必手动构造字符串流。

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

然后将其定义为无释放,您将没有额外的分配。

【讨论】:

  • 你不需要 do/while 在块周围。您可以使用独立的大括号来控制表达式的范围。
  • 我的主要目标是优化。尽管我想保留一个适用于传递的许多不同类型的单个 LOG() 宏,但我可能需要为此创建一个异常来区分它。问题是在发布模式下确实仍然会保留一些日志(取决于日志记录的严重性),对于这些我不希望上面描述的开销,因此我可能需要为每个严重性创建异常#define。我试图在这个遗留项目上尽可能地保持日志记录语法的格式不变。
  • 我将其标记为有用,但暂时不会将其标记为解决方案。
  • @KayEss:do/while 是这样可以将分号添加到行尾,这使得对LOG() 的调用看起来更自然。见stackoverflow.com/questions/154136
  • @Marius:虽然我完全理解使用统一日志记录宏背后的目标,但实际上很难正确实现。使用单独的LOG_DEBUG()LOG_WARNING()LOG_ERROR() 等宏更易于管理。
猜你喜欢
  • 2013-02-14
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-25
相关资源
最近更新 更多