{A}简介
这篇文章是"第二版",取代旧的,从以前的一些要求,并完善某些设计方面。
最初的想法,而写一个小程序,一定的数学计算,不想按预期方式工作。有必要在某些执行点跟踪的一些值,通常跟踪执行流。
在控制台应用程序,这是典型的做法与std::法院,但是这可能不是一个很好的选择,如果你想保留调试肮脏的控制台,或者您使用的是Windows应用程序,没有控制台。
历久弥新的C运行时LT; crtdbg.hgt;头,提供了一些类似的printf名为_RPTx宏。但是,如果我们想要的东西,可以支持一个通用的C风格的界面,可能每一种类型的好,缺少的东西。
的Win32 OutputDebugString的帮助,但它仅仅是一个LPCTSTR输出功能。所以,这个想法是写一个STL流输出使用该API。目录{A7}{A8}{A9}位内流
最直观的方式来此解决方案是有一个重写一个类operatorlt; LT;,调用Win32 API的输入数据转换成一个字符串后,
事实上,这样做,将导致。重新发明的STL格式公用事业。
的STL流是在事实上共同的std全部重新诠释:basic_ostream基类,提供给它一个方便覆盖的std::basic_streambuff类。
流需要通过选定的区域设置的数据格式,提供公用事业而streambuff提供了空间流写。流也需要照顾对媒体输出(在这种情况下,调试器;更普遍,标准输出或一个打开的文件)。
因此,适当的方式来处理这个任务是提供一个覆盖冲洗调试输出到提供的缓冲区的内容basic_streambuff。{A10}安排
如果我们希望能够在一个纯粹的Win32环境下使用等功能,我们必须准备与TCHARs。的std::ostream的和wostream出于这个原因,是不适合的,因为他们是一个预定义的字符类型。
我们需要另一种类型定义。在GE_::DBG命名空间,我把一个ostream_t的typedef,走样basic_ostreamlt; TCHARgt;
然后一个内在的report_h命名空间是用来包含实现细节。在这里面,类的全局声明为成员ostream_t流对象和streambuff对象,其中streambuff是从std派生的类:basic_streambuflt; TCHARgt。
要提供跟踪缩进,深度成员也出席了会议。
不管的streambuff实施,全局的构造函数初始化流,通过streambuff对象的地址。此外,在全球功能提供了一个懒惰的静态全局实例的访问。
内的命名空间,GE_::DBG:出(外)返回引用静态的隐藏流,使其成为全球可用。{A11} streambuff实施
streambuff类派生自std::basic_sreambufflt; TCHARgt;和它的构造函数调用继承pubsetbuff,提供的TCHAR []缓冲区,保持一个字符作为监护人,作为一个字符串结束符。
这种实现的关键部件是setbuf,溢出和同步覆盖虚函数。
setbuf覆盖简单地调用基SETP功能来告诉它使用提供的缓冲区,由先达第二的最后一个字符(,从而留下的最后一个总是空)。
同步功能是所谓的缓冲区时,需要被刷新流。它发生时的std::冲洗或在std::endl机械臂遇到。它只是获取当前正在运行的PBASE()和PPTR()(流输出,其最后的输出字符开始),计算出字符串的大小,力量的一个过去的最后一个输出字符'\ 0',并调用OutputDebugString的,从而导致缓冲区中的内容打印出来。在此之后,它调用pbump(深圳)倒带的缓冲区,允许流覆盖它。
溢出功能被称为流发送到输出比缓冲区可以包含多个字符。它只是要求同步,从而刷新缓冲区,地方在刚刚清洁的缓冲区"待"字,并递增文字的位置。{A12}简单的样品
上面描述的代码是所有这样的snipset要求是什么int n = 5;
double d = 6.75;
dbg::out() << "n = " << n << "; d = " << d << std::endl;
跟踪像这样的一行{C}{A13} RAII缩进
将DBG:跟踪类是RAII类(一类的析构函数"撤消"的构造做了什么资源),有助于跟踪函数调用的进入/退出。
它的名称和地址参数,并递增/建筑/销毁globals.depth成员递减。
它的构造将通过名称[地址]由GT之前,在深入广泛的领域的迹象。同样,析构函数写入LT的名称[地址],具有相同的缩进。
通用operatorlt; LT;流输出参数,前面一个缩进"。"这种"效果"还可以得到在DBG::dpeth机械臂,一个正常的"LT,LT"链内。
因此,此功能:void function(int n)
{
dbg::trace trc("function", function);
trc << "hallo with n = " << n << std::endl;
if(n) function(n-1);
}
如果N = 3的要求,将跟踪:
{A14} RAII对象跟踪>function[xxxxxxxx]
.hallo with n = 3
>function[xxxxxxxx]
.hallo with n = 2
>function[xxxxxxxx]
.hallo with n = 1
>function[xxxxxxxx]
.hallo with n = 0
<function[xxxxxxxx]
<function[xxxxxxxx]
<function[xxxxxxxx]
<function[xxxxxxxx]
将DBG:轨道类是类似于在DBG:跟踪,但它使用的quot; C:"和"D:",而不是"长线"字符串"GT"的招牌,不递增/递减的深度(而连贯书面)。
,它可以被用来跟踪对象的创建/破坏,想在这snipset:class myobject
{
private:
dbg::track trk;
public:
myobject() :trk("myobject", &trk) {}
};
与下面的代码:int main()
{
dbg::trace trc("main", 0);
trc << "creating two objects on stack" << std::endl;
myobject m1, m2;
trc << "creating a static object" << std::endl;
static myobject m3;
trc << "Bye" << std::endl;
}
将跟踪>main[00000000]
.creating two object on stack
C:myobject[xxxxxxxx]
C:myobject[yyyyyyyy]
.creating a static object
C:myobject[zzzzzzzz]
.Bye
D:myobject[yyyyyyyy]
D:myobject[xxxxxxxx]
<main[00000000]
D:myobject[zzzzzzzz]
(注意静态对象,主要返回后销毁)其他的东西
插入跟踪代码的问题之一是它消失从发行版本(避免跟踪字符串和可能是漫长的函数调用)。
在前面的文章中中,我建议使用"假流",其操作是"不作为"的实施,让编译器的优化过程,以消除假冒电话。
在这里,我使用的是更传统的方法。作为自己的参数的DBG宏定义,如果定义了_DEBUG符号,或为"无",如果没有定义这样一个符号。
这是可能放置在此snipset像调用:void myfunc()
{
DBG(dbg::trace trc("myfunc", myfunc);)
...
DBG(trc << "some code" << a_variable << std::endl;)
...
}
或跟踪的对象中插入一些虚拟变量,如
结论class myclass
{
DBG(dbg::trackedobj<myclass> trk;)
public:
...
};
我希望这个新版本可以比其前身更加灵活和实用。一些旧的文章(如stackval操盘"fetaures",不在座的,主要是因为他们没有如果使用RAII的包装要求。
在任何情况下,我放在旧的文章文字和嵌入式代码在这个阶段"的利益,谁愿意看到的历史。编译器的兼容性
这里给出的代码是编译在C 8.0(顺昌表达)。代码本身并不是特别明智的编译器,但不同的版本,编译器提供的STL可以赚取差价。
一个特定的情况下,使用Unicode是:当TCHAR ID定义为char(没有Unicode),为wchar_t *类型是由性病治疗:作为一个void *的ostream(打印地址的十六进制表示) 。
定义UNCODE时,在C 8 STL处理都作为字符串的char *和wcahr_t *由std::wostream。这允许你写C字符串(为const char []),即使在Unicode环境的调试字符串文字。
不幸的是,这是不guaranted始终工作:旧的C编译器提供的STL实现可能无法代表的char *"字符串"在std::wostream时,TCHAR是定义为wchar_t。德育:除非你知道你的工作正好与STL版本提供商,始终使用_T宏()。。 (正是我没有使用本文在我的示例snipsets。)