"); //-->
#ifdef _DEBUG #define TRACE_FILE_LINE_INFO() do{\ CString str;\ str.Format(_T("file=%s,line=%u\r\n",__FILE__,__LINE__);\ CFile file("logfile.txt");\ file.Write(str,str.GetLength());\ }while(0) #else #define TRACE_FILE_LINE_INFO() #endif上面这段代码通过#ifdef #else #endif三个条件编译指令,根据_DEBUG定义情况(该宏用于区分DEBUG版本和Release版本),决定了具体的TRACE_FILE_LINE_INFO宏函数的实现。使用者可以用如下方法使用
TRACE_FILE_LINE_INFO();//这里显示行号和文本信息当然,采用其他方式也可以实现这一功能,但是使用宏有以下特殊好处: 只有需要的代码才会被编译,减少了符号表的尺寸,也减少了代码尺寸 宏在编译时被展开,因此用于表示代码位置的__FILE__,__LINE__宏可以起作用,如果用函数实现,这两个宏则不能起作用。
void Lock(); void Unlock();新设计的函数的原型是:
void Lock(LPCTSTR szFileName,UINT uLineNo); void Unlock(LPCTSTR szFileName,UINT uLineNo);设计完新的函数后,项目经理希望所有模块统一使用这两个函数并提供文件名和行号信息作为参数。这样将是一个非常浩大且烦琐的工作,意味着重复性的劳动、数小时无聊的加班和工期的延误,这是谁都不愿意遇到的。 使用宏可以非常轻松的解决这一切。首先,应该把新设计的函数换个名字,不妨叫它们NewLock和NewUnlock,也就是他们的原型为:
void NewLock(LPCTSTR szFileName,UINT uLineNo); void NewUnlock(LPCTSTR szFileName,UINT uLineNo);这个函数原型应该放在一个头文件中,避免在多个地方重复的声明。需要用到这两个函数的cpp文件,只要包含他们原型所在的头文件即可。为了不改动使用Lock/Unlock函数的模块,在头文件中增加如下两行:
#define Lock() NewLock(__FILE__,__LINE__) #define Unlock() NewUnlock(__FILE,__LINE__)这样,当不同模块使用这个函数时,宏替换功能在编译时起作用,自动使用了__FILE__和__LINE__为参数,调用了新设计的函数。调试的时候就可以根据日志来判断什么地方遗漏了调用Unlock。
#define Lock() Lock();\ TRACE("Lock called in file = %s at line =%u\n",__FILE__,__LINE__) #define Unlock() Unlock();\ TRACE("Unlock called in file = %s at line =%u\n",__FILE__,__LINE__)编译器在编译过程中,发现如下代码
//here the Lock function is called Lock();它首先把这个Lock理解成宏函数,展开成:
//here the Lock function is called Lock(); TRACE("Lock called in file = %s at line = %u\n",__FILE__,__LINE__);上述代码中,__FILE__和__LINE__应该同时被展开,由于与论题无关,所以还是原样给出。展开以后,Lock还是一个和宏匹配的式子,但是编译器发现如果这样下去,它将是一个无休止的迭代,因此它停止展开过程,讯中同名的函数,因此上面的代码已经是最终展开式。 这样,我们成功的不改变Lock函数的原型和设计,捆绑了一条调试信息上去。由于TRACE语句在Release版本中不会出现,这样也避免了不得不进行额外的测试过程。
CMyDoc * pDoc = GetDocument(); ASSERT_VALID(pDoc); CParameterDlg dlg; //设置对话框初值 dlg.m_nValue1 = pDoc->m_nValue1; dlg.m_szValue2 = pDoc->m_szValue2; ...... dlg.m_lValuen = pDoc->m_lValuen; //显示对话框 if(dlg.DoModal() == IDOK) { //点击OK按钮后保存设置 pDoc->m_nValue1 = dlg.m_nValue1; pDoc->m_szValue2 = dlg.m_szValue2; ...... pDoc->m_lValuen = dlg.m_lValuen; }如果整个程序只有一两个这样的代码段,并且每个代码段涉及的变量个数都很少,当然没有问题,但是当你程序中有成百上千个这样的参数对话框,每个对话框又对应数十个这样的参数,工作量就非常可观了(而且是没有任何成就感的工作量)。我想,用VC做界面的朋友们大多遇到过这样的问题。可以注意到,上述代码在DoModal前后都是一组赋值过程,但是赋值的方向不是很一致,因此每个变量对都需要写两个赋值语句。那么是否可以做一个函数,前后各调用一次,根据一个参数决定方向。而且函数中也只需要对每个变量写一次?
void DataExchange(CMyDoc * pMyDoc,CParameterDlg * pDlg,BOOL flag ) { BEGIN_EXCHANGE(pMyDoc,CMyDoc,pDlg,CParameterDlg,flag) EXCHANGE(m_nValue1); EXCHANGE(m_szValue2); .... EXCHANGE(m_lValue2); END_EXCHANGE() }为了使上述语义能起作用,定义上面三个宏如下:
#define BEGIN_EXCHANGE(left,lefttype,right,righttype,flag) \ {\ CSmartPtr<lefttype> pLeft = left;\ CSmartPtr<righttype> pRight = right #define END_EXCHANGE() } #define EXCHANGE(varible) \ if(flag)\ {\ pLeft->varible = pRight->varible ;\ }else{\ pRight->varible = pLeft->varible;\ |这里为了避免每次都输入varible所属对象的指针,使用了一个智能指针来提供一个左指针pLeft和一个右指针pRight语义,这个智能指针只需要实现取下标功能即可,因此可以简单实现如下(为了通用,必须为模板类):
template <typename TYPE> class CSmartPointer { protected: TYPE * m_pPointer; public: CSmartPointer(TYPE * pPointer):m_pPointer(pPointer){}; TYPE* operator->() {return m_pPointer;} };这样,原来的代码就可以修改成这样:
CMyDoc * pDoc = GetDocument(); ASSERT_VALID(pDoc); CParameterDlg dlg; //设置对话框初值 DataExchange(pDoc,&dlg,FALSE); //显示对话框 if(dlg.DoModal() == IDOK) { //点击OK按钮后保存设置 DataExchange(pDoc,&dlg,TRUE); }上述代码要求左右指针对应变量名必须相同,如果变量名不同,就不能这样使用,需要设计成这样的EXCHANGE2宏:
#define EXCHANGE2(leftvar,rightvar) \ if(flag)\ {\ pLeft->leftvar,pRight->rightvar;\ }else{\ pRight->rightvar = pLeft->leftvar;\ }这样,对应的EXCHANGE子句需要修改成
EXCHANGE2(m_lValue1,m_dwValue2);上述代码看起来是完美的,但是有一些特殊还是不正确,这些特殊情况就是=用于赋值不正确的情况。
#define EXCHANGE(var) \ if(flag)\ {\ Assign(pLeft->var,pRight->var);\ }else{\ Assign(pRight->var,pLeft->var);\ } #define EXCHANGE2(leftvar,rightvar) \ if(flag)\ {\ Assign(pLeft->leftvar,pRight->rightvar);\ }else{\ Assign(pRight->rightvar,pLeft->leftvar);\ }这样只要针对每个类型对实现一次Assign即可。由于C++允许重载,这显得很容易。需要实现的函数一般有:
void Assign(CString & left,CString & right) | 直接赋值CString类型 |
void Assign(CString & left, float & fValue) | 格式化float数值到left |
void Assign(float & fValue,CString & right) | 从字符串中读取出float |
void Assign(CString & left, double& dValue) | 格式化double数值到left |
void Assign(double& dValue,CString & right) | 从字符串中读取出double |
void Assign(CString & left, int & iValue) | 格式化int数值到left |
void Assign(int & iValue,CString & right) | 从字符串中读取出int |
void Assign(CString & left, short& sValue) | 格式化short数值到left |
void Assign(short & sValue,CString & right) | 从字符串中读取出short |
void Assign(CString & left, long & lValue) | 格式化long数值到left |
void Assign(long & lValue,CString & right) | 从字符串中读取出long |
void Assign(CString & left, CTime & time) | 格式化CTime数值到left |
void Assign(CTime & time,CString & right) | 从字符串中读取出CTime |
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。