XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Желтая пресса: обучающие статьи
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

 
Van Damm
wait... what?
offline
Опыт: 22,268
Активность:
ExecuteFunc

1. Что такое ExecuteFunc?

ExecuteFunc – это просто native-функция, но она - одна из самых полезных.
Она берёт аргументом имя функции и запускает её в другом потоке. Это значит что запущенная функция может использовать ожидания без прерывания потока вызова, и, так как она запомнит ответы на события, это позволяет вам использовать ожидания при вызовах ForGroup и TimerStart, к примеру. Более того, это – единственный способ получить форвардные ссылки в JASS.

2. Какие функции я могу использовать с ней?

Вы можете использовать ExecuteFunc с любыми функциями без аргументов (takes nothing), функция может быть постоянной и даже может возвращать значение, но вы не сможете его прочитать.
Как сказано выше, native-функция ExecuteFunc позволяет форвардные ссылки, поэтому код вызываемой функции может находиться ниже её вызова через ExecuteFunc.

3. Как в точности мне её вызывать?

Вызвать ExecuteFunc очень легко, если вы знаете JASS. Вы просто вызываете её с именем нужной функции в качестве строкового аргумента. Аргумент – имя функции чувствителен к регистру и должен быть правильно написан, в другом случае это приведёт к крушению. Вы можете использовать такие вещи, как конкатенация (сляиние строк), главное – быть уверенным, что строка верная. Вызов ExecuteFunc не приведёт к крушению, если в качестве аргумента использовать null или “” (пустая строка).
Вы можете передать ей имя функции, содержащее вызов в качестве аргумента, но будьте уверены, что вы используете ожидания, или что-то для предотврашения создания бесконечного цикла без пауз, что может привести к крушению.

4. Хочу примеров!

Базовый пример вызова:
function Hello takes nothing returns nothing
    call ExecuteFunc("Hi")
    call BJDebugMsg("превед")
endfunction

function Hi takes nothing returns nothing
    call BJDebugMsg("хай")
endfunction
Когда выполняется функция Hello, покажется текст:
хай
превед
Причина, по которой "хай" отображается раньше, чем "превед" – то, что вызов ExecuteFunc в функции Hello расположен над вызовом функции BJDebugMsg. Это также пример форвардных ссылок с использованием ExecuteFunc.
Функцию Hi можно было без проблем заменить этой функцией:
constant function Hi takes nothing returns string
    return "Это глупо, но возможно."
endfunction
Ранее я говорил об ожидании при использовании ForGroup. Пример:
function Enum2 takes nothing returns nothing
    local unit e = GetEnumUnit() // Сохранить юнита, над которым проводится операция, в локальной переменной
    call TriggerSleepAction(5) // Подождать 5 секунд
    call KillUnit(e) // Убить юнита
    set e = null // Установит локальную переменную в null чтобы избежать утечек памяти
endfunction

function Enum takes nothing returns nothing
    call ExecuteFunc("Enum2") // Давайте вызовем функцию Enum2 чтобы разрешить ожидания.
endfunction

...
    call ForGroup(somegroup, function Enum) // убьёт всех юнитов группы ‘somegroup’ через 5 секунд реального времени.
...
Обычно вызов TriggerSleepAction остановит выполнение ForGroup, но не здесь, так как ожидания находятся в функции Enum2, которая запускается в параллельном потоке через ExecuteFunc.
Это также показывает, что ответы на события, типа GetEnumUnit отлично работают с ExecuteFunc.

5. Есть ли проблемы, связанные с использованием ExecuteFunc?

Да. Она вызовет крушение карты в игре, если аргумент – имя функции не является действительным именем функции, но это не случится, если пользователь знает, что делает.
Другая проблема случается, когда вы используете функцию обфускации кода программ для защиты карты типа Heavy Locker или Extprotect, которая даёт функциям случайные имена. Так как они не изменяют содержимое строк, имеющих такое же имя, как и функция, это приведёт к крушениям при вызове ExecuteFunc.
В этом случае решением может быть Vexorian’s Map Optimizer, если вы используете строки напрямую (без слияния или вещей такого же типа). Другим решением будет избегать использования этих программ, или хотя бы избегать использования их методов обфускации.

Это перевод статьи Blade.dk с сайта WC3Jass.com
Перевод был сделан мной ещё три года назад, пока сейчас не попросили выложить его как статью ^^


Также имеется альтернативный перевод от товарища NCrashed.
» Читать

Примечание переводчика

Данная статья является свободным переводом оригинала (автор Blade.dk) . Материал этой статьи очень полезен для новичков в jass'е. Главная мысль статьи: вы сможете организовывать задержку в пике юнитов ForGroup (да и в любых call-back функциях) и в действиях таймеров.

