文章目錄
第九章 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
參數。在這裏,宏調用%ar
g參數調用WRITE
命令。
- 函數、表達式和運算符的使用:
#Define FunctionExpressionOperatorMacro ($ZDate(+$Horolog))
這裏,宏作爲一個整體是一個表達式,其值是$ZDate
函數的返回值。$ZDate
對系統變量$HOLOLOG
保存的系統時間上的“+
”運算符的運算結果表達式進行運算。如上所述,最好將表達式括在括號中,以便它們與使用它們的語句的交互最小化。
- 對其他宏的引用:
#Define ReferenceOtherMacroMacro WRITE $$$ReferencedMacro
在這裏,宏使用另一個宏的表達式值作爲WRITE
命令的參數。
注意:如果一個宏引用了另一個宏,則引用的宏必須出現在引用宏之前編譯的代碼行上。
宏命名約定。
- 第一個字符必須是字母數字字符或百分比字符(
%
)。 - 第二個字符和後續字符必須是字母數字字符。宏名稱不能包含空格、下劃線、連字符或其他符號字符。
- 宏名稱區分大小寫。
- 宏名稱最長可達500個字符。
- 宏名稱可以包含日語ZENKAKU字符和日語漢字假名字符。
- 宏名稱不應以ISC開頭,因爲
ISCname.inc
文件是爲系統保留的。
宏空白約定
- 按照慣例,宏指令不縮進,出現在第1列中。但是,宏指令可以縮進。
- 宏指令後面可以跟一個或多個空格。在宏內,宏指令、宏名稱和宏值之間可以出現任意數量的空格。
- 宏指令是單行語句。指令、宏名和宏值必須全部出現在同一行上。可以使
##CONTINUE
將宏指令繼續到下一行。 #if
和#ElseI
f指令接受測試表達式。此測試表達式不能包含任何空格。#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中的過程爲:
- 從文件 File菜單中選擇保存Save 或另存爲Save As 。
- 在“另存爲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的條件,這會使代碼更簡單並優化性能。