VC 中的一些宏


预处理命令之条件编译(#ifdef,#else,#endif,#if等):

预处理就是在进行编译的第一遍词法扫描和语法分析之前所作的工作。说白了,就是对源文件进行编译前,先对预处理部分进行处理,然后对处理后的代码进行编译。这样做的好处是,经过处理后的代码,将会变的很精短。
   关于预处理命令中的文件包含(#include),宏定义(#define),条件编译(#ifdef,#else,#endif,#if等)

 

1、概述

 
C++中出了const关键字以后,宏定义常量的功能已经不在被推荐使用。这使得宏似乎没有了用武之地。实际上,宏还可以做很多事情,笔者也难以全部列举。这里,仅仅列举几个典型的用法,希望大家能够从中获益。

2、实现多环境兼容


常见的情况是,我们实现了一个函数,希望它只在某种编译条件满足是被编译和使用。例如,我希望在源码中插入调试语句,以便以Debug方式运行时能够通过调试信息观察程序运行情况。但是,在产品发售给用户时,我又希望这些调试信息不要输出,以降低代码尺寸,提高运行性能。 这一问题的解决方法就是使用宏。根据条件编译指令,对于不同的编译条件,提供不同的实现。例如:我们希望在特定的位置向日志中写入当前行号和文件名,以判断对应代码是否被执行到,可以使用下面的宏:
#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__宏可以起作用,如果用函数实现,这两个宏则不能起作用。

3、用新函数替换原有函数



对于一个设计好的函数,假设它已经在一个很大的工程中到处使用,突然发现它的一个不足,想修改它的功能。也许这个新增加的功能需要一个额外的参数,但是又不想修改使用这些函数的地方。 假设有两个函数必须成对使用,一个占用资源并使用,另外一个则释放资源以供其他模块使用。典型的例子是,函数一(假设为Lock)获得一个全局的锁,这个锁用于保护在多线程情况下多个线程对一个公共资源如一个全局变量的访问。问题是,这个Lock函数获得锁以后,其他线程将不能再获得这个锁,直到当前线程释放这个锁。编制Lock函数的程序员同时提供了一个 Unlock函数用于释放锁,并要求使用Lock的人必须对应的使用Unlock。调试程序时,发现线程被死锁,怀疑有人使用完Lock后忘记调用 Unlock,但是Lock和Unlock在这个大工程中都被广泛的使用,因此设计者希望Lock和Unlock都增加两个额外的参数file和line,以说明这两个函数在哪里被调用了,哪些地方被死锁以及哪些地方调用了Lock但是没有调用Unlock。 假设这两个函数的原型为:
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。



 4、给一个函数捆绑其他功能


上述方法修改了原来函数的设计。实际上,这两个函数本身没有问题,只是使用者使用上出了问题。你可能只需要在调试版本中测试到底谁遗漏了这些重要信息。对于一些严谨的公司,一旦软件被修改,推出销售前就需要进行严格的测试。因此项目经理可能不会允许修改原有函数的设计,要求直接捆绑一个测试代码。产品发售时,删除捆绑代码即可。 使用宏也可以捆绑代码,这需要首先了解一个宏的特点:如果你的代码中出现了一个字符串,编译器会首先匹配宏,并试图用宏展开。这样,即使你有同名的函数,它也不会被当作函数处理。但是,如果一个宏展开时发现,展开式是一个嵌套的宏展开,展开式就试图在进入下一次嵌套展开之前,试图用函数匹配来终止这种无限循环。 为此,定义如下两个宏:

#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  is called
        Lock();
它首先把这个Lock理解成宏函数,展开成:
//here the Lock  is called
        Lock();
        TRACE("Lock called in file = %s at line = %u/n",__FILE__,__LINE__);

上述代码中,__FILE__和__LINE__应该同时被展开,由于与论题无关,所以还是原样给出。展开以后,Lock还是一个和宏匹配的式子,但是编译器发现如果这样下去,它将是一个无休止的迭代,因此它停止展开过程,讯中同名的函数,因此上面的代码已经是最终展开式。 这样,我们成功的不改变Lock函数的原型和设计,捆绑了一条调试信息上去。由于TRACE语句在Release版本中不会出现,这样也避免了不得不进行额外的测试过程。 


 5、实现一些自动化过程


程序中需要输入一组参数,为此设计了一个对话框来输入。问题是:每次显示对话框时,都希望能按照上次输入的值显示。设计当然没有问题,在文档中保存输入的参数,在显示对话框前在把保存的值赋值给对话框对应控制变量。下面是常见的代码:
CMyDoc * pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    
    CParameterDlg dlg;
    //设置对话框初值
    dlg.m_n1   = pDoc->m_n1;
    dlg.m_sz2  = pDoc->m_sz2;
    ......
    dlg.m_ln   = pDoc->m_ln;
    //显示对话框
    if(dlg.DoModal() == IDOK)
    {
       //点击OK按钮后保存设置
        pDoc->m_n1  = dlg.m_n1;
        pDoc->m_sz2 = dlg.m_sz2;
            ......
        pDoc->m_ln  = dlg.m_ln;
    }
如果整个程序只有一两个这样的代码段,并且每个代码段涉及的变量个数都很少,当然没有问题,但是当你程序中有成百上千个这样的参数对话框,每个对话框又对应数十个这样的参数,工作量就非常可观了(而且是没有任何成就感的工作量)。我想,用VC做界面的朋友们大多遇到过这样的问题。可以注意到,上述代码在DoModal前后都是一组赋值过程,但是赋值的方向不是很一致,因此每个变量对都需要写两个赋值语句。那么是否可以做一个函数,前后各调用一次,根据一个参数决定方向。而且函数中也只需要对每个变量写一次?
下面这个函数就是一个实现:
void DataExchange(CMyDoc * pMyDoc,CParameterDlg * pDlg,BOOL flag )
    {
        BEGIN_EXCHANGE(pMyDoc,CMyDoc,pDlg,CParameterDlg,flag)
        EXCHANGE(m_n1);
        EXCHANGE(m_sz2);
                ....
        EXCHANGE(m_l2);
        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_l1,m_dw2);
上述代码看起来是完美的,但是有一些特殊还是不正确,这些特殊情况就是=用于赋值不正确的情况。

有两种常见问题:
  1. leftvar和rightvar分别是指针类型,但是其实想拷贝它们指向的缓冲区的内容(如字符串拷贝)。
  2. 为了控制显示精度,对话框控制变量是一个CString对象,它是文档对象中对应变量的格式化后的信息。最常见的是, leftvar是一个浮点数,需要以几个小数位格式输出,因此rightvar是一个CString对象。
为了实现上面的目的,就不能使用=来直接赋值,而应该用一个函数Assign(函数名当然可以任意取啦)来做这件事。为此,修改上述的EXCHANGE和EXCHANGE2宏如下:
#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 & f)格式化float数值到left
void Assign(float & f,CString & right)从字符串中读取出float
void Assign(CString & left, double& d)格式化double数值到left
void Assign(double& d,CString & right)从字符串中读取出double
void Assign(CString & left, int & i)格式化int数值到left
void Assign(int & i,CString & right)从字符串中读取出int
void Assign(CString & left, short& s)格式化short数值到left
void Assign(short & s,CString & right)从字符串中读取出short
void Assign(CString & left, long & l)格式化long数值到left
void Assign(long & l,CString & right)从字符串中读取出long
void Assign(CString & left, CTime & time)格式化CTime数值到left
void Assign(CTime & time,CString & right)从字符串中读取出CTime

 

 

 

问题一: #if ,#ifdef与#if define的区别

 

对于条件编译#if,#ifdef和#if defined这些,深究起来还是有些意思

#if要求是一个表达式,为真则执行以下代码,

#ifdef则只要求是否定义,并不关心这个值是什么。

#if defined则是#if和#define的连用。

比如:

#if 0
/* some code here*/
/* commented */
#endif

#ifdef __MMI_MESSAGES_CLUB__
#include "MessagesResDef.h"
#endif

#if defined( __MMI_IRDA_SUPPORT__ )
#include "ConnectivityResDef.h "
#endif

#if defined还可以串连多个条件:

#if defined( __MMI_MYAPP1_SUPPORT__ ) || defined( __MMI_MYAPP2_SUPPORT__ )
#include "ConnectivityResDef.h "
#endif

 

问题二:VC++预定义宏

 

在MSDN中输入“__LINE__”,就可以进入“Predefined Macros”的相关界面。

我这里介绍一下它的简单用法(在写程序的日志文件时很有用,这里用MessageBox来替代写文件)

#include <afx.h>
#include <iostream>

using namespace std;

//
//主函数部分
int main()
{
string str = __FILE__;//当前文件名
int i = __LINE__;//当前所在行数
string time = __TIME__;//系统时间 (时、分、秒)
string timestamp = __TIMESTAMP__;//系统时间 (年月日小时分秒)

CString sss;
sss = str.c_str();    //将string类型转换成CString类型
MessageBox(NULL,sss,"Test",MB_OK);

cout<<i<<endl;

sss = time.c_str();
MessageBox(NULL,sss,"Test",MB_OK);

sss = timestamp.c_str();
MessageBox(NULL,sss,"Test",MB_OK);

char ch;
cin>>ch;

return 0;
}

====================================================================

这些宏有:

Predefined Macros

The compiler recognizes six predefined ANSI C macros (see Table 1.1), and the Microsoft C++ implementation provides several more (see Table 1.2). These macros take no arguments and cannot be redefined. Their value (except for __LINE__ and __FILE__) must be constant throughout compilation. Some of the predefined macros listed below are defined with multiple values. Their values can be set by selecting the corresponding menu option in the Visual C++ development environment, or by using a command-line switch. See the tables below for more information.

Table 1.1    ANSI Predefined Macros

MacroDescription
__DATE__The compilation date of the current source file. The date is a string literal of the form Mmm dd yyyy. The month name Mmm is the same as for dates generated by the library function asctime declared in TIME.H.
__FILE__The name of the current source file. __FILE__ expands to a string surrounded by double quotation marks.
__LINE__The line number in the current source file. The line number is a decimal integer constant. It can be altered with a #line directive.
__STDC__Indicates full conformance with the ANSI C standard. Defined as the integer constant 1 only if the /Za compiler option is given and you are not compiling C++ code; otherwise is undefined.
__TIME__The most recent compilation time of the current source file. The time is a string literal of the form hh:mm:ss.
__TIMESTAMP__The date and time of the last modification of the current source file, expressed as a string literal in the form Ddd Mmm Date hh:mm:ss yyyy, where Ddd is the abbreviated day of the week and Date is an integer from 1 to 31.

 

Table 1.2    Microsoft-Specific Predefined Macros

MacroDescription
_CHAR_UNSIGNEDDefault char type is unsigned. Defined when /J is specified.
__cplusplusDefined for C++ programs only.
_CPPRTTIDefined for code compiled with /GR (Enable Run-Time Type Information).
_CPPUNWINDDefined for code compiled with /GX (Enable Exception Handling).
_DLLDefined when /MD or /MDd (Multithread DLL) is specified.
_M_ALPHADefined for DEC ALPHA platforms. It is defined as 1 by the ALPHA compiler, and it is not defined if another compiler is used.
_M_IX86Defined for x86 processors. See Table 1.3 for more details.
_M_MPPCDefined for Power Macintosh platforms. Default is 601 (/QP601). See Table 1.4 for more details.
_M_MRX000Defined for MIPS platforms. Default is 4000 (/QMR4000). See Table 1.5 for more details.
_M_PPCDefined for PowerPC platforms. Default is 604 (/QP604). See Table 1.6 for more details.
_MFC_VERDefines the MFC version. Defined as 0x0421 for Microsoft Foundation Class Library 4.21. Always defined.
_MSC_EXTENSIONSThis macro is defined when compiling with the /Ze compiler option (the default). Its value, when defined, is 1.
_MSC_VERDefines the compiler version. Defined as 1200 for Microsoft Visual C++ 6.0. Always defined.
_MTDefined when /MD or /MDd (Multithreaded DLL) or /MT or /MTd (Multithreaded) is specified.
_WIN32Defined for applications for Win32®. Always defined.

As shown in following tables, the compiler generates a value for the preprocessor identifiers that reflect the processor option specified.

Table 1.3    Values for _M_IX86

Option in Developer StudioCommand-Line OptionResulting Value
Blend/GB_M_IX86 = 500 (Default. Future compilers will emit a different value to reflect the dominant processor.)
Pentium/G5_M_IX86 = 500
Pentium Pro/G6_M_IX86 = 600
80386/G3_M_IX86 = 300
80486/G4_M_IX86 = 400

 

Table 1.4    Values for _M_MPPC

Option in development environmentCommand-Line OptionResulting Value
PowerPC 601/QP601_M_MPPC = 601 (Default)
PowerPC 603/QP603_M_MPPC = 603
PowerPC 604/QP604_M_MPPC = 604
PowerPC 620/QP620_M_MPPC = 620

 

Table 1.5    Values for _M_MRX000

Option in Developer StudioCommand-Line OptionResulting Value
R4000/QMR4000_M_MRX000 = 4000 (Default)
R4100/QMR4100_M_MRX000 = 4100
R4200/QMR4200_M_MRX000 = 4200
R4400/QMR4400_M_MRX000 = 4400
R4600/QMR4600_M_MRX000 = 4600
R10000/QMR10000_M_MRX000 = 10000

 

Table 1.6    Values for _M_PPC

Option in Developer StudioCommand-Line OptionResulting Value
PowerPC 601/QP601_M_PPC = 601
PowerPC 603/QP603_M_PPC = 603
PowerPC 604/QP604_M_PPC = 604 (Default)
PowerPC 620/QP620_M_PPC = 620

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值