如何创建我自己的' cout'和' cerr'类

时间:2015-10-10 09:40:45

标签: c++ cout cin ostream

如何创建自己的类,其行为完全相同:std::cout& std::cerr

我正在编写一个迷你操作系统,这是一个要求,将这些作为模块存在。

代码如下:

myNewCoutClass myCout; // create cout behavioral class
myNewCerrClass myCerr; // create cerr behavioral class

myCout << someString << endl; // prints the string
myCerr << someString << endl; // prints the string as error

2 个答案:

答案 0 :(得分:2)

这些对象是std::ostream个。您可以创建自己的std::ostream。确切地说,它的工作方式完全取决于您尚未指定的数据接收器,但std::ostringstream足以让您开始测试使用它的代码。

但是,如果你真的希望重新发明std::cout不要。它的数据接收器是魔术文件句柄 stdout ,您无法重新创建它,因为它是由操作系统提供的。您可以创建std::ostreamstd::cout窃取缓冲区,但重点是什么?

答案 1 :(得分:0)

首先, 执行此操作,除非您知道非常以及您正在做什么,并且愿意承担所有涉及的风险。它只是一个例子,说明如何将另一个流绑定到stdout,实际上创建第二个cout,作为思想实验。那就是说,我们走了。

如果你想为stdout创建另一个流,你必须好好看看你的编译器的深刻内脏,并找出它如何定义cout,{{ 1}}和/或cerr。这将位于编译器相关的位置,很可能不在您期望的位置;例如,在旧版本的Visual Studio中,您必须查看clog文件夹中的一些文件:

crt\src

由此,我们可以为// Visual Studio 2010 implementation of std::cout. // Irrelevant parts omitted. // cout.cpp __PURE_APPDOMAIN_GLOBAL static filebuf fout(_cpp_stdout); #if defined(_M_CEE_PURE) __PURE_APPDOMAIN_GLOBAL extern ostream cout(&fout); #else __PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream cout(&fout); #endif struct _Init_cout { __CLR_OR_THIS_CALL _Init_cout() { _Ptr_cout = &cout; if (_Ptr_cin != 0) _Ptr_cin->tie(_Ptr_cout); if (_Ptr_cerr != 0) _Ptr_cerr->tie(_Ptr_cout); if (_Ptr_clog != 0) _Ptr_clog->tie(_Ptr_cout); } }; __PURE_APPDOMAIN_GLOBAL static _Init_cout init_cout; // stdio.h #define _INTERNAL_BUFSIZ 4096 // ... #define _IOB_ENTRIES 20 // ... #ifndef _STDSTREAM_DEFINED #define stdin (&__iob_func()[0]) #define stdout (&__iob_func()[1]) #define stderr (&__iob_func()[2]) #define _STDSTREAM_DEFINED #endif /* _STDSTREAM_DEFINED */ // _file.c char _bufin[_INTERNAL_BUFSIZ]; FILE _iob[_IOB_ENTRIES] = { /* _ptr, _cnt, _base, _flag, _file, _charbuf, _bufsiz */ /* stdin (_iob[0]) */ { _bufin, 0, _bufin, _IOREAD | _IOYOURBUF, 0, 0, _INTERNAL_BUFSIZ }, /* stdout (_iob[1]) */ { NULL, 0, NULL, _IOWRT, 1, 0, 0 }, /* stderr (_iob[3]) */ { NULL, 0, NULL, _IOWRT, 2, 0, 0 }, }; _CRTIMP FILE * __cdecl __iob_func(void) { return _iob; } // `__PURE_APPDOMAIN_GLOBAL` is an internal macro that can generally be ignored. // `_CRTIMP` is an internal macro that can generally be ignored. // `_CRTDATA2` is an internal macro that can generally be ignored. // `__CLR_OR_THIS_CALL` is a calling convention macro that expands to either // `__clrcall` or `__thiscall`. 派生自己的流,尽管它依赖于编译器。

stdout

结果......

// Visual Studio 2010 user-created char16_t cout.
// Note that in VStudio 2010, char16_t is actually a typedef for unsigned short.
#include <iostream>
#include <fstream>
#include <string>
#include <codecvt>

#define _cpp_stdout (&(__iob_func())[1])
typedef std::basic_filebuf<char16_t, std::char_traits<char16_t>> filebuf_c16;
typedef std::basic_ostream<char16_t, std::char_traits<char16_t>> ostream_c16;

int main() {
    filebuf_c16 f16out(_cpp_stdout);
    ostream_c16 c16out(&f16out);
    // It really should be tied to the other stdin/stdout/stderr streams,
    //   but this is a simple program where it won't be a problem.

    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;

    std::string u8tmp = "Hello from char16_t!";
    std::u16string u16str = converter.from_bytes(u8tmp);

    c16out << u16str << std::endl;
}

如果您想将第二个标准Hello from char16_t! (a.k.a。与ostream)绑定到basic_ostream<char, char_traits<char>>,您可以使用类似的内容。请注意,由于stdoutfout,您需要制作自己的static。还要注意,这只是在寻找麻烦,但这不是重点;只是要小心数据竞赛或类似的任何事情。

请注意,虽然可以这样做,但除非您非常了解自己所做的事情,否则愿意为出现问题的任何事情负责,并愿意花足够的时间来钻研在编译器的库和/或代码中找出它是如何实现filebuf和默认字符串的,你很好不应该这样做。

另请注意,您的代码将与编译器紧密耦合,并且同一编译器的未来版本可能会破坏它。例如,据我所知,由于CRT的更改,此代码不会使用Visual Studio 2015进行编译(具体来说,我相信它是因为stdout的更改,但我没有&#39看看吧。