vJass

Contents:

Содержание главы:

Методы функции

В 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 

`
LOADING AD...

Only a small set of comments around the pointed one shown. Go to actual.
0
28
2 months ago
0
"поиск" присутствует только в контексте обычных структур, но это даже не поиск, потому что vJass прекрасно знает, какой индекс свободен во время аллокации новой структуры указанного типа. Выходить за лимит в 8к не рекомендуется, иначе во время каждого обращения к атрибуту или перезаписываемому методу будет происходить резолв массива по индексу структуры.

ScorpioT1000, так а причём тут структуры, если речь про функции и их "интерфейсы"? Структуры другой разговор.
0
9
2 months ago
0
evaluate не создаёт.
Тестил недавно, 200к операций через evaluate несколько раз, все ок работает. Через обычный вызов функции не работает. Выше владимир тоже написал о том, что создается триггер.
Vladimir TVK:
в собранном проекте под неё делается отдельная функция-обёртка и триггер.
0
28
2 months ago
Edited by PT153
0
Функция, как объект, имеет некоторые преимущества - метод evaluate позволяет совершать вызов функции из любого участка кода, даже если место вызова находится выше места объявления функции. Метод execute делает то же самое, но при этом порождает новый поток, однако, выполняется быстрее старого доброго ExecuteFunc.
И может так и было, когда JassHelper только создавался, но я сейчас сам протестировал на 1.26, TriggerEvaluate действительно создаёт свой поток. Тогда в execute() нет смысла кроме вейтов.
Забавный факт: execute() не возвращает значение, даже если оригинальная функция это делает. Почему? Vexorian решил, что это не нужно. 🤣

Не удивлюсь, если TriggerExecute на самом деле нифига не быстрее ExecuteFunc.
Uploaded files
0
10
2 months ago
0
Опять люди теоретической информатикой занимаются вместо того чтобы карты делать 😞
Показали бы конкретику - карту без этой фичи и с ней, и что она конкретно дает разрабу, а главное конечному пользователю (игорьку).
0
37
2 months ago
0
PT153, без нового потока смысл был бы нулевой в этой функции. Но я обычно ExecuteFunc юзал для таких вещей
Slonick, это полезно для инициализации больших баз данных, потому что движок рвет поток после N операций. Например, я создавал интерфейс инвентаря и дерева талантов в jc и обычного потока не хватало, чтобы создать всё, приходилось перезапускать поток инициализации через ExecuteFunc
А для периодических действий типа движения всё и так на обычных таймерах крутится, там такой проблемы нет
0
28
2 months ago
Edited by PT153
0
без нового потока смысл был бы нулевой в этой функции.
Абсолютно нет, это всё ещё вызов условий триггера и всё ещё вызов функции из любого места кода (о чём и говорится в цитате из этой статьи).
Я всегда считал, что TriggerEvaluate не создаёт свой поток, поэтому эта функция и быстрее TriggerExecute. И поэтому для создания потоков я использовал .execute(). А оно вон как на самом деле: TriggerExecute медленный из-за поддержки вейтов.
Показали бы конкретику - карту без этой фичи и с ней, и что она конкретно дает разрабу
Читаем статью, получаем ответ на вопрос.
Only a small set of comments around the pointed one shown. Go to actual.
To leave a comment please sign in to the site.