C++ iostream 使用字符串流的损坏
C++ iostream Corruption using stringstream
我正在尝试编写一个非常简单的线程安全记录器。理想情况下,我希望它像std::cout
一样工作,您可以在其中重载<<
运算符,并神奇地将所有内容显示在日志中。我在Windows机器上,所以这是我尝试的方法:
// Threadsafe logger
class Logger
{
public:
Logger()
{
InitializeCriticalSection(&s);
}
~Logger()
{
DeleteCriticalSection(&s);
}
void Log(std::ostream const& os)
{
EnterCriticalSection(&s);
//std::cout << static_cast<std::stringstream const&>(os).str();
std::cout << os.rdbuf();
LeaveCriticalSection(&s);
}
private:
CRITICAL_SECTION s;
};
请注意,我已经尝试了两种Log()
函数的方法。我接受ostream
的原因是,这就是stringstream
在调用<<
运算符后产生的。当我运行此代码时,Log()
函数的两个变体都以相同的方式失败:
#include <iostream>
#include <sstream>
#include <Windows.h>
int main(int argc, char* argv[])
{
Logger logger;
//logger.Log(std::stringstream("Test"));
logger.Log(std::stringstream("Another ") << "test");
std::cin.get();
}
输出第一行("测试")工作正常,并使用 Log 函数的两个变体正确显示。第二行输出一个损坏的输出:
testher
这显然是test
写在Another
上.关于这些流的工作方式,我错过了什么?我试着打了一个flush
电话,希望能解决问题,但它什么也没做。
如何让线程安全记录器在流中正常工作的尝试?
使用可变参数模板:
void Log_impl(std::ostream &os) {} // recursion base case
template<typename T,typename... Us>
void Log_impl(std::ostream &os,T &&t,Us &&... us) {
os << std::forward<T>(t);
Log_impl(os,std::forward<Us>(us)...);
}
template<typename... Ts> void Log(Ts &&... ts) {
std::stringstream ss;
Log_impl(ss,std::forward<Ts>(ts)...);
fprintf(stdout,"%sn",ss.str().c_str()); // thread safe output
}
用法:
Log("Another"," test ",100);
我还没有真正测试过这段代码...
,这种解决问题的方法至少使用起来有些笨拙,因为它需要您创建某种辅助ostream
对象,将数据流式传输到其中,然后将其传递给您的日志。这似乎与你所说的你真正喜欢的不太吻合。
我也对你如何完成线程锁定代码有点不那么兴奋。例如,如果您在std::cout << os.rdbuf();
期间遇到异常,则可以在不离开关键部分的情况下退出范围。
我想我会从关键部分周围的薄包装器开始,添加一个 RAII 样式的类来锁定关键部分(并在超出范围时自动解锁它),然后在实现 Log
类时使用它们。此外,我会作弊并让Log
类使用模板成员函数一举接受几乎任何类型的输出:
编辑:经过一番思考,我决定接受这样一个概念,即每个问题都可以通过另一个级别的间接解决。为此,我添加了一个中间transaction
,它将许多项目的输出链接到一个stringstream
中,然后将该结果写出为线程安全事务。
#include <windows.h>
#include <iostream>
#include <sstream>
class crit_sect {
CRITICAL_SECTION cs;
void lock() { EnterCriticalSection(&cs); }
void unlock() { LeaveCriticalSection(&cs); }
friend class lock;
crit_sect(crit_sect const &); /* = delete; */
crit_sect &operator=(crit_sect const &other); /* = delete; */
public:
crit_sect() { InitializeCriticalSection(&cs); }
~crit_sect() { DeleteCriticalSection(&cs); }
};
class lock {
crit_sect &cs;
public:
lock(crit_sect &c) : cs(c) { cs.lock(); }
~lock() { cs.unlock(); }
};
class transaction {
std::ostringstream buffer;
public:
transaction(std::string const &s="") : buffer(s, std::ios::out | std::ios::ate) {}
template <class T>
transaction &operator<<(T const &t) {
buffer << t;
return *this;
}
friend std::ostream &operator<<(std::ostream &os, transaction const &t) {
return os << t.buffer.str();
}
};
class Log {
std::ostream &out;
crit_sect mutex;
public:
Log(std::ostream &sink) : out(sink) { }
template <class T>
void operator<<(T const &t) {
lock l(mutex);
out << t;
}
};
int main() {
Log l(std::cout);
l << "This is a stringn";
l << (transaction("Another ") << "Test");
return 0;
}
由于 log
类型不支持链接,因此任何不使用transaction
链接输出的尝试都将失败(不会编译)。与原始版本相比,用法仍然更简洁一些 - ostringstream
ctor所需的额外参数被隐藏,并且名称transaction
阐明了正在做的事情,或者更确切地说,完成了什么。
问题不在于记录器,而在于你对字符串流的使用。初始化 std::stringstream 时,流的位置指示器位于流的开头。
现在,当您开始使用 '<<' 写入字符串时,您开始在位置指示器处写入,替换之前的任何内容。
要解决此问题,您可以使用std::stringstream("Another ", stringstream::in |字符串流::输出 |std::stringstream::ate)
(根据 http://www.cplusplus.com/reference/iostream/stringstream/stringstream/)
将"test"末尾的空字节发送到您创建的临时字符串流("另一个"),这就是您看到"testher"的原因。
- 删除字符串后C++检测到堆损坏
- 检测到堆损坏(字符串导致堆损坏)|C++
- 我的 c 字符串复制函数正在损坏其他变量的堆栈
- 字符串标记化期间的内存损坏
- 放置在外部 DLL 中的类中的字符串数据的内存损坏
- 标准::字符串中的堆损坏
- C#到C代码P/调用多个std:字符串声明导致堆栈损坏
- 为了验证std::字符串是否已损坏,您将执行哪些调试验证
- 使用时间函数后,字符串已损坏
- 字符指针指向字符串,然后指向字符串数组。"./a.out"中的错误:malloc():内存损坏:0x0900c3b0***
- C++ - 删除标准::字符串*;堆损坏
- CPUID品牌字符串损坏
- 第二个字符串在多次重新分配调用后损坏
- Visual Studio 2008:字符串文字"??-"、"??'"、"??="损坏
- C++ iostream 使用字符串流的损坏
- 烟囱损坏;使用字符串进行位操作;MSVC++
- 从文件读取时字符串已损坏
- 按字符串堆损坏
- c# COM字符串在c++ BSTR中被损坏
- 变量损坏c++字符串Visual Studio 2005