一个很好用的调试辅助类,使用需要一定C++基础,请多提建议,UP有分

【原创】一个很好用的调试辅助类,使用需要一定C++基础,请多提建议,UP有分![size16px]很多时候我们需要把程

【原创】一个很好用的调试辅助类,使用需要一定C++基础,请多提建议,UP有分!
[size=16px]很多时候我们需要把程序运行过程中的一些临时变量显示出来,一般我们可以通过wcout或cout类将调式信息输出到控制台。但有时我们需要将调试信息输出到别的地方,比如VS.net的“输出”窗口就比较麻烦了,可能需要频繁的在字符串和数字间相互转换,这对于C++程序员来说可真是一场恶梦。下面提出一种方案,可以很好的解决这个问题。即利用替换wcout或cout的内部字符串缓存的方法,使写入到wcout或cout的字符串转写到其它的设备中。

第一步:在你整个工程的头文件,比如stdafx.h里的下面加入以下代码。这是用于wcout或cout的字符串缓存类。

C/C++ code
#include <iostream>#include <algorithm>#include <sstream>#include <tchar.h>using namespace std;template<class _Elem, class _Traits = char_traits<_Elem> >class CDebugStreamBuf : public basic_stringbuf<_Elem, _Traits >{    // 回调函数接受一个准备输出到设备的字符串参数。public:    // 如果回调函数返回false,缓存对象的_Mystate将被设为BAD,并不再继续写入。    typedef bool (__stdcall *_Myof)( const _Elem* );    explicit inline CDebugStreamBuf( _Myof _Outfunc )        : _Myoutfunc( _Outfunc ){}    // 初始化回调函数    virtual ~CDebugStreamBuf( void ){}    // 虚析构函数protected:    // 每次同步都会刷新缓存中的所有内容    virtual int sync( void )    // 重载sync函数,将在需要输入到设备(同步)时被调用    {    // 如果无回调函数或缓冲未初始化,返回错误。_Mystate将被设为BAD        if ( _Myoutfunc == 0 || pptr() == 0 ) return -1;        else if ( _Mysb::pbase() >= _Mysb::epptr() ) return 0;        else    // 确保缓存可以写入        {    // 将当前写指针的后一位改写为0,如果缓存长度不足将调用overflow执行添加            sputc( _Traits::to_char_type( 0 ) );            setg( pbase(), pbase(), pptr() );    // 将读指针设为缓存起点            seekpos( 0, ios_base::out );    // 将写指针置0以备下次从起点写入            for ( ; gptr() < egptr(); )    // 遍例            {    //忽略包括当前指针在内后面的所有0值                for ( ; !sbumpc(); );                if ( !_Myoutfunc( gptr() - 1 ) ) return -1;    // 输出字符串                gbump( (int)( find( gptr(), egptr(),    // 找到下一个0值                    _Traits::to_char_type( 0 ) ) - gptr() + 1 ) );            }        }        return 0;    }protected:    _Myof _Myoutfunc;    // 回调函数成员,必须在构造时初始化};


