Wednesday, March 30, 2005

 

Some notes on VC setting

I.
Visual C++ 编译参考

Visual C++ 预编译宏参数(包括#define,#error,#import,#undef,#elif,#if,#include,#else,#ifdef,#line,#endif #ifndef,#pragma )
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/prepr.asp

Visual C++ 编译器编译参数(Compiler Options)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_compiler_options_listed_by_category.asp

Visual C++ 连接器连接参数(Linker Options)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_linker_reference.asp

Visual C++ 自定义Build
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcug98/html/_asug_customizing_a_build_process.asp

II.
让VC不再编译帮助文件的方法

来源不祥

VC帮助虽然很好,但有时很烦,每一次编译都会自动编译帮助文件,不理我们有没有修改过RTF文件,耗神又耗时间,实在让人烦。

仔细找了一下,项目文件里没什么特别的文件,但有一个DSP文件,用记事本打开此文件仔细看一下,发现有三个地方是用于处理帮助文件的编译的。祸根正是由这三段代码产生,下面我们一起来来铲除祸根。

祸根一、Making help file...

SOURCE=.\hlp\Test.hpj
!IF "$(CFG)" == "Test - Win32 Release"
# PROP Ignore_Default_Tool 1

USERDEP__TEST_=hlp\AfxCore.rtf hlp\AfxPrint.rtf hlp\$(TargetName).hm

# Begin Custom Build - Making help file...
OutDir=.\Release
TargetName=Test
InputPath=.\hlp\Test.hpj
InputName=Test

"$(OutDir)\$(InputName).hlp" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
start /wait hcw /C /E /M "hlp\$(InputName).hpj"
if errorlevel 1 goto :Error
if not exist "hlp\$(InputName).hlp" goto :Error
copy "hlp\$(InputName).hlp" $(OutDir)
goto :done
:Error

echo hlp\$(InputName).hpj(1) : error:
type "hlp\$(InputName).log"
:done

# End Custom Build

!ELSEIF "$(CFG)" == "Test - Win32 Debug"

# PROP Ignore_Default_Tool 1
USERDEP__TEST_=hlp\AfxCore.rtf hlp\AfxPrint.rtf hlp\$(TargetName).hm
# Begin Custom Build - Making help file...
OutDir=.\Debug
TargetName=Test
InputPath=.\hlp\Test.hpj
InputName=Test

"$(OutDir)\$(InputName).hlp" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
start /wait hcw /C /E /M "hlp\$(InputName).hpj"
if errorlevel 1 goto :Error
if not exist "hlp\$(InputName).hlp" goto :Error
copy "hlp\$(InputName).hlp" $(OutDir)
goto :done
:Error
echo hlp\$(InputName).hpj(1) : error:
type "hlp\$(InputName).log"
:done

# End Custom Build
!ENDIF

# End Source File
# Begin Source File

把此段代码用以下代码替换它,或者干脆删掉它。

SOURCE=.\hlp\Test.hpj

# End Source File
# Begin Source File

祸根二、Making help include file...

SOURCE=.\Resource.h

!IF "$(CFG)" == "Test - Win32 Release"

# PROP Ignore_Default_Tool 1
# Begin Custom Build - Making help include file...
TargetName=Test
InputPath=.\Resource.h

"hlp\$(TargetName).hm" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
echo. >"hlp\$(TargetName).hm"
echo // Commands (ID_* and IDM_*) >>"hlp\$(TargetName).hm"
makehm ID_,HID_,0x10000 IDM_,HIDM_,0x10000 resource.h >>"hlp\$(TargetName).hm"

echo. >>"hlp\$(TargetName).hm"
echo // Prompts (IDP_*) >>"hlp\$(TargetName).hm"
makehm IDP_,HIDP_,0x30000 resource.h >>"hlp\$(TargetName).hm"
echo. >>"hlp\$(TargetName).hm"
echo // Resources (IDR_*) >>"hlp\$(TargetName).hm"
makehm IDR_,HIDR_,0x20000 resource.h >>"hlp\$(TargetName).hm"
echo. >>"hlp\$(TargetName).hm"
echo // Dialogs (IDD_*) >>"hlp\$(TargetName).hm"
makehm IDD_,HIDD_,0x20000 resource.h >>"hlp\$(TargetName).hm"
echo. >>"hlp\$(TargetName).hm"
echo // Frame Controls (IDW_*) >>"hlp\$(TargetName).hm"
makehm IDW_,HIDW_,0x50000 resource.h >>"hlp\$(TargetName).hm"

# End Custom Build

!ELSEIF "$(CFG)" == "Test - Win32 Debug"
# PROP Ignore_Default_Tool 1
# Begin Custom Build - Making help include file...
TargetName=Test
InputPath=.\Resource.h

"hlp\$(TargetName).hm" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
echo. >"hlp\$(TargetName).hm"
echo // Commands (ID_* and IDM_*) >>"hlp\$(TargetName).hm"
makehm ID_,HID_,0x10000 IDM_,HIDM_,0x10000 resource.h >>"hlp\$(TargetName).hm"
echo. >>"hlp\$(TargetName).hm"
echo // Prompts (IDP_*) >>"hlp\$(TargetName).hm"
makehm IDP_,HIDP_,0x30000 resource.h >>"hlp\$(TargetName).hm"
echo. >>"hlp\$(TargetName).hm"
echo // Resources (IDR_*) >>"hlp\$(TargetName).hm"
makehm IDR_,HIDR_,0x20000 resource.h >>"hlp\$(TargetName).hm"
echo. >>"hlp\$(TargetName).hm"
echo // Dialogs (IDD_*) >>"hlp\$(TargetName).hm"
makehm IDD_,HIDD_,0x20000 resource.h >>"hlp\$(TargetName).hm"
echo. >>"hlp\$(TargetName).hm"
echo // Frame Controls (IDW_*) >>"hlp\$(TargetName).hm"
makehm IDW_,HIDW_,0x50000 resource.h >>"hlp\$(TargetName).hm"

# End Custom Build
!ENDIF

# End Source File
# Begin Source File


把上面这段代码用以下内容替换它,千万别删了。
SOURCE=.\Resource.h

# End Source File
# Begin Source File

祸根三、Copying contents file...

SOURCE=.\hlp\Test.cnt

!IF "$(CFG)" == "Test - Win32 Release"

# PROP Ignore_Default_Tool 1
# Begin Custom Build - Copying contents file...

OutDir=.\Release
InputPath=.\hlp\Test.cnt
InputName=Test

"$(OutDir)\$(InputName).cnt" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
copy "hlp\$(InputName).cnt" $(OutDir)

# End Custom Build
!ELSEIF "$(CFG)" == "Test - Win32 Debug"

# PROP Ignore_Default_Tool 1
# Begin Custom Build - Copying contents file...
OutDir=.\Debug
InputPath=.\hlp\Test.cnt
InputName=Test
"$(OutDir)\$(InputName).cnt" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"

copy "hlp\$(InputName).cnt" $(OutDir)
# End Custom Build
!ENDIF
# End Source File
# End Group
# Begin Source File

把上面这段代码用以下内容替换它,最好别删了。

SOURCE=.\hlp\Test.cnt
# End Source File
# End Group
# Begin Source File

好了,解决了,世界又恢复了清净。

III.
Viusal C++的优化代码

原文:http://msdn.microsoft.com/library/en-us/dv_vstechart/html/vctchOptimizingYourCodeWithVisualC.asp?frame=true
作者:Mark Lacey
翻译:cnss

概要:这篇文章介绍了Visual C++.NET 2003中的代码优化。另外,有些读者可能对VC.NET 2002的优化不太了解,所以我们会简短介绍一下全程优化(Whole Program Optimization)。最后我们用一些例子充分表现一下VC.NET的优化性能,并对其讨论。

本文适用于:Visual C++ .NET 2003, 2002

前言

人们在使用一个新的编程工具时总会感到缺乏自信,本文试图让你对VC的代码优化有更直观的感觉,希望你能通过阅读本文从VC中"得到"更多的东西。

Visual C++ .NET 2003
VC.NET 2003不仅带来了两个新的优化选项,它还改进了VC.NET 2002中一些优化的性能。

第一个新增选项是"/G7",它告诉编译器对Intel Pentium 4和AMD Athlon处理器进行优化。

使用"/G7"选项编译的程序,当我们和VC.NET 2002生成的代码比较时发现,它通常能使典型的程序的运行速度提高5到10个百分点,如果使用了大量浮点代码甚至能提高10到15个百分点。而提高的优化程度可能很高也可能较低,在一些使用最新CPU和"/G7"选项的测试中,甚至提高了20%的性能。

使用"/G7"选项不代表生成的代码只能运行在Intel Pentium 4和AMD Athlon处理器上。这些代码仍可以运行在老的CPU上,只是在性能表现上可能有"小小的惩罚"。另外,我们观察到一些程序使用"/G7"后在AMD Athlon上运行的比用Intel Pentium 4更慢。

当没使用"/Gx"选项时,编译器会默认使用"/GB"选项,此时为"blended"优化模式。在VC.NET 2002和VC.NET 2003中,"/GB"代表"/G6",即为Intel Pentium Pro, Pentium II, Pentium III处理器优化。

这儿有一个例子,它展示了做与常整数乘法时使用Pentium 4和"/G7"的优化效果,下面是源代码:

int i;



// Do something that assigns a value to i.



return i*15;

当使用"/G6"时,生成了目标代码:

mov eax, DWORD PTR _i$[esp-4]

imul eax, 15

当使用"/G7"时,生成了更快(可惜更长)的代码,它没用imul(乘)指令,在Pentium 4上执行只需要14个周期。目标代码如下:

mov ecx, DWORD PTR _i$[esp-4]

mov eax, ecx

shl eax, 4

sub eax, ecx

第二个优化选项是"/arch:[argument]",用它可对SSE或SSE2优化,生成使用Streaming SIMD Extensions (SSE) 和 Streaming SIMD Extensions 2 (SSE2) 指令集的程序。当使用"/arch:SSE"选项时,目标代码只能运行在支持SSE指令(如:CMOV, FCOMI, FCOMIP, FUCOMI, FUCOMIP)的CPU上。当使用"/arch:SSE2"选项时,目标代码只能运行在支持SSE2指令集的CPU上。

相比于"/G7",使用了SSE或SSE2优化的程序,一般能减少2-3%的运行时间,个别测试中甚至能减少5%的运行时间。

使用"/arch:SSE"可得到以下效果:

1。在使用单精度浮点数时,使用SSE指令对其处理。

2。使用CMOV指令,它最早被Pentium Pro支持。

3。使用FCOMI, FCOMIP, FUCOMI, FUCOMIP指令,它们也是最早被Pentium Pro支持的。

使用"/arch:SSE2"的话,可以得到所有"/arch:SSE"选项的效果,另外还有以下几个效果:

1。在使用双精度浮点数时,使用SSE2指令对其处理。

2。使SSE2指令集做64位切换。(原文:Making use of SSE2 instructions for 64-bit shifts)

还有其它的好处,在同时使用"/arch:SSE"或"/arch:SSE2” 和 "/GL"(全程优化)选项选项时,编译器会对浮点参数和浮点返回值做函数调用规则优化。

上面说的几点优化特性已经包括于VC.NET 2003里了。另外还有一点就是能消除"死参数"--从没被用过的参数。比如:

int

f1(int i, int j, int k)

{

return i + k;

}

int

main()

{

int n = a+b+c+d;



m = f1(3, n, 4);

return 0;

}

在函数f1()中,第二个参数从没被使用过。当我们用"/GL"(全程优化)选项时,编译器将产生如下目标代码来调用f1():

mov eax, 4

mov ecx, 3

call ?f1@@YAHHHH@Z

mov DWORD PTR ?m@@3HA, eax

在这个例子里,变量"n"从没被运算,只有两个参数被f1()使用,所以只传递那两个参数(并且它们是从寄存器传过去的,这比使用栈传更快)。另外,编译这个例子时要禁止内联(inlining),否则函数f1()就不存在了,而直接给m赋予值7。

Visual C++ .NET 2002
VC.NET 2002引入了全程优化(Whole Program Optimization,缩写为WPO)的概念,"/GL"选项代表使用全程优化。全程优化意味着:编译器在.obj文件中存放的是代码的中间表达而不是目标代码,在连接时连接器对其优化处理并生成真正的目标代码。

全程优化的一个主要好处在于我们可以跨越源文件进行函数内联,这将大大提高程序的性能。还有一个好处在于编译器可以跟踪内存和寄存器的使用,以便优化使函数调用的开销更小。

下面的代表展示了全程优化的表现:

// File 1

extern void func (int *, int *);

int g, h;

int

main()

{

int i = 0;

int j = 1;

g = 5;

h = 6;

func(&I, &j);

g = g + i;

h = h + i;

return 0;

}

// File 2

extern int g;

extern int h;



void

func(int *pi, int *pj)

{

*pj = g;

h = *pi;

}

当不使用"/GL"选项时,生成了如下代码:

sub esp, 8

lea eax, DWORD PTR _j$[esp+8]

push eax

lea ecx, DWORD PTR _i$[esp+12]

push ecx

mov DWORD PTR _i$[esp+16], 0

mov DWORD PTR _j$[esp+16], 1

mov DWORD PTR ?g@@3HA, 5

mov DWORD PTR ?h@@3HA, 6

call ?func@@YAXPAH0@Z

mov eax, DWORD PTR _i$[esp+16]

mov edx, DWORD PTR ?g@@3HA

mov ecx, DWORD PTR ?h@@3HA

add edx, eax

add ecx, eax

mov DWORD PTR ?g@@3HA, edx

mov DWORD PTR ?h@@3HA, ecx

xor eax, eax

add esp, 16

ret 0

当使用了"/GL"时,你会看到下面的代码,现在的代码短多了。注意编译这个例子时同样要注意关掉内联优化。

sub esp, 8

lea ecx, DWORD PTR _j$[esp+8]

lea edx, DWORD PTR _i$[esp+8]

mov DWORD PTR _i$[esp+8], 0

mov DWORD PTR ?g@@3HA, 5

mov DWORD PTR ?h@@3HA, 6

call ?func@@YAXPAH0@Z

mov DWORD PTR ?g@@3HA, 5

xor eax, eax

add esp, 8

ret 0

表现优化的最好例子

VC编译器包括两个主要的优化参数,"/O1"和"/O2"。"/O1"代表最小尺寸,选了它编译器认为用了以下选项。

1./Og 全局优化,比如经常用到的变量使用寄存器保存,或者循环内的计算优化

2./Os 程序(exe或dll)尺寸优化优先于代码速度优化

3./Oy 使用帧指针,以提高函数调用速度

4./Ob2 编译器“觉得”应该使用内联的函数,都使用内联

5./GF 使用只读字符串池

6./Gy 告诉编译器将各个函数按打包格式编译

"/O2"选项代表最快速度,它基本上与"/O1"相同,只是用"/Ot"(更快的代码)代替了"/Os"。另外还有"/Oi"代表了展开内联函数。

一般来说,对小程序使用最快优化,对大程序使用最小尺寸优化,这是因为尺寸大的程序通常能导致加载缓慢,CACHE命中率低,系统频繁切换分布内存等问题。使用最小尺寸优化,编译不再展开循环,也不会采用更长的代码。

在选择了主要优化选项后,用profile去寻找"热区"是一个好办法,这样你可以对程序不同部分做最适当的优化。比如如果你用最小尺寸优化后,用profile发现有几个函数执行的很频繁,那你就可以把那几个函数按最快速度优化。

VC编译器可以对特定函数进行优化选项!

比如,如果你发现fiddle()函数被调用的频率很高,那你就可以让编译器只对这个函数进行最快速度优化,这样:

#pragma optimize("t", on)

int fiddle(S *p)

{

…;

}

#pragma optimize("", on)

除了"/O1"和"/O2"以外,还有"/Ox"选项,它很与"/O2"效果相同,而"/Ox"与"/Os"组合则与"/O1"效果相同。我们推荐使用"/O1"和"/O2",而不是用"/Ox"。

至此,我们讨论了"/G7","/arch"和"/GL"优化选项。

除了上面介绍的,VC还提供了两个:

1./GA 优化静态线程局部存储。(不要用于DLL project,用了也没效果)

2./Gr 使用__fastcall作默认调用规则,这代表头两个参数会用寄存器传送(如果参数能装进寄存器)。

另外的一个选项是"/opt:ref",用它可以通知连接器,在连接时去掉没被调用的函数和没被使用的数据。用"/opt:icf"选项能合并相同函数(比如你的程序可能通过模板展开了好几遍),这时优化也能减小程序的尺寸。

Visual C++ .NET中的优化改进

这儿有3个重要的优化选项,你可以把它们用在VC.NET 2003的项目中。虽然VC.NET 2002也提供了这些选项,但VC.NET 2003对它们做了性能上的改进。

下表简要的描述了它们,如果你想了解更详细的内容,请查阅VC所带的文档。

/RTC1
使用无优化的Debug模式,编译器插入动态检测代码以帮助你发现程序中的错误。比如你没有初始化的内存,或者你把__stdcall和__cdecl弄混了。

/GS
加入检测静态缓冲区(栈)溢出的代码,黑客就不能覆盖函数返回的地址以执行恶意代码。

注意:这不意味着你可以高枕无忧,你仍要留心编写安全的代码!

/Wp64
检测生成64位代码的问题,通过它你可以发现移植到64位环境下你的代码可能出现的问题。

结论

VC.NET 2003引入了两个新的优化选项,同时也改进了VC.NET 2002中的几个优化的性能,希望你能通过VC.NET 2003的优化选项来提高你程序的质量,ok, enjoy it!

IV.
解析VC++中的#pragma指令

作者:hustli
http://www.csdn.net/develop/article/19/19337.shtm

在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
其格式一般为: #Pragma Para
其中Para 为参数,下面来看一些常用的参数。

(1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗
口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:
#Pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_
X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了


(2)另一个使用得比较多的pragma参数是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。

(3)#pragma once (比较常用)
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。

(4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。

(5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体
外观的定义。

(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等价于:
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
#pragma warning(once:4385) // 4385号警告信息仅报告一次
#pragma warning(error:164) // 把164号警告信息作为一个错误。
同时这个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1---4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
常用的lib关键字,可以帮我们连入一个库文件。
(8)#pragma pack 1
将c/c++结构体里面的数据按照1字节对齐。
这条指令在通信的时候很常用。

V.
浅析VC++里面的宏

作者:hustli
http://www.csdn.net/Develop/article/19/19318.shtm

(按:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/prepr.asp )

说到宏,恐怕大家都能说出点东西来:一种预处理,没有分号(真的吗?)。然后呢?
嗯.......茫然中......
好吧,我们就从这开始说起。
最常见的宏恐怕是#include了,其次就是#define还有.......
还是从宏的用途分类吧:

1、#include 主要用于包含引用文件,至今其地位无人能替代;

2、注释掉代码。例如:
#if 0
.......
#endif;
这种机制是目前注释掉代码的最佳选择,为摩托罗拉公司员工所普遍采用;

3、代码版本管理。例如:
#ifdef DEBUG
file://调试版本
#else
file://非调试版本
#endif;

4、声明宏。例如:
#define DECLARE_MESSAGE(x) x();~x() file://有没有分号?哈哈
//........
class A
{
public:
DECLARE_MESSAGE(A);
..............
}
想起什么了,呵呵:)对,VC里面有好多这样的东东,有空我会写《我的VC历程》,到
时候会把VC里的各种宏详细的解释一下,那可是一个庞大的工程:)

5、符号常量。例如:
#define PI 3.14159

6、内联函数。例如:
#define CLEAR(x) ((x)=0)

7、泛型函数。例如:
#define ABS(x) ((x)>0? (x):-(x))
x=3 没问题! x=1.3 也没问题!
如果是这样呢:
#include
#define A(x) ((x)>0? (x):-(x))

void main()
{
int i=-1;
cout<< A(1)<< endl;
cout<< A(++i)<< endl;
}
有问题了,不过以后再说,大概讲const or inline 时会说的:)

8、泛型类型。例如:
#define Stack(T) Stack__ ##T
#define Stackdeclare(T) class Stack(T) {.....}
Stackdeclare(int);
Stackdeclare(char);
.......
Stack(int) s1;
Stack(char) s2;

9、语法扩展。例如:
Set s;//假设Set为一个描述集合的类
int i;
FORALL(i,s);
.......

宏最大的问题便是易引起冲突,例如:
libA.h:
#define MACRO stuff
同时:
libB.h:
#define MACRO stuff
下面我们对他们进行引用:
user.cpp:
#include "libA.h"
#include "libB.h"
.............
糟糕,出现了重定义!
还有一种冲突的可能:
libB.h:(没有定义宏MACRO)
class x { void MACRO(); ...........};
那么程序运行期间,libA.h中的宏讲会改变libB.h中的成员函数的名字,导致不可预料
的结果。
宏的另一个问题,便是如7中出现的问题,如果你把7中的x设为'a',程序也不会给出任
何警告,所以他是不安全的。
针对以上的问题,我们说:
1、尽可能的少用公用宏,能替换掉就替换掉;
2、对那些不能替换的宏,使用命名约定;

1、符号常量预处理程序我们可以用const or enum 来代替:
const int TABLESIZE=1024;
enum { TABLESIZE=1024 };

2、非泛型内联函数的预处理程序可以使用真正的内联函数来代替:
inline void clear(int& x) {x=0;}
奥,对了,还有这样一种情况:
#define CONTROL(c) ((c)-64)
..........
switch(c)
{
case CONTROL('a') : ......
case CONTROL('b') : ......
case CONTROL('c') : ......
case CONTROL('d') : ......
..........
}
这时候就不能单独使用内联函数来取代了,因为case标签禁止函数调用,我们只好
做如下转换:
inline char control(char c) { return c+64; }
...........
switch(control(c))
{
case 'a':.....
case 'b':.....
case 'c':.....
case 'd':.....
........
}
当然这样做是以牺牲时间作为代价的(你想想为什么)

3、对于泛型预处理程序,我们可以用函数模板或类默板来代替:
template
T ABS(const T& t) { return t>0 ? t : -t; }
template
Class Stack { ............ };

4、最后对于语法扩展程序几乎都可以用一个或多个C++类代替:
Set s;
int i;
Set_iter iter(s);
while(iter.next(i))
...........
与使用宏相比,我们只是牺牲了一点程序的简洁性而已。

当然并不是所有的宏都能替换(我们也并不主张替换掉所有的宏!),对于不能替换的
宏,我们应该对他们实行命名约定,例如:
#define COMPANY_XYZ_LIBABC_MACRO stuff
同时我们也要采取一定的方法,进行预防:
#ifndef COMPANY_XYZ_LIBABC_MACRO
#define COMPANY_XYZ_LIBABC_MACRO stuff
#endif
当然,在程序库实现内部定义的宏没有这个约束:
my.cpp:
#define MACRO stuff
........

我们给出几个常见的宏:
#define A(x) T_##x
#define Bx) #@x
#define Cx) #x
我们假设:x=1,则有:
A(1)=======T_1
B(1)======'1'
C(1)======"1"
还有一个比较常见的宏:_T
TCHAR tStr[] = _T("t code";
_T宏的作用就是转换成TCHAR。

VI.
VC++ IDE 设置技巧

来源不祥

1. 识别自定义的文件后缀名
HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Text Editor\Tabs/Language Settings\C/C++

这里显示:
数据名称:FileExtensions
数据数值:cpp;cxx;c;h;hxx;hpp;inl;tlh;tli;rc;rc2

VC++ IDE就是通过这个定义,来识别源文件后缀的。

2. External 文件
C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin 有一个SYSINCL.DAT文件,用文本编辑器打开,它的内容是:
_DBDAO.H
_ENTRYID.H

ACTIVECF.H
ACTIVEDS.H
ACTIVEX.MAK
ACTIVEX.RCV
ACTIVEX.VER
ACTIVSCP.H
ACTIVSCP.IDL


下面做一点粗浅的解释:
在编译的过程中,编译器纪录下所有被”#include”的头文件,但是要刨掉下面两种头文件:
在SYSINCL.DAT文件中提到的头文件,如:ACTIVEDS.H;
那些以//{No_DEPENDENCIES}}的头文件。譬如说,一个VC工程中的Resource.h的开头就写着这样的话:
//{{NO_DEPENDENCIES}}。

记录下来之后,编译器在Build过程中,就会检查”#include”的头文件是否被改变;是否应该重新编译源文件。

其实这些”#include”的文件有许多可能永远不会改变,这时应该告诉编译器忽略对这些文件的Dependency Check,这样可以节省许多编译时间。

这样编译器的“Minimal Rebuild”设置就可以充分发挥作用了。

SYSINCL.DAT文件中保存的就是所有微软认为要排除在Dependency Check之外的头文件。你也可以添加自己的头文件到这个列表。你也可以删除这个文件,并不会有什么麻烦。

添加了自己的头文件之后,一定要重新启动Visual Stdio IDE才能生效。

注意:运行VC的Rebuild命令时,并不启动Dependency Check。

有一些其他的标准文件,微软没有放在这个SYSINCL.DAT中,所以就显示在WorkSpace的External Dependencies文件夹下了。常见的文件如:basetsd.h、guiddef.h、msxml.dll等等。

External Dependencies的文件列表是系统自动生成的,你无法显式地添加。但是你可以拖着其中的文件到其他文件夹下,比如你可以把basetsd.h拖到Header Files文件夹下。这样,编译器将Dependency Check这个文件了。

4. 显示自定义关键字
C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin 新建一个usertype.dat文件,用文本编辑器打开,加入下列文字:
; UserType.Dat - for defining your own keywords to use in Visual C++
; Copyright(c) 1997 Microsoft Corporation. All Rights Reserved.
;---------------------------------------------------------------

CMyClass

需要重新启动VC IDE,使设置生效。

这样,当你在*.cpp等文件中输入CMyClass后,VC++IDE将显示它为蓝色

5. Macrocpp.dat
打开你的Vstdio工作路径:
C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin

有一个MACROCPP.DAT文件,用文本编辑器打开,它的内容是:

__RPC_FAR
CALLBACK
EXPORT
FAR
far
FASTCALL __fastcall
NEAR
PASCAL
SIZE_T_MAX UINT_MAX
UNALIGNED
ATL_NO_VTABLE __declspec(novtable)
AFX_API_EXPORT __declspec(dllexport)
AFX_API_IMPORT __declspec(dllimport)
AFX_CDECL __cdecl
AFX_CLASS_EXPORT __declspec(dllexport)
AFX_CLASS_IMPORT __declspec(dllimport)
AFX_COMDAT __declspec(selectany)


这个文件原用作:预编译器用来理解和处理宏的。
在最新的VisualStudio中,这个文件的功能被集成到feacp.dll中了。
所以,现在编辑这个文件不会有什么影响。
因此,在MSDN上是查不到这个文件的介绍的。



<< Home

This page is powered by Blogger. Isn't yours?