前 言
VcExtend 扩展了VC6.0的功能,包括函数体批生成、数据接口自动生成,注释可隐可现,注释自动生成文档等功能,可以大大提高VC的工作效率。
VcExtend使用VC60的扩展宏接口,采用VBScript编写,使用起来跟VC本身的命令几乎没有区别。VcExtend完全公开源代码,可以无偿使用和修改。
可以到http://www.mmmnn.com下载VcExtend.
下边介绍几个主要的命令:
建立新类:生成干净清爽的类框架,代码模板可自由修改。
代码整理:为函数声明生成函数体,为成员变量生成读写接口(包括声明和实现),将函数体按声明的顺序重排序。这些操作一次性完成,无论你是新设计一个类还是修改一个类,这个命令都会工作得很好。
注释:生成注释模板,注释可隐藏,函数体可调出头文件写的函数注释进行阅读和修改。注释自动生成类似于MSDN的文档。文档数据采用XML文件存储,你可以按照自己的喜好定义显示格式。
注释批处理:显示/隐藏全部详细注释,显示/隐藏注释摘要,自动为数据接口函数生成注释,文档整理理。
一、预备----开始!
当然,你要先下载VcExtend.rar。另外,也需要安装微软的XML插件。
解包以后,可以看到Txt和Doc文件夹,Txt是一些生成新类时使用的代码模板,Doc文件夹存放由你的注释生成的文档和显示文档的工具。在你的工程中使用VcExtend要把这两个目录拷到你的工程的根目录下,后面的讲解使用Example目录下的Demo工程,Txt和Doc文件夹已经在里边了。
VcExtend.dsm是VcExtend的源代码,把它拷贝到C:\\Program FilesMicrosoft\\ Visual Studio \\ Common\\ MSDev98\\ Macros目录下,并把里边提供的命令挂接到工具栏上,就可以使用了。
如果你从没用过VC60的扩展宏,可以照下边的说明来操作。
启动VC60,不要打开任何工程。
在Toos菜单选择Macro,点击Options >>, 点击Load Files,选中VcExtend前面的复选框,取消其他选中的框。
点击Toolbars标签,新建一个工具条,命名为VcExtend。
点击Commands标签,在Category下拉列表中选中Macros,Commands框里就会显示VcExtend的全部命令。
选中ClassNew,拖到VcExtend工具条上,在弹出的对话框中选中Text Only,你就会看到ClassNew命令已经挂到工具条上了。把其他命令也挂到工具条上。
为了便于说明,我们先全部使用Text Only,以后,等你熟悉VcExtend后,可以把文字按钮改为你喜欢的图标。
在Show Menus For下拉列表中选择All Editors,点击Close关闭对话框。
关闭VC60, 再重新打开。好啦,下面可以尝尝滋味如何了!
二、看一看,试一试,味道怎么样?
这是AppWidzard生成的SDI工程,全部使用缺省选项,只不过加了两个类罢了。
让我们先来看一看。
打开CShow类的头文件,是不是觉得代码特别的清爽干净?
再用浏览器打开Doc目录下的Doc.htm文件。
如果你没有安装微软XML插件,赶紧装一个。我用的是msxml3_cn。没有这个可能无法看到文档的效果。
你可以看到CShow的文档了,虽然这是随便贴的一些文字,没有实际意义,但如果是真正的工程,这个文档就可以在编码、调试、维护的过程中发挥很大的作用,这些东西完全是由注释自动生成的。CTaste类的文档还是空的,等一下由你来完成。
现在,我们再来试一试。请打开CTaste类的头文件,这是一个什么功能也没有的类。
先试一下CodeCleanUp功能。
随便写两个函数的声明,不要使用VC的Add Member Function功能,而是直接在头文件上写。如:
CString CreateNewObj(CString Name, int Age);
在private:下面加个几成员变量,也是直接在头文件上写。每个变量要独占一行,并且,在要需要提供读写接口的变量后面加上可读写标记,可读的为//R,可写的为//W,可读写的为//RW,只能加在注释的最前面,如:
private:
CString m_strName; //RW 对象的名字
int m_nAge; //R 对象年龄
CString m_strAddr; //W
写完了吧?现在,点一下CodeCleanUp按钮。
把光标放到你的函数的声明上,点击CodeJump按钮,怎么样?到了函数体了吧?再点一下CodeJump,又回到声明这边来了。
再看看头文件的下方,注明R的函数生成了Get函数,注明W的生成了Set函数,把光标移到这些函数的上面,点击CodeJump,怎么样?函数体和实现都有了!
再用CodeJump跳回头文件。我们只是走马观花一下,后面会有更详细的说明,现在先看看另一个主要功能:注释!
把光标在一个函数的声明上,点击Comment按钮,函数上方会出来一个注释模板。先不管这些注释项是否符合你的要求,如果需要,你可以修改的,以后再说。
在各注释项上填一些内容,最好不要填垃圾文字,不想写的话,从别的地方拷一些也行,比如说就从这篇说明书上拷一些文字。
再点击Comment按钮,出问题啦,注释不见了!不会丢了吗?再点Comment,还好,又回来了。
用CodeJump跳到函数体。当你在写函数实现时想看一下注释怎么办?点一下Comment,真棒,头文件写的注释,源文件也可以查看,当然,修改也是可以的。
这是函数注释,类注释有没有?当然有了,还是这个Comment,只不过要先把光标放到类声明或类声明上方。
在类注释各项上填些文字,特别是简述和说明两项,多填一些,当然,内容可以是随便拷来的。
无论是函数注释还是类注释,写完或修改后都要再点一下Comment,并且,光标要放在注释或函数上,当注释隐藏了,才是保存了。
现在我们来看看你写的文档。没写过文档?刚才填的注释就是了。
用浏览器打开工程目录下的Doc目录下的doc.htm,接下来,就随便逛吧,反正,文档很象MSDN,格式是我们大家都很熟悉的。
毛主席教导我们,要知道梨子的滋味,就得亲口尝一尝。味道怎么样?如果你喜欢,就继续往下看,如果不喜欢,如果不喜欢,那么......不会是真的吧?
三、丑话说在前头
这是使用VC提供的宏功能和几个组件,用VBScript写的东东,语言的功能比较有限,并且,我对VBScript也不太熟,有些地方可能写得比较烂。
最糟的是出错处理,比如我想打开一个文件,使用Documents.Open函数,这个函数没有返回值,如果文件不存在,它不会告诉我不存在,而是造成一个致命的内存错误,可能导致VC要整个退出。哪位知道有没有解决办法啊?
当然,你不要害怕,这也没什么大不了的。如果你新建了一个工程,要使用VcExtend功能,千万记住要把Doc和Txt两个文件夹拷到工程的目录下,和Debug和res目录并列,如果你忘了的话,我以我的人格担保:一定会出问题的。
如果修改了VcExtend的源代码,最好把VC退出后再重新打开,要不然也可能产生与上边所说相似的内存错误。
出了上述的问题后,如果在win2000环境下,最好注销再登录。
还有一个问题是,由于语言所限,考虑到效率和性能,无法对代码进行复杂的词法分析,所以代码要比较规范,当然也不太特别,都是非常普通的编码规范,只要做到了下面几点就行:
一是每个类一定要有构造函数,并且放在最上面,构造函数上方的代码将不作处理,你可以把内置的数据类型,如enum,struct或嵌套类什么的,放在构造函数的上边。
二是每个成员变量的声明要独占一行,是指头文件的成员变量,函数体内的东西,VcExtend是完全不管的。
三是如果要写额外的注释,不能使用/**/,要使用//。
存在的主要问题,大概就这些了吧。如果你发现了什么问题,请在论坛发表,我将尽力解决。
另外要说明的是,Inline函数的实现放在.inl文件中,所以一个类有三个文件。这是自动处理的,可不敢给你添麻烦。CodeJump的良好跳转功能,会让你感觉不到多了个文件。
虽然还有一些问题,但我觉得VcExtend还是挺好用的,能提高工作效率十分之N。我向你推荐它,就表示这个东东不错,因为,我觉得不好的东西,是从来不会给人的,比如说,我从来不会随便送钱给人家,因为,我知道,金钱是肮脏的东西。
四、与清爽干净的代码为伴----类框架生成
ClassNew很简单易用,但还是有一些需要说明的。
首先是一些可选项,请根据你的需要来填写,如继承自某一个类的,就加:BaseName,不指定则会认为以CObject为基类;要指定文件名,就加#FileName,不能有后缀,因为.cpp和.h和.inl都会共用这个文件名,不指定则会以类名去掉前缀C作为文件名,我主张不指定为好;至于目录名,现在不理它吧,很少用,以后会提到的;最后就是宏选项了,如果是可序列化的类,就加/S,消息类就加/M,加了这些选项就会生成相应的宏。除了类名在最前面外,所有的选项都不需要考虑输入的顺序。
你还可以修改代码模板,Txt目录下的cpp.txt,hpp.txt,inl.txt就是生成新类框架的模板,可以按自已的喜好来修改。
ClassNew有一个显著的缺陷,就是生成的类不会自动加入工程,也不会加入到ClassWizard中,要使用Project->Add To Project->Files功能手工把它加入工程,如果要加入到ClassWizard中,可以删掉工程的.clw文件,再重新编译。一个更简单的办法是,先用VC的New CLass功能生成一个类,再用ClassNew重新生成一遍,这样就不会有这些缺陷了。
不是使用ClassNew生成的类,也完全可以使用VcExtend的功能,只不过首次使用的时候会要求你输入类名和基类名而已,唯一的问题是代码跳转,偶尔可能会找不到,不过很少见。
如果你用VC的AppWizard新建了一个工程,想把VC生成的代码改为用ClassNew来生成,也很简单的。先把工程另存一份,并启动另一个VC打开另存的工程。用ClassNew重新生成各个类,并从另存的工程中把所缺少的代码拷过来。最好是先拷头文件的代码,拷完后点一下CodeCleanUp生成函数体,再拷函数体的内容。每做完一个类后编译并运行工程看有没有问题,没问题再做下一个类。APP类千万不要忘了拷CMyApp theApp这一行。
五、生成函数体与数据接口----好用的代码完成功能
CodeCleanUp主要有三个功能,一是函数体生成,二是数据接口函数生成,三是函数体按声明的顺序重排序。不管你的头文件写了多少或修改了多少,CodeCleanUp都会很好的工作。
要提供公共接口的成员变量,只要在注释上加上读写标记就行,只读为//R,只写为//W,可读写为//RW,这样,你自己可以对哪些成员变量是可读写的一目了然,而CodeCleanUp会根据这些标记生成公共数据接口。任何时候,你都可以修改读写标记,然后点击CodeCleanUp,都会自动生成或删除接口函数。
接口函数默认的命名规则是Get或Set加上去掉前缀的变量名,如果不喜欢,可以修改,参见后面的关于可选项的章节。
接口函数的实现代码也是自动生成的,一般情况下可以直接使用,当然你也可以修改它。
接口函数都是内联的,实现在.inl文件里,你可以使用CodeJump方便的在声明与实现之间跳转。
除了接口函数外,其他函数也可以声明为内联,只要在声明的最前面加inline,CodeCleanUp就会把它生成到.inl文件中去,但函数体里的实现代码当然不会象接口函数一样自动生成了,可以使用CodeJump跳过去写自己的代码。
要删除一个或一些函数,只要删掉声明后运行CodeCleanUp就行,接口函数则只需修改成员变量的读写标记。内联函数一般都是很简单的,所以删除时会完全删掉,但普通函数则不会删除函数体,因为里边的代码说不定你还要用呢,所以只会把它移到最上边,要手工删掉才行。
CodeCleanUp会把接口函数的声明放在头文件的最下边,这样你所要关注的代码就少了很多,可以把注意力放在最重要的代码上。
六、让你爱上写注释----注释可隐可现,文档自动生成
注释项分为三部份,一是全自动处理的,完全不用理会它。包括:类注释的类名,基类名,文件名;函数注释的函数名,函数原形,这部分不会在源文件中显示出来,但可以在文档中显示。二是必选项,包括类注释的简述,函数注释的简述,简述就是最简单的功能描述,如果连这个都不写的话,你这个注释也就没什么意义了。函数注释的返回值和参数项也属于这一类,但不写这两项注释还比较无所谓。三是可选项,你可以增删一些项目,如说明就是可选的。可选项的增删方法参见后面的关于选项的章节。
生成注释模板时,Comment并不是直接贴个模板了事,而是先进行了一些分析的。重载函数会当作一个函数,所有的参数都会收集起来。如果你为多个重载函数写了多份注释,后保存的就会覆盖先保存的。同名的函数,即使不是连续声明,也会当作重载函数。
类模板中的[]内的返回类型或参数,千万不要删掉它。你的注释加在它后面就是了。用空格隔开比较好看一些。
如果你修改了函数,如加了参数,或加了重载的函数,可以把当前的注释保存,再调出来,就会把新加的参数加入模板。
当注释隐藏时,实际上是保存到Doc目录下的Doc.xml文件上去了。隐藏和保存是同时进行的,所以修改后最好隐藏一下,虽然你不隐藏,注释还在你的源文件上,不会丢失,但当你在cpp文件上打开相应的注释时,只会调出已经保存过的内容,并且,没保存的注释不会出现在文档上。这些操作,可以使用注释批处理功能,参见后续章节。
每一个注释项都是以///$加标签名开头的,如果要换行,从第二行开始,要用///开头,千万不要用///$,否则会当作是一个新的项,也不要用//开头,否则会认为格式不规范而拒绝存入。如果注释较长,应该分多行来写,就象代码一样。多行注释可以先写成普通文字,写完后用MultiLineComment命令来处理。因为注释是要用来生成文档的,而文档的显示环境与在源文件中不一样,写注释时的分行,文档中会忽略。如果分档中要强制分行,必须加r,要强制分行并空一行,就加rr。下面是一个多行注释项的例子:
///$说明:
///这是第一行,
///这是第二行,在文档中不会强制换行。
///\r这是第二段,在文档中会强制换行,但\r不会显示出来。
///\r\r这是第三段,在文档中会在前面插一个空行。
写完以后选中注释,注意不要选中///$那一行,点击MultiLineComment,就会按标准格式排版。保存前是什么样子的注释,以后调出来还是什么样子。
注释与函数声明(或类注释与类声明)之间不能有空行。
注释将存成XML文件,这样内容和显示可以完全分开。如果你对XML比较熟悉,可以定义自己的XSL文件来显示文档。不过,使用我写的文档显示工具也不错啊,虽然我对XML不熟悉,但是费了很多心血的。考虑到VC程序员比较习惯MSDN的风格,所以基本上是按照MSDN的格式来显示文档的,如果你喜欢漂亮一点,可以加一些配色或图片。
文档的显示其实只使用一个文件Doc.htm,具体内容完全是用脚本和xsl模板来实现的,所以IE的前进后退刷新之类的功能无法使用。
还有一个不错的功能:你也可以象MSDN一样,对函数进行分类。同一类的函数的声明要放在一起,并且在每类的第一个函数的注释上加分类这一项。用手工加上这一项
///$分类:类名
注意是中文的分号。象访问控制符一样,后面的函数都会当作是这一类的,直到定义了另一个分类。同一类的函数只能在第一个加分类这个注释项,如果你定了多个同名的类,我可不帮你合并。如果不明白,打开CShow类的头文件,点击CommentDetail调出全部注释,可以很清楚的看到是如何将函数分类的,再看一下CShow的文档,可以看到分类的效果。
后面还提供一些自动化功能,帮你高效地处理注释和文档。
七、注释批处理的魔法----省时高效、随心所欲
CommentDesc 显示/隐藏注释摘要。这个功能会把所有函数注释的简述部份作为普通注释加在函数声明后面。这样有两方面的作用,一是帮助读者快速的理解类的功能,避免了注释隐藏后完全没有注释的极端,也不会让大量的注释掩没了有效的代码,第二呢,猜到了吗?这是供VC调用的,就是在对象成员列表中弹出函数注释。怎么样?想得很细吧。我的建议是,当一个类的代码完成后,就调用CommentDesc,然后作为最终代码保存。
CommentDetail和CommentDesc是“不兼容”的,互相都会把对方显示的注释先隐藏掉,以避免太多的注释。两者都是开关式的,使用它们,你可以很方便地让你的源文件完全没有注释(成员变量除外),显示全部注释,或是只显示注释摘要。
值得注意的一点是,CommentDesc仅仅是显示注释摘要,在这里作修改,是无法保存到文档中去的。
AttributeComment 为成员变量的读写接口生成注释。如果你给的成员变量写了注释,如: CString m_strName; //RW 主角的名字。 并调用AttributeComment,那么GetName和SetName函数都会自动生成注释,即使你还没有调用CodeCleanUp生成GetSet函数。注释包括简述和返回值注释和参数注释。生成的注释也许并不太合你的心意,但是修改它总比完全重新写它要省时得多。生成的注释会直接保存到文档,而不会在源文件中显示出来,可以调用CommentDetail全部调出来并修改它。不管有多少成员变量,都会一次性生成注释。已经修改并保存的注释,不会重新生成,以免删掉了你的工作成果。
DocCleanUp 文档整理,这个功能会把多余的文档删掉,比如说函数已经删除了,调用这个命令就会把相关的文档删除。另外,这个命令会把文档按函数声明的顺序重排序。如果你看到文档有些混乱,就调用这个命令试一下。
八、还有一些小玩意,喜欢吗?
VC60的代码跳转功能,常常找不到,并且也没有方便的头文件源文件之间的跳转命令,加了一个.inl文件之后,就更不方便了,所以我写了一个自己的跳转命令CodeJump。
函数声明和函数实现的相互跳转,跟VC的跳转命令差不多,但CodeJump一般不会找不到。
CodeJump还会根据函数是否内联,来自动跳到.cpp或.inl文件,.inl到.h的跳转也工作良好。
如果找不到,CodeJump会弹出一个窗口,问你是否跳转到相应的文件,它会根据函数是否内联来判断哪个文件是你的可能目标。
如果你只是想作文件的跳转,可以把光标放在空行或注释上行上,点击CodeJump,就会进行文件的跳转。如果你工作在头文件上,想跳到.cpp文件,光标就放到//!begin get/set这一行的上方,如果想跳到.inl文件,光标就放到//!begin get/set的下方。
VC60中为MFC的子类显示基类有哪些可覆盖函数,自定义的CObject子类就没有这个功能了,VirtualFunction可以弥补这个小缺陷。它的界面有点恶心,要填序号来选函数,并且只显示函数名。别骂我做得不好,VBScript就提供InputBox这么一个输入工具,我容易吗我?
我一直很奇怪VC60的ListMembers命令为什么做成默认是显示全局成员,而不是显示this指针的成员,有一个::来显示全局成员了,默认显示this指针的成员不是很好吗?害我每次都要写一个this->,然后又要删掉它。ThisList就是用来显示this指针成员的。这个命令也做得不好,要调用ThisList,再调用VC60的ListMembers命令才能调出成员列表,我在ThisList宏的最后加了ExecuteCommand "ListMembers"语句,但却不起作用。ThisList实际上只是删掉代码中的上一个this->,再在光标位置插入一个this->。我把ThisList的快捷键设为Shift + Left Arrow,把ListMembers的快捷键设为Shift + Right Arrow,这样按住shift再按左右箭头就出来了。开头感觉不是很爽,习惯了觉得也不错,总比写一个this->,然后又删掉它好些。请注意,如果你的代码中有Isthis->这样的字符,ThisList会把其中的this->删掉,不过这种代码出现的可能性很小,并且编绎时也很容易发现的。
九、可选项----定制你的VcExtend
你可以通过对源代码的一些全局常量进行修改来使VcExtend更适合你的习惯或开发组的规范。源代码中已对这些全局常量加了注释,很容易理解的,这里只对其中的几个可选项加一些说明。
HppDir = "" ’ "Hpp" ’Hpp文件的存储目录,如果不为"",一定要加
CppDir = "" ’ "Cpp" ’Cpp文件的存储目录,如果不为"",一定要加
InlDir = "" ’ "Hpp" ’Inl文件的存储目录,如果不为"",一定要加
一个工程里文件较多时可能要分目录存储,修改上面的存储目录即可实现分目录存储。如果定义了存储目录,一定要在工程目录下建立这些目录,如果忘了,就会产生第三节所说的内存错误。如果把InlDir设为等于HppDir,inl文件就会和.h文件放在一起。
关于目录,还有一点要说明的。生成新类的时候,有一个目录选项,所填目录不包括上述的存储目录的部份。比如HppDir="Hpp",CppDir="Cpp", InlDir=HppDir,生成新类时加了目录名@Views,那么cpp文件会存到CppViews目录下,而hpp和inl文件会存到HppViews目录下。并且,一定要先建好Views目录!!!
Mark_Desc = "简述" ’注释标签,修改了这些项则必须对Doc目录下的.xsl文件作相同修改。
Mark_Return = "返回" ’
Mark_Param = "参数" ’
Mark_Cort = "分类" ’
这些是注释的必选项,你可以改变标签名字,比如说改为英文。我喜欢用中文,因为英文长短不一。
Marks_Class = "版权/作者/初建/修改/说明" ’可选类注释项,修改则须修改.xsl文件
Marks_Function = "说明" ’可选函数注释项
这些是注释的可选项,你可以随便增删或改为英文,也可以按自己的喜好或开发规范重新排序。
NameLen = Len(Mark_Desc) ’等于上面的最长的标签的Len
NameLang = 2 ’标签的语言,英文为1,中文2
如果你改变了标签,这两个变量也要做相应改变,否则注释会很难看,存入时还可能读错。
下面的可选项目前不推荐改动,有兴趣的话可以看看。
AssertGet = False ’是否在所有的读函数中加断言语句
AssertSet = False ’是否在所有的写函数中加断言语句
GetSetAll = False ’是否为所有的成员变量生成读写函数
AssertSentence = "ASSERT_VALID(this);" ’断言语句的内容
前面已经说过,用//RW来标记成员变紧是否可读写,标记为//RW的成员变量会生成public:的读写接口。
如果你把GetSetAll设为True,VcExtend就会为所有的成员变量生成数据读写函数,其中,有可读写标记的成员变量会生成public:的读写函数,而其他读写函数的访问控制符与成员变量一致。有点迷糊吧?看看例子:
protected:
CString m_strName; //R
private:
int m_nAge; //W
CStrign m_strAddr;
如果GetSetAll设为True,则会生成:
public:
CString GetName() const;
void SetAge(int Age);
protected:
void SetName(const CString& Name);
private:
int GetAge() const;
CStrign* GetAddr();
void SetAddr(const CStrign& Addr);
可以看出,公共的读写接口还是一样的,那么protected和private的读写函数有什么意义吗?
我正在尝试一种C++编程观念:类的封装可以进一步扩大,将类设计和类实现完全分开,函数声明、成员变量、构造与析构及对象有效性检查、数据接口均属于类设计,其他功能函数的实现属于类实现,类实现完全不接触成员变量,而是通过数据读写函数来操作数据。这样做有一些很吸引人的好处,比如说AssertGet、AssertSet、GetSetAll全部设为True,类实现中全部通过GetSet函数来读写成员数据,那么将自动形成严密的断言网络,可以拦截很多很难查找的错误,照我的说法,是用密集阵火炮拦截巡航导弹。当然,这是指AssertValid函数已经实现得很好。
还有其他好处,但也有一些缺点,我正在尝试使用,有兴趣的朋友可以一起探讨,目前不推荐。
十、如果VcExtend对你有用,请跟我一起完善它
跟开发工具有关的东西,是否有用好用,要靠实际应用中来检验。如果你发现了什么问题,或修改了其中的某些缺陷, 请在论坛中发表。如果你有建议,更不要藏私啊。我专门搞了个小论坛,就是为了收集各种问题和建议,过一段时间,我会推出修改后的版本。
还有一些功能正在酝酿中,比如小组中多人各自开发,如何合并文档等等。你如果有什么相关需求,请当作建议提出来。
本文地址:http://com.8s8s.com/it/it1614.htm