第九章 瘋狂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的條件,這會使代碼更簡單並優化性能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章