第九章 疯狂Caché 宏和宏预处理器(一)

第九章 Caché 宏和宏预处理器(一)

CachéObjectScript编译器包括预处理器,CachéObjectScript包括对预处理器指令的支持。这些指令允许创建在应用程序中使用的宏-无论是在方法中还是在例程中。

这些宏提供了在代码中进行简单文本替换的功能。Caché本身还包括各种预定义的宏,这些宏在Caché文档集中的相关上下文中进行了描述。

预处理器指令包含在代码的.Mac版本中。编译.Mac代码时,ObjectScript编译器执行预处理并生成.int(中间的、可读的ObjectScript)代码和.obj(可执行对象)代码。

本章共分三个部分:

  • 使用宏-介绍如何定义和使用宏。
  • 预处理器指令参考-提供可在宏定义中使用的预处理器指令列表。
  • 使用系统提供的宏-介绍如何使用高速缓存提供的预定义宏,并提供这些宏的列表。

注意:预处理器在ObjectScript解析器处理任何嵌入式SQL或嵌入式HTML代码之前展开宏。这意味着嵌入式HTML中不支持宏。预处理器支持嵌入式或延迟编译模式下的嵌入式SQL;预处理器不在动态SQL中展开宏。ObjectScript分析器在分析预处理器指令之前删除多行注释。因此,在/ * 中指定的任何宏预处理器指令。。。 * /不执行多行注释。

以下全局变量返回MAC代码信息。使用ZWRITE显示以下全局变量及其下标:

  • ^rINDEX(routinename,“MAC”)包含修改后最后保存MAC代码的时间戳,以及此MAC代码文件的字符数。包括注释和空行在内的字符数。上次保存MAC代码的时间戳、编译时间以及有关使用的#include文件的信息都记录在.int文件的^例程global中。

  • ^rMAC(Routinename)包含MAC例程中每行代码的下标节点,^rMAC(routinename,0,0)包含行数,^rMAC(routinename,0)包含上次保存时的时间戳,^rMAC(routinename,0,“size”)包含字符数。

  • ^rMACSAVE(例程名)包含MAC例程的历史记录。它包含与前五个保存的MAC例程版本的^rMAC(例程名称)相同的信息。它不包含有关当前MAC版本的信息。

使用宏

创建自定义宏

宏是替换的一行定义,可以支持ObjectScript功能的许多方面。在它们的基本形式中,它们是用#DEFINE指令创建的。例如,下面的代码创建一个名为StringMacro的宏并将其替换为字符串“Hello,World!”

#Define StringMacro "Hello, World!"