第二步:在包含(#include)第一步所选头文件的任意一个代码文件(*.cpp)顶部,包含头文件说明的下面加入如下代码。在这里我们对字符串缓存类的对象进行了初始化,并自己定义了一个回调函数。该回调函数将接受来自缓存类的调式自符串,然后将它输出到VS.net的“输出”窗口。当然,你也可以自己定义这样一个回调函数使字符串写入磁盘映像或SOCKET之类更复杂的设备。
C/C++ code
bool CALLBACK MyOutputDebugString( LPCTSTR lpText ){    OutputDebugString( lpText );    return true;}CDebugStreamBuf<TCHAR> g_DbgBuf( MyOutputDebugString );


第三步:在任意一个类的初始化里加入如下代码(两行任选其一):
C/C++ code
    wcout.rdbuf( &g_DbgBuf );    // Unicode版本    cout.rdbuf( &g_DbgBuf );    // 非Unicode版本

之后就可以方便的使用wcout或cout进行GDI程序的调式了,写入wcout或cout的字符串将全部显示在VS.net的“输出”窗口中。

使用举例
C/C++ code
    //     CRect rc;    GetWindowRect( &rc );    wcout << _T("窗口的宽度是:") << rc.Width() <<        _T(",高度是:") << rc.Height() << endl;

[/size]

[解决办法]
up一下
[解决办法]
up一星大师
[解决办法]
探讨
up一星大师

[解决办法]
-> UP <-
[解决办法]
学习了
------解决方案--------------------


探讨
-> UP <-

[解决办法]
up,学习下!
[解决办法]
up
[解决办法]
up!
[解决办法]
拜lz~~
[解决办法]
up up
JF
[解决办法]
up 
我研究下大师的杰作
[解决办法]
ding
[解决办法]
up
[解决办法]
不明白,研究研究!
[解决办法]
我来UP
[解决办法]
探讨
我来UP

[解决办法]
UP一下
[解决办法]
up
[解决办法]
mark
[解决办法]
up
[解决办法]
up
[解决办法]
up
[解决办法]
感觉不好用。感觉麻烦、复杂。不过代码比较强。支持一下。
[解决办法]
up
[解决办法]
看看
[解决办法]
学习
[解决办法]
up
[解决办法]
多谢楼主..
[解决办法]
up
[解决办法]
THK~!

[解决办法]
顶了
[解决办法]
收藏,楼主对C++的流了解得很深入。
[解决办法]
真好,upup
[解决办法]
MARK.
[解决办法]
探讨
UP一下

[解决办法]
探讨
up一下

[解决办法]
好复杂,看不懂....
[解决办法]
mark下
------解决方案--------------------


探讨
收藏,楼主对C++的流了解得很深入。

[解决办法]
up
学习 并收藏ing
[解决办法]
up
[解决办法]
我觉得用outputdebugstring也不错,,,在弄个dgbview看就可以了.
[解决办法]
up
[解决办法]
好东西,收藏,谢谢!
[解决办法]
探讨
UP一下

[解决办法]
探讨
up,学习下!

[解决办法]
花时间看看,UP!
[解决办法]
学习。。up
[解决办法]
up
[解决办法]
学习了呀!UP
[解决办法]
up,不懂
[解决办法]
我希望能这样

1.exe > debug.txt

直接把调试输出到debug.txt里面,然后我打开来看。。
[解决办法]
探讨
我希望能这样

1.exe > debug.txt

直接把调试输出到debug.txt里面,然后我打开来看。。

[解决办法]
up 学习中
[解决办法]
支持下
[解决办法]
UP,UP,不错的想法。
[解决办法]
学习了,谢谢。。。
[解决办法]
探讨
up

[解决办法]
up
[解决办法]
探讨
up

[解决办法]
// Log.h: interface for the CLog class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_LOG_H__CCD52280_87C9_4325_9AB2_BD62099CD0F8__INCLUDED_)
#define AFX_LOG_H__CCD52280_87C9_4325_9AB2_BD62099CD0F8__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <fstream>
#include <sstream>
#include <string>
#include <limits>
#include "LogStream.h"
#include "synTools.h"

using namespace std;
#undef max
#undef min
static const char* DayOfWeek[] = {" ", "7", "1", "2", "3", "4", "5", "6"};
#defineMAX_SIZE(1024 * 1024 * 1)

////////////////////////////////////////////////////////////////////
/*
输出类型:
* 由target_t参数化,即可以输出到任意目标,如文件(默认就是ofstream)、
控件(如richedit、ListBox, 已经被实现,详见CLogTargetListBox、CLogTargetRichEdit);


* 如果需要扩展输出类型,可参考CLogTargetRichEdit, 只需实现 operator<< (LPCSTR)的接口即可。

输出支持的类型:
* 所有对stringstream有效的类型(int、 DWORD等)、函数(如endl)
* 输出当前时间:log << ldt;ldt解释:log datetime的简写
* log << CString对象; 
* 输出当前代码的文件名、文件行:log << lfl;lfl解释:log file line的简写
* 输出到目标: log << lfh;lfh解释:log flush的简写;因为此类从stringstream派生,
所以除此输出动作外,所有的输出动作都只是被放在缓冲区内,而 << lfh则真正才输出到target_t目标对象。
* 输出当前系统状态 log << llastErr;

目前存在问题:
* log << 非CLog扩展类型 << CString对象; ------- log << 非CLog扩展类型对象调用的是iostream& operator<< (const T& t )
,返回的是iostream& ,对后面的CString对象调用不到template<typename T>CLog<T>& operator<<(CLog<T>& out, const CString& str) 
所以只能是扩展类型和stringstream有效的类型分开来写,或stringstream有效的类型写在后。

?解决:class CLog :private stringstream, 再重写operator<<?

未来版本特性:
* 增加warning等级
* 增强配置,输出配置
* 输出到richedit/Listbox可根据不同等级显示不同颜色(需要派生CTRL以支持此特性)

*/
////////////////////////////////////////////////////////////////////
//通用类型
struct normal_type{
normal_type(){}
};
template<typename T>
struct log_train{
typedef typename normal_type traint_type;
};

//时间
struct log_time{};
struct log_train<log_time>{typedef log_time traint_type;};
//支持CString
struct log_train<CString>{ typedef CString traint_type;};
//支持flush
struct log_flush{};
struct log_train<log_flush>{ typedef log_flush traint_type;};
//函数指针
//typedef self_t& (__cdecl *PFN)(self_t&);
//struct log_train<PFN>{ typedef PFN train_type;};

typedef UINT size_type;
struct CLogTargetListBox 
{
CLogTargetListBox(LPTSTR lpName, size_type maxSize = MAX_SIZE
, int mode = ios::app){}
void SetCtrl(CListBox* pListBox ){ m_pListBox = pListBox;}
CLogTargetListBox& operator<<(LPCTSTR lpLog)
{
if(m_pListBox && ::IsWindow(m_pListBox->m_hWnd))
{
m_pListBox->AddString(lpLog);
}
return *this;
}
public:
CListBox* m_pListBox;
};

struct CLogTargetRichEdit
{
CLogTargetRichEdit(LPTSTR lpName=0, size_type maxSize = MAX_SIZE
, int mode = ios::app){}
~CLogTargetRichEdit(){
}
void SetCtrl(CRichEditCtrl* pCtrl){ m_pCtrl = pCtrl;}
CLogTargetRichEdit& operator<<(LPCTSTR lpLog)
{
if(!m_pCtrl || !::IsWindow(m_pCtrl->m_hWnd))
return *this;
const long Sel = numeric_limits<long>::max();
m_pCtrl->SetSel(Sel, Sel);
m_pCtrl->ReplaceSel(lpLog);
m_pCtrl->LineScroll(-1, 0);
return *this;
}
public:
CRichEditCtrl* m_pCtrl;
};
//#define LOG_STREAM
#ifdef LOG_STREAM
typedef ofstreamlog_ostream;
#else 
//typedef CLogTargetListBoxlog_ostream;
typedef CLogTargetRichEditlog_ostream;
#endif
//////////////////////////////////////////////////////////////////
//输出target_t可以是文件ofstream、out(ostream),还可以是ListBox、richedit\edit等控件,等等
//target_t只要求支持operator<<(LPCTSTR)、ctor接口.
template<typename target_t = ofstream>
class CLog //:public stringstream
{
public:
typedef CLog<target_t> self_t;
typedef self_t OutStream;//stringstream OutStream;//

typedef basic_ios<char>::pos_type pos_type;
typedef unsigned int size_type;
public:
explicit CLog()
{
}
~CLog(){};
public:
void openfile(LPTSTR lpName = 0, size_type maxSize = MAX_SIZE
, int mode = ios::app )
//m_fileName(lpName)
//m_target(lpName, mode)
//, m_MaxSize(maxSize)
{
m_target.open(lpName, mode);
//ASSERT(lpName);
//CheckSize();
}
//输出通用数据类型对象
template <typename T> 


OutStream&operator<< (const T& t ){
return Output(t, log_train<T>::traint_type());
}
//输出函数指针对象:
self_t& operator<<(self_t& (__cdecl *pf)(self_t&)){ return (*pf)(*this); }
public:
//通用类型
template<typename T>
OutStream& Output(const T& t, normal_type)
{
m_strstream << t;
return (*this);
}
//时间
template<typename T>
OutStream& Output(const T& t, log_time)
{
return SetDataTime();
}
//CString
template<typename T>
OutStream& Output(const T& t, CString)
{
(*this) << (LPCSTR)t;
return (*this);
}
OutStream& flush()
{
m_target << (LPCTSTR)m_strstream.str();//.c_str();
//如是ListBox ctrl则可能使用以下代码,每到换行就AddString
//string str;
//while(getline((*this) , str))
//{
//str += "\n";
//m_target << str.c_str();
//}
m_strstream.clear();
m_strstream.str(""); //clear
return (*this);
}
OutStream& SetDataTime()
{
CTime dt = CTime::GetCurrentTime();
(*this) << dt.GetYear() << "-" << dt.GetMonth() << "-" << dt.GetDay()
<< '(' << DayOfWeek[dt.GetDayOfWeek()] << ") "
<< dt.GetHour() << ':'
<< dt.GetMinute() << ':'
<< dt.GetSecond() << '\t';
return (*this);
}

protected:
//void Clear()
//{
//(*this).close();
//(*this).open(m_fileName.c_str(), ios::trunc);
//}
//void CheckSize()
//{
//(*this).seekp(0, ios_base::end);
//pos_type pos = tellp();
//if(m_MaxSize < pos)
//Clear();
//}
//pos_type tellp(){ return (*this).tellp();}
private:
//size_type m_MaxSize;
//string m_fileName;
public:
target_t m_target;
CCrtlSection m_cs;
//stringstream m_strstream;
LogStream<char> m_strstream;
};

typedef CLog<CLogTargetRichEdit> EditLog;
typedef CLog<ofstream> FLog;

/*
//通用类型
template <typename TStream, typename TOutput> 
static CLog<TStream>&operator<< (CLog<TStream>&stream, const TOutput& t ){
return stream.Output(t, log_train<TOutput>::traint_type());
}
*/

//支持输出时间格式: lg << ldt(log datetime)----------------------------------------------------
template<typename T>
static inline CLog<T>& __cdecl ldt(CLog<T>& log)
{
return log.Output(log, log_train<log_time>::traint_type());
}

//--------------------------------------------------------------------
//为什么lg << endl不会调用成员函数self_t& operator<<(self_t& (__cdecl *pf)(self_t&))?/*
//*/
template<typename T>
static inline CLog<T>& __cdecl endl(CLog<T>& log)
{
return log << "\n";
}
static inline CLog<ofstream>& __cdecl endl(CLog<ofstream>& log)
{
return log << "\n";
}
static inline CLog<CLogTargetRichEdit>& __cdecl endl(CLog<CLogTargetRichEdit>& log)
{
return log << "\n";
}
//*/

//支持lg << lfh (log.flush())----------------------------------
template<typename T>
static inline CLog<T>& __cdecl lfh(CLog<T>& log)
{
return log.flush();
}



[解决办法]
//支持输出代码文件的当前行:lg << lfl (lg << __)----------------------------------


//#definelfl(CString(__FILE__) + "(" + CString(__LINE__ )+ (")"))
#definelfl __FILE__ << "(" << __LINE__ << ")"
//以下函数不能inline所以__FILE__ __LINE__得到的是当前文件的位置,所以改为上面的宏
//template<typename T>
//static __forceinline CLog<T>& __cdecl lfl(CLog<T>& log)
//{
//log << __FILE__ << "(" << __LINE__ << ")";
//return log;
//}

//支持输出当前系统状态 log << llastErr;
template<typename T>
static inline CLog<T>& __cdecl llastErr(CLog<T>& log)
{
LPSTR lpMsgBuf;
DWORD lasterrno = GetLastError();

FormatMessage( 
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
lasterrno ,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL 
);

//sprintf(Temp, "WARNING: Failed with the following error: \n%s\n", lpMsgBuf); 
//MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);
log << "[WARNING] errno(" << lasterrno << "):" << lpMsgBuf ;
LocalFree(lpMsgBuf);

return log;
}

/*
//支持CString不必作(LPCTSTR)转换----------------------------------
template<typename T>
CLog<T>& operator<<(CLog<T>& out, const CString& str) {
out << (LPCTSTR)str;
return out;
}
*/
//支持输出换行:lg << lendl(log endline), 输出换行时,同时正真写到目标(target_t)中
//支持输出当前文件名、行号:lg << lfl(log file line -- __FILE__, __LINE__)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef CLog<log_ostream> CLogEx;
extern "C" CWBYLIBDLLEXPORT CLogEx lg; //log和math.h的log冲突

#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif

#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif

#endif // !defined(AFX_LOG_H__CCD52280_87C9_4325_9AB2_BD62099CD0F8__INCLUDED_)
[解决办法]
学习学习........
[解决办法]
我是我自己写的LOG类,可通过指定target_t 类型,输出到CRichedit ctrl、文件、CListCtrl
[解决办法]
输出到文件:
CLog<ofstream> lg;
lg.openfile(...);
基本用法:

CGuard g(lg.m_cs); //多线程的同步
lg << ldt //输入当前时间 yyyy-mm-dd hh:mm:ss(星期X)
<< lfh //当前代码文件名、文件行号
<< llastErr //当前系统状态
<< CString("MFC String")
<< endl //换行
<< lfh; //输出


原来的CLog从stringstream派生,是为了支持<<操作,但在服务器的多线程环境中使用发现有BUG,老是出现内存异常,很郁闷,所以CLog里字符串改用LogStream,而LogStream使用了MFC的CString,改后,服务器再不再死掉。


[解决办法]
头文件tchar.h是什么?
标准库没有,是那个库带的?
[解决办法]
可定制、扩展的几个地方:
1、定制target_t,以输出到控件、文件、或其它地方,只要提供target_t& operator<<(LPCTSTR )接口的类,都可以作为target_t类,甚至可以输出到网络socket;

2、通过C++的train,可以扩展各种自定义函数或类型如 已经实现的了ldt ,lfh, llastErr ,CString

缺点:
1、不能同时输出到多个目标,如不能同时输出到文件和socket,有时我想通过网络SOCKET来远程查看调试信息,还能同时输出到文件,这个目前没有时间去弄了。
2、还不支持LOG的告警等级,
3、...


[解决办法]
多线程中使用stringstream有BUG,所以用LogStream仿照了stringstream的接口


// LogStream.h: interface for the LogStream class.
//
//////////////////////////////////////////////////////////////////////



#if !defined(AFX_LOGSTREAM_H__0F1805C4_EB7A_4A2C_9C40_F38230C058AB__INCLUDED_)
#define AFX_LOGSTREAM_H__0F1805C4_EB7A_4A2C_9C40_F38230C058AB__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <list>
using namespace std;

template <class E = char, class T = char_traits<E> >
class LogStream
{
public:
LogStream(){};
virtual ~LogStream(){};
public:
//LogStream& operator<<(LogStream& (*pf)(LogStream&));
//LogStream& operator<<(basic_ios<E, T>& (*pf)(basic_ios<E, T>&));
//LogStream& operator<<(ios_base<E, T>& (*pf)(ios_base<E, T>&));
//LogStream& operator<<(basic_streambuf<E, T> *sb);

LogStream& operator<<(const E *s)
{
m_string += s;
return *this;
}
LogStream& operator<<(char c)
{
CString str;
str.Format("%c", c);
m_string += str;
return *this;
}
LogStream& operator<<(bool n)
{
CString str;
str.Format("%s", n ? "true" : "false");
m_string += str;
return *this;
}
LogStream& operator<<(short n)
{
CString str;
str.Format("%d", n);
m_string += str;
return *this;
}
LogStream& operator<<(unsigned short n)
{
CString str;
str.Format("%d", n);
m_string += str;
return *this;
}
LogStream& operator<<(int n)
{
CString str;
str.Format("%d", n);
m_string += str;
return *this;
}
LogStream& operator<<(unsigned int n)
{
CString str;
str.Format("%d", n);
m_string += str;
return *this;
}
LogStream& operator<<(long n)
{
CString str;
str.Format("%d", n);
m_string += str;
return *this;
}
LogStream& operator<<(unsigned long n)
{
CString str;
str.Format("%d", n);
m_string += str;
return *this;
}
LogStream& operator<<(float n)
{
CString str;
str.Format("%f", n);
m_string += str;
return *this;
}
LogStream& operator<<(double n)
{
CString str;
str.Format("%f", n);
m_string += str;
return *this;
}
LogStream& operator<<(long double n)
{
CString str;
str.Format("%lf", n);
m_string += str;
return *this;
}

//LogStream& operator<<(void * n);
public:
const CString& str()
{
return m_string;
}
void clear()
{
}
//void str(const basic_string<E, T>& str)
void str(const char* str)
{
m_string = str;
}
protected:
//typedef list<basic_string<E, T> > TStrings;
//TStrings m_strings;
//basic_string<E, T> m_string;
CString m_string;
};

#endif // !defined(AFX_LOGSTREAM_H__0F1805C4_EB7A_4A2C_9C40_F38230C058AB__INCLUDED_)

[解决办法]
study~
[解决办法]
up
[解决办法]

之后就可以方便的使用wcout或cout进行GDI程序的调式了,写入wcout或cout的字符串将全部显示在VS.net的“输出”窗口中。 [/Quote

[Quote=引用:]
引用:
我希望能这样

1.exe > debug.txt

直接把调试输出到debug.txt里面,然后我打开来看。。


这个想法好。

在MFC中可以用TRACE 输出到output窗口。
用Dump可以输出文件,有兴趣可以参阅MFC相关的资料。



[解决办法]
up,学习下!
[解决办法]
up
[解决办法]
探讨
windows的文本编辑器你不可能去控制它,但VC不是提供了一个可刷新的“输出”调试窗口么?用OutputDebugString这个API就可以向里面写入东东了。我最初的意图就是让OutputDebugString也可以方便的输出数字之类的非字符串变量。后来发现可以通过设计一个回调函数让调试的用户有更自由的选择,用户只要自己定义一个回调函数作为构造参数传进去,就可以实现将调试信息写入任何设备的功能了。多线程是根本没想过的,因为这只是为STL的IO流做了一个扩展,是否线程安全当然还要看STL自身的情况,当然我们可以把这个类放在任何标准STL库(或许被实现为线程安全的)的环境中使用,代码完全符合平台移植要求。

[解决办法]
用回调和函数对象,其实一样
[解决办法]
就是因为stringstream还是string在多线程中有BUG,所以才搞得有点复杂,绕了很多弯.
[解决办法]
设计是复杂点,但用起来仍是很方便。
[解决办法]
学习下
[解决办法]
基础不够,看不明白!- -!
[解决办法]
STLPort 什么时候是线程安全了?他们自己也只是说允许并发只读访问……
[解决办法]
现在用不上 不过还是帮忙顶下
[解决办法]
up
[解决办法]
好贴,顶啊
[解决办法]
呵呵,厉害,看不懂
估计以后应该就能看懂了
[解决办法]
UP
[解决办法]
不懂,UPUP
[解决办法]
up
[解决办法]
g g study
[解决办法]
up ~up~
[解决办法]
我操作string当然是加了锁的。
我记得是vc6.0的string实现有BUG,MS还给出地址,但不记得什么地址了。
到vs2003才得到修复。