【原创】一个很好用的调试辅助类,使用需要一定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!
[解决办法]
拜lz~~
[解决办法]
up up
JF
[解决办法]
up
我研究下大师的杰作
[解决办法]
ding
[解决办法]
up
[解决办法]
不明白,研究研究!
[解决办法]
我来UP
[解决办法]
[解决办法]
UP一下
[解决办法]
up
[解决办法]
mark
[解决办法]
up
[解决办法]
up
[解决办法]
up
[解决办法]
感觉不好用。感觉麻烦、复杂。不过代码比较强。支持一下。
[解决办法]
up
[解决办法]
看看
[解决办法]
学习
[解决办法]
up
[解决办法]
多谢楼主..
[解决办法]
up
[解决办法]
THK~!
[解决办法]
顶了
[解决办法]
收藏,楼主对C++的流了解得很深入。
[解决办法]
真好,upup
[解决办法]
MARK.
[解决办法]
[解决办法]
[解决办法]
好复杂,看不懂....
[解决办法]
mark下
------解决方案--------------------
[解决办法]
up
学习 并收藏ing
[解决办法]
up
[解决办法]
我觉得用outputdebugstring也不错,,,在弄个dgbview看就可以了.
[解决办法]
up
[解决办法]
好东西,收藏,谢谢!
[解决办法]
[解决办法]
[解决办法]
花时间看看,UP!
[解决办法]
学习。。up
[解决办法]
up
[解决办法]
学习了呀!UP
[解决办法]
up,不懂
[解决办法]
我希望能这样
1.exe > debug.txt
直接把调试输出到debug.txt里面,然后我打开来看。。
[解决办法]
[解决办法]
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
[解决办法]
[解决办法]
up,学习下!
[解决办法]
up
[解决办法]
[解决办法]
用回调和函数对象,其实一样
[解决办法]
就是因为stringstream还是string在多线程中有BUG,所以才搞得有点复杂,绕了很多弯.
[解决办法]
设计是复杂点,但用起来仍是很方便。
[解决办法]
学习下
[解决办法]
基础不够,看不明白!- -!
[解决办法]
STLPort 什么时候是线程安全了?他们自己也只是说允许并发只读访问……
[解决办法]
现在用不上 不过还是帮忙顶下
[解决办法]
up
[解决办法]
好贴,顶啊
[解决办法]
呵呵,厉害,看不懂
估计以后应该就能看懂了
[解决办法]
UP
[解决办法]
不懂,UPUP
[解决办法]
up
[解决办法]
g g study
[解决办法]
up ~up~
[解决办法]
我操作string当然是加了锁的。
我记得是vc6.0的string实现有BUG,MS还给出地址,但不记得什么地址了。
到vs2003才得到修复。