(可以使用##CONTINUE#DEFINE指令继续到下一行。)

ObjectScript允许使用“$”语法调用宏,例如:

  WRITE $$$StringMacro

在本例中,它显示字符串“Hello,World!”以下是整个示例:

/// d ##class(PHA.TEST.ObjectScript).TestDefine()
ClassMethod TestDefine()
{
	#Define StringMacro "Hello, World!"
	WRITE $$$StringMacro
}
Hello, World!

支持的功能包括:

  • 字符串替换,如上所述。
  • 数字替换
#Define NumberMacro 22
#Define 25M ##Expression(25*1000*1000)

与ObjectScript中的典型情况一样,数字宏的定义不需要引用数字,而字符串必须在字符串宏的定义中引用。

  • 变量替换:
#Define VariableMacro Variable

在这里,宏名代替已经定义的变量的名称。如果未定义该变量,则会出现<unfinded>错误。

  • 命令和参数调用:
#Define CommandArgumentMacro(%Arg) WRITE %Arg,!

宏参数名称必须以“%”字符开头,例如上面的%arg参数。在这里,宏调用%arg参数调用WRITE命令。

  • 函数、表达式和运算符的使用:
#Define FunctionExpressionOperatorMacro ($ZDate(+$Horolog))

这里,宏作为一个整体是一个表达式,其值是$ZDate函数的返回值。$ZDate对系统变量$HOLOLOG保存的系统时间上的“+”运算符的运算结果表达式进行运算。如上所述,最好将表达式括在括号中,以便它们与使用它们的语句的交互最小化。

  • 对其他宏的引用:
#Define ReferenceOtherMacroMacro WRITE $$$ReferencedMacro

在这里,宏使用另一个宏的表达式值作为WRITE命令的参数。

注意:如果一个宏引用了另一个宏,则引用的宏必须出现在引用宏之前编译的代码行上。

宏命名约定。

  • 第一个字符必须是字母数字字符或百分比字符(%)。
  • 第二个字符和后续字符必须是字母数字字符。宏名称不能包含空格、下划线、连字符或其他符号字符。
  • 宏名称区分大小写。
  • 宏名称最长可达500个字符
  • 宏名称可以包含日语ZENKAKU字符和日语汉字假名字符。
  • 宏名称不应以ISC开头,因为ISCname.inc文件是为系统保留的。

宏空白约定

  • 按照惯例,宏指令不缩进,出现在第1列中。但是,宏指令可以缩进。
  • 宏指令后面可以跟一个或多个空格。在宏内,宏指令、宏名称和宏值之间可以出现任意数量的空格。
  • 宏指令是单行语句。指令、宏名和宏值必须全部出现在同一行上。可以使##CONTINUE将宏指令继续到下一行。
  • #if#ElseIf指令接受测试表达式。此测试表达式不能包含任何空格。
  • #if表达式、#ElseIf表达式、#Else指令和#EndIf指令都出现在各自的行上。同一行上这些指令之一后面的任何内容都被视为注释,不会进行解析。

宏注释和Studio助手

宏可以包括注释,这些注释作为其定义的一部分传递。用/* and */, //,#;, ;, 和 ;; 分隔的注释都按照其通常的方式运行。

///指示符开头的注释具有特殊功能。如果要将Studio Assist与包含文件中的宏一起使用,请在紧接其定义之前的行上放置 /// 注释;这会使其名称出现在Studio Assist弹出窗口中。(当前文件中的所有宏都显示在Studio Assist弹出窗口中。)例如,如果通过#include指令引用以下代码,则第一个宏将出现在Studio Assist弹出窗口中,而第二个宏不会:

/// A macro that is visible with Studio Assist
#Define MyAssistMacro 100
 //
 // ...
 // 
 // A macro that is not visible with Studio Assist
#Define MyOtherMacro -100

保存自定义宏

宏可以出现在调用它们的文件中,或者更常见的是出现在单独的包含文件中。要使宏在类范围内可用(即可供任何成员调用),请将其定义放在包含文件中并将其包含在类中。

要将宏放置在包含文件中,Studio中的过程为:

  1. 从文件 File菜单中选择保存Save 或另存为Save As 。
  2. 在“另存为Save As”对话框中,指定“Files of Type”字段的值为“Include File(*.inc)”。

注意:此字段的宏例程(*.mac)值不是ObjectScript宏的正确文件类型。
3. 输入目录和文件名,然后保存宏。

调用宏

当宏定义是要定义的方法或例程的一部分时,或者当该方法或例程使用#include指令引用外部源中的定义时,可以调用宏。

若要从ObjectScript代码中调用宏,请使用前缀为“$$$”的名称来引用它。因此,如果定义了一个名为MyMacro的宏,则可以通过引用$$$MyMacro来调用它。请注意,宏名称区分大小写。

可以在无法提供变量值的上下文中调用宏来替换值。例如:

#Define StringMacro "Hello",!,"World!"
   WRITE $$$StringMacro
#Define myclass "Sample.Person"
  SET x=##class($$$myclass).%New()

请记住,宏是文本替换。在替换宏后,将检查结果语句的语法是否正确。因此,定义表达式的宏应在需要表达式的上下文中调用;命令的宏及其参数可以作为独立的ObjectScript行;依此类推。

引用外部宏(包括文件)

将宏保存到单独的文件后,可以使用#include指令使其可用。该指令不区分大小写,因此它可以显示为#include

在类中或例程开始处包含宏时,指令的形式如下:

#include MacroIncFile

其中,MacroIncFile是指包含名为MacroIncFile.inc的宏的包含文件。请注意,当引用的文件是#include的参数时,其名称中不包括.inc后缀。

例如,如果文件MyMacros.inc中有一个或多个宏,则可以在以下调用中包含它们:

#include MyMacros

如果YourMacros.inc文件中还有其他宏,则可以在以下调用中包含所有这些宏:

#include MyMacros
#include YourMacros

当在例程中使用#include时,必须在单独的行上为每个include文件指定单独的#include语句。

要在类定义的开头包含包含文件,语法为:

include MyMacros

要在类定义的开头包含多个包含文件,语法为:

include (MyMacros, YourMacros) 

请注意,此include语法没有前导井号;此语法不能用于#include。此外,Studio中的编译会转换单词的形式,使其第一个字母大写,后面的字母小写。

ObjectScript编译器提供了允许包含外部宏的/定义限定符。

PHA.MOB.TEST.Macros.inc
#Define SelfString "自定义",!,"宏!"
#Define sysDate +$h
#Define sysTime $p($h,",",2)
Include PHA.MOB.TEST.Macros

Class PHA.TEST.ObjectScript Extends %RegisteredObject
{
    /// d ##class(PHA.TEST.ObjectScript).TestOtherDefine()
    ClassMethod TestOtherDefine()
    {
    	WRITE $$$StringMacro,!
    	WRITE $$$SelfString,!
    	WRITE +$h,!
    	WRITE $$$sysDate,!
    	WRITE $p($h,",",2),!
    	WRITE $$$sysTime,!
    }
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestOtherDefine()
Hello, World!
自定义
宏!
65462
65462
52469
52469

预处理器指令参考

Caché包括对以下系统预处理器指令的支持:

注意:宏预处理器指令不区分大小写。为清楚起见,本文档以标题大小写显示其名称,但这不是必需的。

#;

#;preessor指令创建.int代码中没有出现的单行注释。注释仅出现在.mac代码或包含文件中。#;出现在该行的开头(第1列)。当前行的其余部分将继续注释。它的形式是:

#; Comment here...

其中注释跟在“#;”后面。

#;使整行成为注释。与##;预处理器指令相比,该指令使当前行的其余部分成为注释。

#Def1Arg

#Def1Arg预处理器指令定义只有一个参数宏,其中参数中可以有逗号。#Def1Arg#DEFINE的特殊版本,因为#DEFINE将参数内的逗号视为参数之间的分隔符。它的形式是:

#Def1Arg Macro(%Arg) Value
  • Macro 是要定义的宏的名称,它只接受一个参数。有效的宏名称是字母数字字符串。
  • %arg是宏的参数名称。指定宏参数的变量名称必须以百分号开头。
  • 值是宏的值,其中包括使用在运行时指定的%arg的值。

#Def1Arg行可以包含##;注释。

例如,下面的 MarxBros 宏接受以逗号分隔的 Marx brothers名字列表作为其参数:

/// d ##class(PHA.TEST.ObjectScript).TestDelArg()
ClassMethod TestDelArg()
{
	#Def1Arg MarxBros(%MBNames) WRITE "%MBNames:",!,"The Marx Brothers!",!
	// some movies have all four of them
	$$$MarxBros(Groucho, Chico, Harpo, and Zeppo)
	WRITE !
	// some movies only have three of them
	$$$MarxBros(Groucho, Chico, and Harpo)
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestDelArg()
Groucho, Chico, Harpo, and Zeppo:
The Marx Brothers!
 
Groucho, Chico, and Harpo:
The Marx Brothers!

在MaxBros宏接受参数%MBNames的位置,该参数接受以逗号分隔的Marx brothers.字列表。

#Define

#DEFINE预处理器指令定义宏。它的形式是:

#Define Macro[(Args)] [Value]
  • Macro 是正在定义的宏的名称。有效的宏名称是字母数字字符串。
  • args (可选)是它接受的一个或多个参数。它们的形式是(arg1、arg2,.)。指定宏参数的每个变量的名称必须以百分号开头。参数值不能包含逗号。
  • value(可选)是分配给宏的值,其中该值可以是任何有效的ObjectScript代码。这可以是文字这样简单的东西,也可以是表达式这样复杂的东西。

如果宏是用值定义的,则该值将替换ObjectScript代码中的宏。如果宏是在没有值的情况下定义的,则代码可以使用其他预处理器指令来测试宏是否存在,然后执行相应的操作。

可以使用##CONTINUE#DEFINE指令继续到下一行。可以使用##;将注释附加到#DEFINE行。但是不能在同一行上使用##Continue##;。

带值的宏

带值的宏为ObjectScript代码中的简单文本替换提供了一种机制。只要ObjectScript编译器遇到宏(格式为$MacroName)的调用,它就会替换为ObjectScript代码中当前位置的宏指定的值。宏的值可以是任何有效的ObjectScript代码。这包括:

  • 字符串
  • 数字
  • 类属性
  • 调用方法、函数或其他代码

宏参数不能包含逗号。如果需要逗号,可以使用#Def1Arg指令。

以下是以各种方式使用宏的定义示例。

#Define Macro1 22
#Define Macro2 "Wilma"
#Define Macro3 x+y
#Define Macro4 $Length(x)
#Define Macro5 film.Title
#Define Macro6 +$h
#Define Macro7 SET x = 4
#Define Macro8 DO ##class(%Library.PopulateUtils).Name()
#Define Macro9 READ !,"Name: ",name  WRITE !,"Nice to meet you, ",name,!

#Define Macro1A(%x) 22+%x
#Define Macro2A(%x) "Wilma" _ ": %x"
#Define Macro3A(%x) (x+y)*%x
#Define Macro4A(%x) $Length(x) + $Length(%x)
#Define Macro5A(%x) film.Title _ ": " _ film.%x
#Define Macro6A(%x) +$h - %x
#Define Macro7A(%x) SET x = 4+%x
#Define Macro8A(%x) DO ##class(%Library.PopulateUtils).Name(%x) 
#Define Macro9A(%x) READ !,"Name: ",name  WRITE !,"%x ",name,!
#Define Macro9B(%x,%y) READ !,"Name: ",name  WRITE !,"%x %y",name,!

宏值的约定

虽然宏可以有任何值,但约定宏是文字表达式或完整的可执行行。例如,以下是有效的ObjectScript语法:

#Define Macro7 SET x =

其中,可以使用如下代码调用宏:

$$$Macro7 22

预处理器将扩展到

 SET x = 22

虽然这显然是有效的ObjectScript语法,但不鼓励使用宏。

没有值的宏

可以在没有值的情况下定义宏。在这种情况下,宏的存在(或不存在)指定存在特定条件。然后,可以使用其他预处理器指令来测试宏是否存在并执行相应的操作。例如,如果将应用程序编译为Unicode可执行文件或8位可执行文件,则代码可能为:

#Define Unicode

#IfDef Unicode
   // 在此处执行操作以编译Unicode
   // 程序的版本
#Else
   // 在此处执行操作以编译8位
   // 程序的版本
#EndIf

JSON转义反斜杠限制

宏不应尝试接受包含\“转义约定的JSON字符串。宏值或参数不能将JSON\“转义序列用于文字反斜杠。在宏主体或传递给宏展开的形参中不允许此转义序列。请不要将JSON\“转义序列用于文字反斜杠。或者,可以将\“转义更改为\u0022。此替代方法适用于同时用作键名和元素值的JSON语法字符串。在将包含文字反斜杠的JSON字符串用作JSON数组或JSON对象的元素值的情况下,也可以将包含\“的JSON字符串替换为圆括号内的ObjectScript字符串表达式,该表达式的计算结果与字符串值相同。

#Dim

#Dim预处理器指令指定局部变量的类型。它的形式是:

#Dim VariableName As DataTypeName
#Dim VariableName As DataType = InitialValue
#Dim VariableName As List Of DataType
#Dim VariableName As Array Of DataType
  • VariableName是要定义的变量,或逗号分隔的变量列表。
  • DataType是VariableName的类型。指定数据类型是可选的,可以省略为dataType,只需指定=InitialValue即可。
  • InitialValue是可选地为VariableName指定的值。(此语法不适用于列表或数组。)

如果VariableName指定逗号分隔的数据变量列表,则为所有变量分配相同的数据类型和初始值。例如:

#Dim a,b,c As %String = "default string"

如果VariableName指定了逗号分隔的对象变量列表,则会为每个变量分配一个单独的OREF。例如:

/// d ##class(PHA.TEST.ObjectScript).TestMDim()
ClassMethod TestMDim()
{
	#Dim d,e,f As %DynamicArray = ["element1","element2"]
	#Dim g,h,i As Sample.Person = ##class(Sample.Person).%New()
	w d.%ToJSON(),!
	w e.%ToJSON(),!
	w f,!
	w g,!
	w h,!
	w i.Age,!
}

DHC-APP>d ##class(PHA.TEST.ObjectScript).TestMDim()
["element1","element2"]
["element1","element2"]
1@%Library.DynamicArray
[email protected]
[email protected]
 

#Else

#Else预处理器指令指定一组预处理器条件中直通情况的开始。它可以跟在#IfDef#If#ElseIf之后。后跟#EndIf。它的形式是:

#Else
  // 指定操作的后续缩进行
#EndIf

#Else指令关键字应该单独出现在一行中。在同一行上跟在#Else后面的任何内容都被视为注释,不会进行解析。

注意:如果#Else出现在方法代码中,并且参数不是文字值0或1,编译器将在子类中生成代码(而不是调用父类中的方法)。若要避免生成此代码,请测试值为0或1的条件,这会使代码更简单并优化性能。

猜你喜欢

转载自blog.csdn.net/yaoxin521123/article/details/106227983