Содержание

  1. Что делает ExecuteFunc?
  2. Какие функции можно использовать с ней?
  3. Как ее правильно вызвать?
  4. Примеры использования
  5. Проблемы и способы их решения

Что делает ExecuteFunc?

Это простая нативка, которая зашита в движок игры, но она является одной из самых полезных. Она берет строку, имя функции, в качестве аргумента и запускает ее в другом потоке. Замечательное свойство ExecuteFunc в том, что она запоминает все локальные переменные от событий(GetEnumUnit(),GetTriggerUnit() и тп).
Это означает, что вызываемая таким образом функция может использовать задержку (TriggerSleepAction или PolledWait) без прерывания текущего потока. Таким образом можно устроить задержку в ForGroup и действиях таймеров. И это единственный способ в jass вызвать функцию до ее объявления.

Какие функции можно использовать с ней?

Лучше использовать функции, которые ничего не возвращают и ничего не берут, конечно, можно вызвать функцию, которая берет аргумент, но вы не сможете забрать результат выполнения функции (return something), единственный способ забрать это значение – передать его через глобальную переменную.
Как сказано выше, с помощью ExecuteFunc можно вызвать функцию еще до ее объявления в коде.
Еще немаловажный момент: использовать можно только постоянные функции, то есть у вас не получиться вызвать таким образом функцию, сохраненную в переменную типа code, прописав как аргумент имя этой переменной.

Как ее правильно вызвать?

Если вы знаете, как вызывать функции в jass, то это не составит проблем. Просто берем имя нужной функции в кавычках и вставляем внутрь ExecuteFunc.
call ExecuteFunc(“DoNothing”)
Имя функции чувствительно к регистру! Если вместо DoNothing написать donothing, то игра вылетит с фаталом. Игра не вылетит, если вместо аргумента поставить пустую строку "".
Также можно использовать операцию сложения строк:
cal ExecuteFunc(“Do”+”Nothing”)
Также можно организовать рекурсию, вызывая в функции ее же через ExecuteFunc. Только надо следить, чтобы был выход из этого бесконечного цикла самовызова. (прим. переводчика: А оно вам надо? Рекурсия в jass организуется и обычным самовызовом, и, вообще, рекурсию надо избегать из-за ее неэффективности)

Примеры использования

Самое простое:
function Hello takes nothing returns nothing
    call ExecuteFunc("Hi")
    call BJDebugMsg("Hello")
endfunction

function Hi takes nothing returns nothing
    call BJDebugMsg("Hi")
endfunction
В результате вызова функции Hello на экран будет выведено:
Hi
Hello
Это отличный пример вызова функции до ее описания.
Без проблем можно функцию Hi заменить на:
""constant function Hi takes nothing returns string
return "Это глупо, но работает."
endfunction
Функция ничего не выведет, и забрать строку вы не сможете.

Пример организации задержки в пике:
function Enum2 takes nothing returns nothing
local unit e = GetEnumUnit() Сохранение взятого юнита в локальную переменную
call TriggerSleepAction(5) Ждем 5 секунд
call KillUnit(e) Убиваем юнита
set e = null Обнуляем переменную, чтобы не возникло утечки
endfunction
function Enum takes nothing returns nothing
call ExecuteFunc("Enum2") Вызываем Enum2, чтобы могли использовать задержку
endfunction
...
call ForGroup(someGroup, function Enum) Через 5 секунд все юниты из группы умрут
...
""
Без нашей хитрой уловки TriggerSleepAction вызвало бы прерывание выполнения функции. Но задержка у нас в Enum2, которая запускается через ExecuteFunc в другом потоке.

Проблемы и способы их решения

Если неправильно использовать ExecuteFunc, то вызвать фатал легче простого, но это не произойдет, если вы будете осознавать что делаете.
Другая проблема связана с протектом карт с помощью, например, Heavylocker's или Extprotect's с включенной функцией “запутывания кода” или просто шифрованием имен функций. В итоге имена функций меняются, а строка аргумента ExecuteFunc нет. Это приводит к фаталам.
Решений проблемы несколько:
  1. Использование Vexorian's Map Optimizer. Он заменит имя функции и в постоянных строках. Однако нельзя будет использовать сумму строк.
  2. Избегание использование таких методов защиты.

Отредактировано Van Damm, 08.11.2009 в 23:27.
Старый 01.11.2009, 00:10
Hellfim
Новичок
offline
Опыт: 79,707
Активность:
это приведёт к крушению.
На самом деле смешно звучит =)
Почерпнул обход слипов в энуме, я нуб, раньше этого не знал -.-
Старый 01.11.2009, 01:09
NCrashed

offline
Опыт: 13,553
Активность:
Статья очень полезна на практике, но перевод немножко дубоватый, некоторые чисто английские формулировки можно было переделать на наш могучий. Например:
"Другая проблема случается" на "Другая проблема связана с"
Старый 01.11.2009, 01:11
DragonSpirit
у - уходи
offline
Опыт: 22,625
Активность:
Статья хорошая,но местами грубо переведено =\
Старый 01.11.2009, 01:17
NCrashed

