Содержание главы:
Методы функции
В vJass функции могут вести себя как объекты с двумя методами: evaluate и execute. Оба метода имеют такой же список принимаемых аргументов, как и сама функция. Метод evaluate так же возвращает значение.
Функция, как объект, имеет некоторые преимущества - метод evaluate позволяет совершать вызов функции из любого участка кода, даже если место вызова находится выше места объявления функции. Метод execute делает то же самое, но при этом порождает новый поток, однако, выполняется быстрее старого доброго ExecuteFunc.
Недостаток же следующий - функция, вызываемая методом evaluate не должна вызывать в своем теле функцию GetTriggeringTrigger (остальные событийные нативки доступны к использованию) или же нативную функцию синхронизации. Так же вызов функции методом evaluate не поддерживает вейты. Более того, вызов функции через evaluate производится медленнее обычного вызова.
Вызывая функцию посредством ExecuteFunc, вы были бы вынуждены использовать глобальные переменные для передачи аргументов. Методы же evaluate и execute позволяют передавать аргументы напрямую.
Взглянем на следующий пример
function A takes real x returns real
if(GetRandomInt(0,1)==0) then
return B(x*0.02)
endif
return x
endfunction
function B takes real x returns real
if(GetRandomInt(0,1)==1) then
return A(x*1000.)
endif
return x
endfunction
В случае Jass, мы получим синтаксическую ошибку, поскольку вызываем функцию B до ее объявления. Использование метода evaluate избавит нас от проблемы
function A takes real x returns real
if(GetRandomInt(0,1)==0) then
return B.evaluate(x*0.02)
endif
return x
endfunction
function B takes real x returns real
if(GetRandomInt(0,1)==1) then
return A(x*1000.)
endif
return x
endfunction
В качестве другого примера, предположим, что нам необходимо уничтожить некоторый эффект спустя какое-то время. По человечески, следует использовать таймер, но для демонстрации мы воспользуемся вейтом. Чтобы не прервать выполнение текущей функции, породим новый поток методом execute
function DestroyEffectAfter takes effect fx, real t returns nothing
call TriggerSleepAction(t)
call DestroyEffect(fx)
endfunction
function test takes nothing returns nothing
local unit u=GetTriggerUnit()
local effect f=AddSpecialEffectTarget("Abilities\\Spells\\Undead\\Cripple\\CrippleTarget.mdl",u,"chest")
call DestroyEffectAfter.execute(f,3.0)
set u=null
set f=null
endfunction
Имя функции
Функция так же обладает специальным атрибутом name типа string, который хранит в себе полное имя этой функции, генерируемое с учетом всех префиксов. Вы можете использовать этот атрибут для вызова функции через ExecuteFunc, например в областях и библиотеках, которые изменяют действительное имя функции при указании модификатора доступа
scope test
public function xxx takes nothing returns nothing
call BJDebugMsg(xxx.name) // Выведет "test_xxx"
endfunction
endscope
Интерфейс функций
Поскольку функция является объектом, мы можем объявить интерфейс функции. Синтаксис следующий:
function interface имя takes (аргументы) returns (типВозвращаемогоЗначения)
что близко к простому об]явлению функции.
В результате, вы можете объявить переменную типа интерфейса функции и присвоить ей значение, в виде указателя на конкретную функцию. Звучит непонятно, давайте взглянем на пример:
function interface Arealfunction takes real x returns real
function double takes real x returns real
return x*2.0
endfunction
function triple takes real x returns real
return x*2.0
endfunction
function Test1 takes real x, Arealfunction F returns real
return F.evaluate(F.evaluate(x)*F.evaluate(x))
endfunction
function Test2 takes nothing returns nothing
local Arealfunction fun = Arealfunction.double // Синтаксис получения указателя на функцию
call BJDebugMsg( R2S( Test1(1.2, fun) ))
call BJDebugMsg( R2S( Test1(1.2, Arealfunction.triple ) )) // Так же допустимо
endfunction
Во время компиляции, интерфейс функции получает в качестве статичного аргумента (подобно статичным аргументам структур) все функции в коде карты, чьи сигнатуры совпадают с сигнатурой этого интерфейса (то есть, список принимаемых аргументов и тип возвращаемого значения). В примере выше, функция double и triple имеют сигнатуру, идентичную сигнатуре интерфейса Arealfunction, что делает их статичными аргументами этого интерфейса. А строчкой
local Arealfunction fun = Arealfunction.double // Синтаксис получения указателя на функцию
мы получаем в переменную указатель на конкретную функцию, соответствующую указанному интерфейсу. Впоследствии, мы можем передавать эту переменную в другую функцию и вызывать методы evaluate и execute описанные ранее.
Вы так же можете получить указатель на функцию не указывая имя интерфейса,
function double takes real x returns real
return 2*x
endfunction
function square takes real x returns real
return x*x
endfunction
function interface realfunc takes real x returns real
function repeater3 takes real x, realfunc F returns real
set x=F.evaluate(x)
set x=F.evaluate(x)
set x=F.evaluate(x)
return x
endfunction
function test takes nothing returns nothing
local real x = repeater3( 2.0, double) // Заметьте, мы используем имя функции в качестве значения
local real y = repeater3( 2.0, square)
/// Да, интерфейс функции позволяет использовать функции в качестве разновидности переменной
endfunction
Мы так же можем производить приведение типом (о чем в соответствующем разделе), для преобразования указателя на функцию к целому числу и обратно.
Метод как объект
Метод структуры тоже можно рассматривать в качестве объекта, они так же обладают методом evaluate, execute и атрибутом name
struct mystruct
static method mymethod takes nothing returns nothing
call BJDebugMsg("this works")
endmethod
endstruct
function myfunction takes nothing returns nothing
call ExecuteFunc(mystruct.mymethod.name) // ВЫзов через ExecuteFunc и конечное имя метода
endfunction
Методы так же обладают атрибутом exists типа boolean, который имеет значение ture, если метод существует или false иначе. Данный атрибут используется для проверки, реализует ли структура тот или иной метод расширяемого интерфейса
interface MyInterface
method myMethod1 takes nothing returns nothing
method myMethod2 takes nothing returns nothing defaults nothing
endinterface
struct MyStruct extends MyInterface
method myMethod1 takes nothing returns nothing
/*
Инструкции...
*/
endmethod
endstruct
function Test takes nothing returns nothing
local MyStruct tmp = MyStruct.create()
// Выведет:
// yes
// no
if (tmp.myMethod1.exists) then
call BJDebugMsg("Yes")
else
call BJDebugMsg("No")
endif
if (tmp.myMethod2.exists) then
call BJDebugMsg("Yes")
else
call BJDebugMsg("No")
endif
call tmp.destroy()
endfunction
Edited by Vladimir TVK
Edited by PT153
Что касается времени вызова. Конечно, там разница смешная. Но эта разница играет роль при многократном вызове и может выстрелить в каком-то узком месте программы. Проблема здесь не в инструменте, а в программисте, который его неправильно использует.
Edited by ScorpioT1000
Edited by PT153