1、创建一个TQMacroManager对象,它用来管理所有的宏。后面所有的操作都必需基于它。
[Delphi]
FMacroMgr:=TQMacroManager.Create;
[C++]
FMacroMgr=new TQMacroManager;
2、将基本的宏入栈,注意确定宏的类型。具体请参考主题中的【QMacros宏的值稳定性选项详解】一文的说明。
(1)、入栈常量
[Delphi]
FMacroMgr.Push(宏名称,宏取值)
[C++]
FMacroMgr->Push(宏名称,宏取值);
(2)、入栈通过函数动态返回值的宏
首先,我们需要定义一个宏的取值函数,目前只支持类的成员函数,首先看下回调函数的类型说明。
[Delphi]
/// <summary>查找动态宏的值时的通过回调函数获取相应的值</summary> /// <param name="AMacro">宏</param> /// <param name="AQuoter">引号类型,值可能是#0(\0)或英文的单引号或双引号</param> TQMacroValueFetchEvent = procedure(AMacro: TQMacroItem; const AQuoter: QCharW)
[C++]
/// <summary>查找动态宏的值时的通过回调函数获取相应的值</summary> /// <param name="AMacro">宏</param> /// <param name="AQuoter">引号类型,值可能是#0(\0)或英文的单引号或双引号</param> typedef void __fastcall (__closure *TQMacroValueFetchEvent)(TMacroItem *AMacro,const WideChar &AQuoter);
根据上面的声明,我们声明自己的回调函数实现,然后通过Push函数入栈。入栈时,可以为函数指定一个用户自定义的附加数据,在回调函数中可以通过TQMacroItem的Value.Value的Tag成员来访问。
入栈回调函数实现的宏时,再次强调需要注意其函数值的稳定性。默认值是考虑最糟糕的情况,使用的mvVolatile,但实际上,如果在一次替换操作中,其值是不变的,那么,mvStable是最佳的选择。如果无论何时始终不变,那么直接调用第一种方式Push我想更合适,或者设定类型为mvImmutable。
我们看下这种Push方法的声明:
[Delphi]
/// <summary>入栈指定名称和值的宏定义</summary> /// <param name="AName">宏名称</param> /// <param name="AOnFetchValue">获取宏对应的值时调用的回调函数</param> /// <param name="AVolatile">函数返回值的稳定性</param> /// <param name="ATag">用户附加的标签数据</param> procedure Push(const AName: QStringW; AOnFetchValue: TQMacroValueFetchEvent; AVolatile: TQMacroVolatile = mvVolatile; ATag: IntPtr = 0);
[C++]
/// <summary>入栈指定名称和值的宏定义</summary> /// <param name="AName">宏名称</param> /// <param name="AOnFetchValue">获取宏对应的值时调用的回调函数</param> /// <param name="AVolatile">函数返回值的稳定性</param> /// <param name="ATag">用户附加的标签数据</param> void __fastcall Push(const UnicodeString &AName,TQMacroValueFetchEvent AOnFetchValue,TQMacroVolatile AVolatile=mvVolatile,NativeInt ATag=0);
对于Delphi 2007及更早的版本,UnicodeString替换为WideString,后面的代码规则相同,请自行脑补。
3、执行模板的编译及替换操作
(1)、简化版接口
TQMacroManager提供了两个Replace函数,分别对应于两个版本的替换函数。其中简化版的接口定义如下:
[Delphi]
/// <summary>使用宏定义替换指定的文本</summary> /// <param name="AText">要被替换的文本</param> /// <param name="AMacroStart">宏定义起始字符,可以与结束字符不同</param> /// <param name="AMacroEnd">宏定义结束字符</param> /// <param name="AFlags">标志位,可取 MRF_IN_DBL_QUOTER 和 MRF_IN_SINGLE_QUOTER 的组合</param> /// <returns>返回替换完成的结果字符串</returns> function Replace(const AText: QStringW; AMacroStart, AMacroEnd: QStringW;const AFlags: Integer = 0): QStringW; overload;
[C++]
/// <summary>使用宏定义替换指定的文本</summary> /// <param name="AText">要被替换的文本</param> /// <param name="AMacroStart">宏定义起始字符,可以与结束字符不同</param> /// <param name="AMacroEnd">宏定义结束字符</param> /// <param name="AFlags">标志位,可取 MRF_IN_DBL_QUOTER 和 MRF_IN_SINGLE_QUOTER 的组合</param> /// <returns>返回替换完成的结果字符串</returns> UnicodeString __fastcall Replace(const UnicodeString &AText,UnicodeString AMacroStart,UnicodeString AMacroEnd,const int AFlags=0);
此函数将直接编译并完成替换后释放编译结果。
(2)、完整版本
完整版本由用户自己控制编译和替换过程,从而实现一次编译,多次运行,避免重复的编译开销,加快批量替换时的效率。
编译操作由函数Complie负责完成,我们看它的参数会发现和上面的Replace简化版实现几乎完全一致,唯一的不同是返回了一个TQMacroComplied类型的对象,这个对象在你不需要时,需要手动释放,以避免内存泄露。
替换操作由函数Replace的一种重载实现,唯一的参数是Complie返回的对象实例。
一般情况下,我们不推荐在Complie编译后,再入栈新的宏定义,因为它会引起在Replace时执行一遍宏类型检查,以确保生成正确的结果。
我们看一下这两个函数的声明:
[Delphi]
/// <summary>使用宏定义替换指定的文本</summary> /// <param name="AText">要被替换的文本</param> /// <param name="AMacroStart">宏定义起始字符,可以与结束字符不同</param> /// <param name="AMacroEnd">宏定义结束字符</param> /// <param name="AFlags">标志位,可取 MRF_IN_DBL_QUOTER 和 MRF_IN_SINGLE_QUOTER 的组合</param> /// <returns>编译成功,返回编译中间结果句柄,失败,返回空,返回的TQMacroComplied对象需要上层手工释放</returns> function Complie(AText: QStringW; AMacroStart, AMacroEnd: QStringW;const AFlags: Integer = 0): TQMacroComplied; /// <summary>使用指定的编译结果执行一次替换操作</summary> /// <param name="AHandle">使用Complie函数编译的中间结果</param> /// <returns>返回替换结果</returns> function Replace(AHandle: TQMacroComplied): QStringW; overload;
[C++]
/// <summary>使用宏定义替换指定的文本</summary> /// <param name="AText">要被替换的文本</param> /// <param name="AMacroStart">宏定义起始字符,可以与结束字符不同</param> /// <param name="AMacroEnd">宏定义结束字符</param> /// <param name="AFlags">标志位,可取 MRF_IN_DBL_QUOTER 和 MRF_IN_SINGLE_QUOTER 的组合</param> /// <returns>编译成功,返回编译中间结果句柄,失败,返回空,返回的TQMacroComplied对象需要上层手工释放</returns> TQMacroComplied * __fastcall Complie(UnicodeString AText,UnicodeString AMacroStart,UnicodeString AMacroEnd,const int AFlags=0); /// <summary>使用指定的编译结果执行一次替换操作</summary> /// <param name="AHandle">使用Complie函数编译的中间结果</param> /// <returns>返回替换结果</returns> UnicodeString __fastcall Replace(TQMacroComplied *AHandle);
4、释放Complie返回的结果(如果不为空),使用Delphi(Free或Dispose或FreeAndNil或由QString提供的FreeObject)或C++的delete即可。
好了,我想基本的使用步骤已经说的比较清楚了。更高级的主题咱们另章讨论。