Давайте признаем, порой мы мы хотим иметь в распоряжении языка Jass достаточно комплексные возможности, но зачастую, все что нам необходимо, это копирование-вставка-замена кусков кода. По этой причине были реализованы текстовые макросы.
Синтаксис их объявления достаточно прост:
//! textmacro NAME takes argument1, argument2, ..., argument n
// Здесь располагается ваш код
//! endtextmacro
Таким образом, мы объявили некоторый блок кода внутри макроса. теперь мы можем выполнить этот макрос следующим образом:
//! runtextmacro NAME argument1, argument2, ..., argument n
Что приведет к копированию кода внутри блока макроса, на место вызова это макроса.
Например, объявление и выполнение следующего макроса
Например, объявление и выполнение следующего макроса
//! textmacro Increase takes TYPEWORD
function IncreaseStored$TYPEWORD$ takes gamecache g, string m, string l returns nothing
call Store$TYPEWORD$(g,m,l,GetStored$TYPEWORD$(g,m,l)+1)
endfunction
//! endtextmacro
//! runtextmacro Increase("Integer")
//! runtextmacro Increase("Real")
приведет к генерации следующего кода
function IncreaseStoredInteger takes gamecache g, string m, string l returns nothing
call StoreInteger(g,m,l,GetStoredInteger(g,m,l)+1)
endfunction
function IncreaseStoredReal takes gamecache g, string m, string l returns nothing
call StoreReal(g,m,l,GetStoredReal(g,m,l)+1)
endfunction
Как вы можете заметить, внутри блока кода макроса используются специальные разделители $$. Они необходимы, поскольку заменяемое строковое значение может стоять неразрывно к другим символам.
Вам так же необходимо учитывать, что комментарии и строковые литералы не защищены от обработки, препроцессор ориентируется исключительно на разделитель $$, не обращая внимание на смысл выражения.
Вам так же необходимо учитывать, что комментарии и строковые литералы не защищены от обработки, препроцессор ориентируется исключительно на разделитель $$, не обращая внимание на смысл выражения.
Если текстовый макрос не подразумевает получение каких-либо аргументов, их можно не указывать:
//! textmacro MSG
call BJDebugMsg("1")
call BJDebugMsg("2")
call BJDebugMsg("3")
//! endtextmacro
function test takes nothing returns nothing
//! runtextmacro MSG()
//! runtextmacro MSG()
endfunction
Макросы могут быть использованы вместе с областями и библиотеками, так же они придают языку некоторую фейковую динамику. В качестве простого примера, реализация стека
//! textmacro STACK takes NAME, TYPE, TYPE2STRING
scope $NAME$
globals
private $TYPE$ array V
private integer N=0
endglobals
public function push takes $TYPE$ val returns nothing
set V[N]=val
set N=N+1
endfunction
public function pop takes nothing returns $TYPE$
set N=N-1
return V[N]
endfunction
public function print takes nothing returns nothing
local integer a=N-1
call BJDebugMsg("Contents of $TYPE$ stack $NAME$:")
loop
exitwhen a<0
call BJDebugMsg(" "+$TYPE2STRING$(V[a]))
set a=a-1
endloop
endfunction
endscope
//! endtextmacro
//! runtextmacro STACK("StackA","integer","I2S")
//! runtextmacro STACK("StackB","integer","I2S")
//! runtextmacro STACK("StackC","string","")
function Test takes nothing returns nothing
call StackA_push(4)
call StackA_push(5)
call StackB_push(StackA_pop())
call StackA_push(7)
call StackA_print()
call StackB_print()
call StackC_push("A")
call StackC_push("B")
call StackC_push("C")
call StackC_print()
endfunction
Стоит упомянуть и пару дополнительных возможностей:
- Для объявления макроса, можно использовать ключевое слово textmacro_once, чей принцип работы аналогичен library_once
- Для выполнения макроса, вы можете использовать директиву //! runtextmacro optional NAME(arg), что гарантирует отсутствие ошибки компиляции, в случае отсутствия соответствующего макроса