В рамках статьи будут рассмотрены шаги по написанию кода простого элемента для пакета CNET.
Какой элемент понимается под простым?
Под простым понимается элемент типа "Копирование строки":
Add(Copy,1951780,567,238)
{
}
У элементов данного класса всегда есть минимум один метод, одно событие, Var точка, которая возвращает результат выполнения метода и набор Data точек для считывания параметров вызываемого метода.
Архитектура простых элементов в пакетах *TCG
Во всех *TCG пакетах на данный момент времени работает примерно одинаковый алгоритм генерации кода для элементов данного типа. Заключается он в следующем: если разработчик использует в своей схеме исключительно метод элемента и соответствующее ему событие, то весь код, генерируемый методом отдается как есть в поток. Рассмотрим этот случай:
Итогом генерации кода данного элемента должна быть строка вида:
#cpp
a.Substring(b,c)
#cpp
editN.Text = a.Substring(b,c);
Далее если разработчик использует в схеме только нижнюю точку (обычно это Result), то элемент сначала эмулирует вызов своего метода и потом уже отдает результаты его работы в поток. Например:
при таком включении код получается точно такой же(при этом мы считаем, что точка Result используется в схеме только один раз):
#cpp
editN.Text = a.Substring(b,c);
И наконец последняя ситуация: разработчик использует все точки элемента (метод, событие и Result):
в этой схеме сгенерировать правильный код без введения дополнительной внутренней переменной невозможно. Поэтому код должен получаться примерно таким:
#cpp
// вызов метода doCopy, в который в качестве исходной строки передаются данные с точки Result этого же элемента
stringVarN = stringVarN.Substring(b,c);
// отдаем в поток stringVarN на точку onCopy
...
// итог передачи stringVarN в поток события onCopy
editN.Text = stringVarN;
API для реализации архитектуры простых элементов
Поскольку 90% кода таких элементов уходит на реализацию общей логики, описанной в предыдущем абзаце, то логично бы было отделить ее от кода элемента и оставить в нем только ту часть, ради выполнения которой он и создавался.
Делается это с использованием специальных методов объекта sys (реализацию которого можно всегда посмотреть в файле code/hiSys.hws):
sys._se_init(<Point name>, <var name>, <var type>)
настраивает контекст текущего элемента для реализации логики, описанной выше (т.е. создает внутреннюю переменную, если она нужна, добавляет методы doXXX и Result). Вызов метода желательно делать из init() элемента
<Point name> - общее имя точек doXXX и onXXX (должно совпадать с названием этих точек в конфиге элемента)
<var name> - имя внутренней переменной (нужно исключительно для читаемости конечного кода)
<var type> - тип данных внутренней переменной
_se_make([args])
вызывается объектом sys для получения кода элемента (фактически именно в этом методе и должна быть вся та работа, которую выполняет элемент). В качестве параметров передаются данные из потока точки doXXX. После выполнения метод должен вернуть готовый код.
Собственно код элемента с использованием описанного API:
#hws
func init
sys._se_init('Copy', 'string', 2)
end
func _se_make(srcstr, position, count)
return('(' && d("SrcStr") && ').Substring(' && d("Position") && ',' && d("Count") && ')')
end
вот так выглядит тот же код с использованием чистого RTCG:
#hws
func init
if(linked("Result") and linked("doCopy"))
sys.add_var_extern('string', 2)
end
end
func make(srcstr, position, count)
return('(' && d("SrcStr") && ').Substring(' && d("Position") && ',' && d("Count") && ')')
end
func doCopy(srcstr, position, count)
if(linked("Result"))
blk.println(this.string, ' = ', make(srcstr, position, count), ';')
event("onCopy", this.string)
else
event("onCopy", make(srcstr, position, count))
end
end
func Result
if(linked("doCopy"))
return(this.string)
else
return(make())
end
end