offline
Опыт: 13,553
Активность:

Примечание переводчика



Данная статья является свободным переводом оригинала (автор Blade.dk) . Материал этой статьи очень полезен для новичков в jass'е. Главная мысль статьи: вы сможете организовывать задержку в пике юнитов ForGroup (да и в любых call-back функциях) и в действиях таймеров.

Содержание

  1. Что делает ExecuteFunc?
  2. Какие функции можно использовать с ней?
  3. Как ее правильно вызвать?
  4. Примеры использования
  5. Проблемы и способы их решения

Что делает ExecuteFunc?



Это простая нативка, которая зашита в движок игры, но она является одной из самых полезных. Она берет строку, имя функции, в качестве аргумента и запускает ее в другом потоке. Замечательное свойство ExecuteFunc в том, что она запоминает все локальные переменные от событий(GetEnumUnit(),GetTriggerUnit() и тп).
Это означает, что вызываемая таким образом функция может использовать задержку (TriggerSleepAction или PolledWait) без прерывания текущего потока. Таким образом можно устроить задержку в ForGroup и действиях таймеров. И это единственный способ в jass вызвать функцию до ее объявления.

Какие функции можно использовать с ней?



Лучше использовать функции, которые ничего не возвращают и ничего не берут, конечно, можно вызвать функцию, которая берет аргумент, но вы не сможете забрать результат выполнения функции (return something), единственный способ забрать это значение – передать его через глобальную переменную.
Как сказано выше, с помощью ExecuteFunc можно вызвать функцию еще до ее объявления в коде.
Еще немаловажный момент: использовать можно только постоянные функции, то есть у вас не получиться вызвать таким образом функцию, сохраненную в переменную типа code, прописав как аргумент имя этой переменной.

Как ее правильно вызвать?



Если вы знаете, как вызывать функции в jass, то это не составит проблем. Просто берем имя нужной функции в кавычках и вставляем внутрь ExecuteFunc.
Код:
call ExecuteFunc(“DoNothing”)

Имя функции чувствительно к регистру! Если вместо DoNothing написать donothing, то игра вылетит с фаталом. Игра не вылетит, если вместо аргумента поставить пустую строку "".

Также можно использовать операцию сложения строк:
Код:
cal ExecuteFunc(“Do”+”Nothing”)

Также можно организовать рекурсию, вызывая в функции ее же через ExecuteFunc. Только надо следить, чтобы был выход из этого бесконечного цикла самовызова. (прим. переводчика: А оно вам надо? Рекурсия в jass организуется и обычным самовызовом, и, вообще, рекурсию надо избегать из-за ее неэффективности)

Примеры использования



Самое простое:

Код:
function Hello takes nothing returns nothing
    call ExecuteFunc("Hi")
    call BJDebugMsg("Hello")
endfunction

function Hi takes nothing returns nothing
    call BJDebugMsg("Hi")
endfunction

В результате вызова функции Hello на экран будет выведено:
Hi
Hello
Это отличный пример вызова функции до ее описания.

Без проблем можно функцию Hi заменить на:
Код:
constant function Hi takes nothing returns string
    return "Это глупо, но работает."
endfunction

Функция ничего не выведет, и забрать строку вы не сможете.

Пример организации задержки в пике:
Код:
function Enum2 takes nothing returns nothing
    local unit e = GetEnumUnit() // Сохранение взятого юнита в локальную переменную
    call TriggerSleepAction(5) // Ждем 5 секунд
    call KillUnit(e) // Убиваем юнита
    set e = null // Обнуляем переменную, чтобы не возникло утечки
endfunction

function Enum takes nothing returns nothing
    call ExecuteFunc("Enum2") // Вызываем Enum2, чтобы могли использовать задержку
endfunction

...
    call ForGroup(someGroup, function Enum) //  Через 5 секунд все юниты из группы умрут
...


Без нашей хитрой уловки TriggerSleepAction вызвало бы прерывание выполнения функции. Но задержка у нас в Enum2, которая запускается через ExecuteFunc в другом потоке.

Проблемы и способы их решения



Если неправильно использовать ExecuteFunc, то вызвать фатал легче простого, но это не произойдет, если вы будете осознавать что делаете.
Другая проблема связана с протектом карт с помощью, например, Heavylocker's или Extprotect's с включенной функцией “запутывания кода” или просто шифрованием имен функций. В итоге имена функций меняются, а строка аргумента ExecuteFunc нет. Это приводит к фаталам.
Решений проблемы несколько:
  1. Использование Vexorian's Map Optimizer. Он заменит имя функции и в постоянных строках. Однако нельзя будет использовать сумму строк.
  2. Избегание использование таких методов защиты.


