在C++程序中,我们有三个流:stdin、stdout和stderr。我能在控制台应用程序中覆盖它们,并在使用表单的应用程序中使用它们吗?
例如,如果在某个基类中,我有cout << "...",我能否“重定向”到可视化的东西(如Windows表单)?
例如,如果在某个基类中,我有cout << "...",我能否“重定向”到可视化的东西(如Windows表单)?
#include <iostream>
#define LOG Log()
class Log
{
public:
Log(){}
~Log()
{
// Add an newline.
std::cout << std::endl;
}
template<typename T>
Log &operator << (const T &t)
{
std::cout << t;
return * this;
}
};
然后,当你想要改变数据的存储位置时,只需更改类的行为。以下是如何使用该类:
LOG << "Use this like an iostream.";
[编辑] 正如potato swatter建议的那样,我将添加一个不使用cout的示例:
#include <sstream>
#define LOG Log()
// An example with a string stream.
class Log
{
private:
static std::stringstream buf;
public:
Log(){}
~Log()
{
// Add an newline.
buf << std::endl;
}
template<typename T>
Log &operator << (const T &t)
{
buf << t;
return * this;
}
};
// Define the static member, somewhere in an implementation file.
std::stringstream Log::buf;
为什么你应该尝试这种方法,而不是从类似于字符串流的东西继承,主要是因为你可以轻松地动态更改记录器输出的位置。例如,您可以拥有三个不同的输出流,并使用静态成员变量在运行时进行切换:
class Log
{
private:
static int outputIndex = 0;
// Add a few static streams in here.
static std::stringstream bufOne;
static std::stringstream bufTwo;
static std::stringstream bufThree;
public:
// Constructor/ destructor goes here.
template<typename T>
Log &operator << (const T &t)
{
// Switch between different outputs.
switch (outputIndex)
{
case 1:
bufOne << t;
break;
case 2:
bufTwo << t;
case 3:
bufThree << t;
default:
std::cout << t;
break;
}
return * this;
}
static void setOutputIndex(int _outputIndex)
{
outputIndex = _outputIndex;
}
};
// In use
LOG << "Print to stream 1";
Log::setOutputIndex(2);
LOG << "Print to stream 2";
Log::setOutputIndex(3);
LOG << "Print to stream 3";
Log::setOutputIndex(0);
LOG << "Print to cout";
这可以轻松扩展,创建一个处理日志的强大方式。您可以添加文件流、使用 std::cerr 等。
endl
,该怎么办?此外,此示例未说明如何与除 cout
之外的其他内容进行接口。 - Potatoswatter这是我在Windows上将std::cout
重定向到GUI的代码:
struct STDOUT_BLOCK : SLIST_ENTRY
{
char sz[];
};
class capturebuf : public std::stringbuf
{
protected:
virtual int sync()
{
if (g_threadUI && g_hwndProgressDialog) {
// ensure NUL termination
overflow(0);
// allocate space
STDOUT_BLOCK* pBlock = (STDOUT_BLOCK*)_aligned_malloc(sizeof *pBlock + pptr() - pbase(), MEMORY_ALLOCATION_ALIGNMENT);
// copy buffer into string
strcpy(pBlock->sz, pbase());
// clear buffer
str(std::string());
// queue string
::InterlockedPushEntrySList(g_slistStdout, pBlock);
// kick to log window
::PostMessageA(g_hwndProgressDialog, WM_APP, 0, 0);
}
return __super::sync();
}
};
main()
函数内部:capturebuf altout;
std::cout.set_rdbuf(&altout);
WM_APP
消息并从SList中获取字符串。但这处理了cout
重定向部分。altout
超出作用域之前,您需要将streambuf*
更改回来。以下代码将这样做:struct scoped_cout_streambuf_association
{
std::streambuf* orig;
scoped_cout_streambuf_association( std::streambuf& buf )
: orig(std::cout.rdbuf())
{
std::cout.rdbuf(&buf);
}
~scoped_cout_streambuf_association()
{
std::cout.rdbuf(orig);
}
};
在 main
函数内部:
capturebuf altout;
scoped_cout_streambuf_association redirect(altout);
set_rdbuf
函数,只有两个ios::rdbuf
的重载。 - Potatoswatterstd::basic_stringbuf< char >
并覆盖sync
和underflow
虚函数,以将流直接绑定到文本字段。但这相当高级,可能不值得。cin.rdbuf()
,而是调用它。这并不是“hackish”,它恰好是标准库iostreams的设计用法。 - Ben Voigtcin
的底层缓冲区指针,使其指向一个本地变量,以便被调用的函数可以通过全局变量访问该本地变量。这非常不规范。 - Potatoswatterstreambuf
对象,并提供公共API以实现清晰的多态性。这就是标准库iostreams中实现多态性的方式。 - Ben Voigtrdbuf
并不是问题所在,使用库全局变量作为函数参数才是。我认为这很清楚了... - Potatoswatter