Надеюсь, статья помогла кому-нибудь.
- Blade.dk

Отредактировано NCrashed, 01.11.2009 в 02:16.
Старый 01.11.2009, 02:09
Rewenger
The culprit will not die
offline
Опыт: 35,873
Активность:
Вах ты, теперь я могу юзать слип в енуме. Огромное спасибо за статью.
NCrashed, а зачем 2-я статья, да ещё и с большим количеством букв?..
Старый 01.11.2009, 06:24
_Red

offline
Опыт: 4,095
Активность:
норм стастья, спасибо!
Rewenger, вторая статья - это типо он перевел лутче
Старый 01.11.2009, 07:32
NCrashed

offline
Опыт: 13,553
Активность:
Информации больше и перевод сделал хорошо. Не пропадать теперь статье, тем более все не знали по сабжу. Самое интересное, что в вызванной функции работают GetEnumUnit(), GetTriggeringTrigger() и остальные
Старый 01.11.2009, 09:58
FunkieFoO

offline
Опыт: 7,159
Активность:
Зачот аффтар +1, норм статья, не знал что ф-ию можно вызвать до ее обьявления... ЗЫ а если я буду вызывать ф-ию таким образом из др. ф-ии. то та ф-ия из которой я ее вызывал примет параметры вызываемой ф-ии? я так все пнял?
Старый 01.11.2009, 10:52
NCrashed

offline
Опыт: 13,553
Активность:
Она примет значения всех локальных переменных от событий GetEnumUnit, GetTriggeringSomething и тп
Старый 01.11.2009, 11:47
Toadcop

offline
Опыт: 54,313
Активность:
Цитата:
Более того, это – единственный способ получить форвардные ссылки в JASS.

о рили ? О_О

+ самое важное Слипы древние зло... и их не стоит использовать ибо получить десинк легче простого в более сложных картах.

что я бы заметил.

1) очень удобно и универсально
2) почти всегда стоит делать проверку на null (строки) если действие не статичное
3) медлено. в моих (неоднократных) тестах это было ~ 27x (неких едениц времени но важно соотношение) чтение с переменой ~0.6х чтение из кеша если значения статик (не перменые) ~4.5x call SetUnitPosition ~250x вызов триггера через TriggerEvaluate ~12.5x

т.е. TriggerEvaluate самый быстрый способ запустить "произвольную функцыю" но он вроде не создаёт поток. хотя на практике с этим у меня некогда проблем небыло.

но всё же для не чястых запусков произвольной функции ExecuteFunc круче, точней удобней и универсальней. где важна производительность надо юзать как выше писал TriggerEvaluate
Старый 01.11.2009, 13:34
NCrashed

offline
Опыт: 13,553
Активность:
Еще он может быть полезен для разбиения циклов на несколько частей, чтобы вар не убивал поток.
Старый 01.11.2009, 13:40
Van Damm
wait... what?
offline
Опыт: 22,268
Активность:
местами грубо переведен
мне было лень сейчас перечитывать и править =)
Старый 01.11.2009, 15:09
adic3x

offline
Опыт: 108,439
Активность:
TriggerEvaluate самый быстрый способ запустить "произвольную функцыю" но он вроде не создаёт поток
создает, но минус в том, что триггер надо инитить
где важна производительность надо юзать как выше писал TriggerEvaluate
или переписать код под несколько ифов и обычный калл/инлайн - будет быстрее
Старый 02.11.2009, 14:16
Toadcop

offline
Опыт: 54,313
Активность:
Цитата:
или переписать код под несколько ифов и обычный калл/инлайн - будет быстрее

это если известны функцыи которые могут быть запущены...
Старый 02.11.2009, 14:30
adic3x

offline
Опыт: 108,439
Активность:
это если известны функцыи которые могут быть запущены...
в триггер евалуате они тоже дб известны (и добавленны как условия к соотв. триггерам)
Старый 02.11.2009, 14:35
alexkill

offline
Опыт: 18,872
Активность:
в целом статья хорошая, собрано воедино то, что часто обсуждалось на форуме,
кстати, об ожидании при использовании ForGroup() я еще год назад писал:
Старый 02.11.2009, 16:23
Van Damm
wait... what?
offline
Опыт: 22,268
Активность:
alexkill, посмотри на дату моего оригинального поста =)
Старый 05.11.2009, 00:57
XOR

offline
Опыт: 38,159
Активность:
Статья тру, имхо нужно версию NCrashed a залить под кат в первый пост
Старый 05.11.2009, 15:25
Van Damm
wait... what?
offline
Опыт: 22,268
Активность:
XiMiKs:
Статья тру, имхо нужно версию NCrashed a залить под кат в первый пост
done
Старый 08.11.2009, 23:27

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы можете скачивать файлы

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 15:27.