Архив рубрики: Control_Grundlagen

certificateIsPermanentlyAccepted()

Функция certificateIsPermanentlyAccepted() возвращает значение TRUE, если данный сертификат уже был принят на постоянной основе, например, пользователем.

Краткое описание

bool certificateIsPermanentlyAccepted(дайджест строки);

Параметр

ПараметрОписание
digestСодержит строку, представляющую вычисленное значение дайджеста для этого сертификата, которое используется для однозначной идентификации этого сертификата. Используйте функции net(), netGet(),netHead()netPost() или netPut() чтобы вернуть сопоставление sslErrors2, содержащее дайджест. sslErrors2 возвращается в случае ошибок SSL.

Возвращаемое значение

Функция certificateIsPermanentlyAccepted() возвращает значение TRUE, если данный сертификат уже был принят на постоянной основе.

Описание

Функция certificateIsPermanentlyAccepted() возвращает значение TRUE, если данный сертификат уже был принят на постоянной основе, например, пользователем в диалоговом окне при обращении к HTTP-серверу. Функция служит для обнаружения ошибок сертификата.

пример

В этом примере проверяется, был ли сертификат принят на постоянной основе. Сначала вызывается netGet(), которая возвращает дайджест в сопоставлении в случае ошибок SSL. Сопоставление sslErrors2 содержит дайджест.


main(mapping event)
{
mapping m;
netGet("https://localhost:443/pictures/picture.png", m, makeMapping("target","D:/Test"));
DebugN(m);
}

main(mapping event)
{
DebugN("certificateIsPermanentlyAccepted", certificateIsPermanentlyAccepted("52bfdc363d0832f1a38cfcb612779bf69e4c8d8d"));
}

Используется в

Функции безопасности

Доступно

UI

См. также

certificateAcceptPermanently()

base64Decode()

Декодирует данные в кодировке base64.

Краткое описание

int base64Decode(кодирование строки, строка | большой двоичный объект и декодирование);

Параметр

ПараметрОписание
encodedДанные, которые должны быть декодированы.
decodedВозвращаемые декодированные данные.

Возвращаемое значение

Возвращает 0 при успешном выполнении или -1, если указанные данные недопустимы (не соответствуют правилам кодирования base64)

Описание

Функция декодирует данные в кодировке base 64 и возвращает их либо в виде строки, либо в виде большого двоичного объекта.

Если закодированные данные содержат 0 байт, то типом данных для декодированной информации должна быть не строка, а большой двоичный объект, поскольку строка не может обрабатывать 0 байт. В этом случае возвращаемое значение функции равно -1.

Используется в

Различные функции

Доступно

UI, CTRL

См. также

base64Encode()

Семантической контроль сценариев

Семантическая проверка сценариев в «КАСКАД Цифра» осуществляется с помощью следующих вопросов (проверка без гарантии полноты):

  • Известны ли переменные?
  • Доступны ли переменные? (индивидуальные)
  • Известны ли $-параметры?
  • Известный ли функции?
  • Доступны ли функции? (индивидуальные)
  • Количество аргументов.
  • Доступно ли объявление переменной уже известной переменной (например, локальная переменная имеет то же имя, что и глобальная переменная)?
  • Можно ли получить доступ к функции посредством внешнего вызова функции, написанного в C++?
  • Существует ли пустое присваивание после присваиваний «while» или «for» (например, «while (foo) ; bar();«).
  • Допускается присваивание в качестве условия в «if», «do», «while» или «for». Например, «if (i = 2) foo();» «if ( (i = 2) ) foo();«.
  • Значение в присваивании, например, dpConnect() = 3;

Семантическую проверку можно использовать следующим образом:

WCCOActrl -syntax -proj [projName] [scriptName]

Например, в случае необходимости проверки сценария, называемого «SynCheck.ctl» в проекте «Proj_3.10«, вызов менеджера сценариев осуществляется следующим образом:

WCCOActrl -syntax -proj Proj_3.10 SynCheck.ctl

Все ошибки (PRIO_SEVERE) и все предупреждения (PRIO_WARNING) демонстрируются в средстве просмотра журналов. Результат семантической проверки может выглядеть, например, следующим образом:

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, WARNING,    12/ctrl, External declaration of function «dpConnect» shadows definition at a.ctl:10.

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, SEVERE,     73, Variable not defined, a.ctl    Line: 25, undefined_var

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, SEVERE,     76, Invalid argument in function, a.ctl    Line: 28, private_var defined in b.ctl is private in this context.

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, SEVERE,     17/ctrl, Cannot find dollar parameter $I. Location: a.ctl    Line: 32

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, SEVERE,     72, Function not defined, a.ctl    Line: 35, undefined_func

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, SEVERE,     76, Invalid argument in function, a.ctl    Line: 38, private_func, private_func defined in b.ctl is private in this context.

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, SEVERE,     75, Argument missing in function, a.ctl    Line: 41, number_of_arg, a2

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, WARNING,    11/ctrl, Declaration of «g» shadows old declaration at «a.ctl    Line: 3». Location: a.ctl    Line: 44

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, WARNING,    13/ctrl, Empty statement found. Location: a.ctl    Line: 51

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, WARNING,    14/ctrl, Assignment used in a condition. Location: a.ctl    Line: 55

WCCOActrl   (0), 2010.11.03 10:50:23.454, CTRL, SEVERE,     78, Assignment to this expression impossible, a.ctl    Line: 58

Ограничения:

  • Менеджер сценариев не может осуществлять проверку сценариев, выполняющихся в пользовательском интерфейсе.
  • Ключевой слово «#uses» следует использовать для объявления всех необходимых библиотек.
  • Функции «addGlobal» и «removeGlobal» не оцениваются. Если глобальные переменные не объявлены как «глобальные», но во время выполнения была добавлена переменная с помощью функции «addGlobal» — будет отображена ошибка.
  • Графические объекты не анализируются.
  • Ошибка отображается, если библиотека Control пытается получить доступ к переменным, объявленным в панели.

Библиотеки в Control

Для сценариев Control можно создать отдельную библиотеку, которая может использоваться всеми сценариями языка Control в проекте. Данную библиотеку Control можно также изменять во время работы. Каждый раз при открытии панели в модуле PARA или посредством диалогового окна выбора данных в модуле VISION менеджер пользовательского интерфейса осуществляет проверку наличия изменений файла библиотеки. В случае если библиотека изменилась, а сценарии на данный момент не выполняются, открывается новый файл библиотеки. Тем не менее, если сценарии выполняются, выдается предупреждение «Library in use» («Библиотека используется»). При тестировании панели при помощи быстрого просмотра, при условии, что панель использует библиотеку CTRL, существует возможность изменения библиотеки CTRL без закрытия окна быстрого просмотра, при этом загрузка измененной библиотеки происходит в фоновом режиме.

Библиотека Control может изменяться пользователем в любом текстовом редакторе. Файл библиотеки можно выбрать непосредственно в обзоре проекта в графическом редакторе. Он загружается непосредственно в редактор сценария модуля GEDI, который также обеспечивает проверку синтаксиса при редактировании файла. Если файл библиотеки содержит синтаксическую ошибку, он не загружается. Однако выводится имя файла и номер строки с ошибкой.

Библиотеку Control также можно открыть в редакторе сценария щелчком на «Edit CTRL lib…» (Редактировать библиотеку CTRL…) в меню «Edit» (Правка) графического редактора. При этом открывается окно выбора файла, в котором можно выбрать нужный файл библиотеки. После этого файл загружается в редактор сценария, в котором можно редактировать библиотеку Control.

ПРИМЕЧАНИЕ

Библиотека Control загружается только раз. Глобальные переменные, определенные внутри библиотеки, для каждого менеджера существуют только один раз.

Сценарии

Сценарии  — это программы, написанные с использованием внутреннего языка «Control», тесно связанные с языком программирования ANSI C. Они интерпретируются системой и, следовательно, не требуют компиляции. Эти сценарии могут использоваться для определения зависимости изменений состояния процесса с определенными ответами, например, изменение настроек устройства или изменения визуализации.

Для задач первого типа, т.е. громоздких программ управления, сценарии можно создавать в любом текстовом редакторе. Менеджер сценария в «КАСКАД Цифра» функционирует как инструмент, не только проверяющий правильность синтаксиса таких сценариев, но также выполняющий функции интерпретатора. Для определения зависимости изменений в переменных процесса с изменениями свойств графических элементов при визуализации из ряда диалоговых окон графического редактора можно открыть специальный Редактор сценария. Он также проверяет правильность синтаксиса написанных в нем программ и обеспечивает поддержку правильности определения цветов, моделей (узоров) заливки, стилей строк и шрифтов, а также адресации точек данных.

Параметры отладки: -dbg CTRL_PERF и -report CTRL_PERF

Для выполнения анализа трудоемкости у сценариев языка программирования CONTROL существуют параметры отладки «-dbg CTRL_PERF» и «-report CTRL_PERF». -dbg CTRL_PERF = период времени, за который исполняется функция, период времени ожидания функция и т.д. (также см. конфигурационные элементы «ctrlMaxTime» и «ctrlMinTime» в разделе «Интерфейс пользователя»).

Начиная с версии 3.7,  «-report CTRL_PERF» также демонстрирует время ожидания событий функциями. Это подразумевает, в качестве результата, наличие следующей информации:

  • период времени блокировки за вызов
  • период времени блокировки предотвращения выполнения кода за вызов

Расчет периода времени блокировки: это период времени, на протяжении которого поток ожидает события. Так поток выявляет необходимую информацию только при выполнении, время блокировки также зависит от того, сколько раз должны выполняться предыдущие потоки. Если, например, для функции «dynUnique» другой поток требует 20 секунд, эти 20 секунд относятся к другим периодам времени блокировки последующих потоков. Выражение, имеющее задержку(1), в этом случае будет иметь время блокировки 20 секунд.

Объектно-ориентированные сценарии (CTRL++)

Усовершенствование CTRL ++ обеспечивает объектную ориентацию для языка сценариев «КАСКАД Цифра».

  • Определяемые пользователем типы переменных struct и class
  • Модификаторы доступа
  • Наследование
  • Полиморфизм
  • Конструкторы
  • Перечисление

Типы переменных, определяемые пользователем — struct & class

Типы переменных struct и class позволяют создавать определяемые пользователем типы данных, которые могут содержать переменные-члены и функции-члены. Члены структуры или класса должны иметь уникальные имена внутри структуры или класса. Следовательно, вы не можете использовать одно и то же имя для функции-члена и переменной-члена.

Модификаторы доступа

Функции — члены и переменные — члены могут быть определены как

  • частные: доступ к закрытым элементам может иметь только класс или структура, которые их определяют.
  • защищенные: доступ к защищенным элементам может осуществляться классом или структурой, которые их определяют, и всеми производными классами или структурами.
  • общедоступные: к общедоступным элементам может быть доступен любой скрипт в пределах области видимости, в которой определен экземпляр этого класса или структуры.

Разница между структурой и классом заключается в модификаторе доступа по умолчанию:

  • Члены структуры по умолчанию являются общедоступными.
  • Членам класса по умолчанию присвоено значение private.

ПРИМЕР

// definitions

struct EngineHandle

{

string dpName;  // defaults to public

};

// example calls

EngineHandle eHandle;

eHandle.dpName = “engine_001″; // OK, sets dpName of eHandle to engine_001

// definitions

class Valve

{

public open()  { state = 2; }

public close() { state = 1; }

public int getState() { return state; }

int state; // defaults to private

};

// example calls

Valve v;

int valveState;

v.open(); // OK, sets state of v to 2

valveState = v.state; // Error: accessing private member

valveState = v.getState(); // OK, assigns 2 to valveState

In addition, nested data types are also possible (e.g. class in class, struct in class etc.).

ПРИМЕР

// definitions

class Engine

{

public Valve v; // nested class: public member v is of type Valve

};

// example calls

Engine eng;

int valveState;

eng.v.open(); // OK

valveState = eng.v.getState(); // OK

Инициализация

Переменные-члены (статические и нестатические) могут быть инициализированы с помощью выражения. Если выражение является константой, анализатор напрямую вычислит значение и использует его как значение по умолчанию для элемента.

Инициализаторы, которые не являются постоянными, будут оцениваться во время создания объекта, что означает, что вы можете использовать вызов функции для инициализации элемента.

Лучшая практика: это должно выполняться только в функции Object() { [машинный код] }:

ПРИМЕР

// definitions

struct EngineHandle

{

string dpName = “undefined»;

};

Статические члены класса инициализируются запуском своего выражения инициализатора во временной внутренней функции таким же образом, как и глобальные скрипты. Следовательно, в принципе, также разрешен вызов других статических функций и т.д. Учтите следующее ограничение:

Статические члены классов CTRL ++, определенные в библиотеках CTRL, глобальные переменные менеджера («global» в библиотеках) и переменные, которые копируются из библиотек в каждый скрипт (например, int x = init(); в библиотеке lib), не должны использовать функции ожидания в инициализаторах.

В качестве первого шага каждый scopeLib и скрипт запускают инициализаторы в следующем порядке:

  • статические члены класса (порядок следования в классе или между несколькими классами не определен)
  • глобальные экземпляры / переменные в порядке объявления

Это означает, что всякий раз, когда конструктор класса использует статическую переменную своего собственного или другого класса, гарантируется, что статическая переменная уже полностью инициализирована. Пока все инициализаторы не будут завершены, никакие другие потоки не будут выполняться в конкретном сценарии. Если это скрипт на панели и у него есть scopeLib , скрипт не будет выполнять ни один поток в нем, пока не будут завершены все инициализаторы scopeLib.

Более того, когда скрипт на панели пытается запустить общедоступную функцию в PanelRef или другой панели (см. Также invokeMethod()), вызываемая функция не запустится до тех пор, пока scopeLib, содержащий функцию, не завершит инициализаторы.

Наследование

Класс может быть производным от другого класса (только одиночное наследование). Производный класс наследует все элементы базового класса.

ПРИМЕР

// definitions

class Valve

{

public open()  { state = 2; }

public close() { state = 1; }

public int getState() { return state; }

protected string baseType = “Valve»;  // can be accessed from derived class

int state = 1; // private

};

class SlideValve : Valve

{

// access protected base member and own private member

public string getType() { return derivedType + baseType; }

string derivedType = “Slide»;

};

// example calls

SlideValve sv;

int valveState;

sv.open(); // OK, access inherited base method

valveState = sv.getState(); // OK, access inherited base method

DebugN(valveState); // Output: 2

DebugN(sv.getType()); // Output: SlideValve

ПРИМЕЧАНИЕ

Производный класс всегда является производным от базового класса «public».

Члены класса или структуры могут быть статическими, что означает: для всех экземпляров класса существует только один экземпляр этого члена. Можно обращаться к статическому общедоступному члену с помощью обозначения области видимости, например Name::member (см. Пример ниже).

ПРИМЕР

// definitions

class Valve

{

public static int classId = 255;  // can be accessed through scope notation

};

class SlideValve : Valve

{

// inherits static member

};

// example calls

DebugN(Valve::classId); // Output: 255

DebugN(SlideValve::classId); // Output: 255

Перегрузка

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

ПРИМЕР

// definitions

class Valve

{

public int getClassLevel() { return 1; }  

};

class SlideValve : Valve

{

public int getClassLevel()

{

return Valve::getClassLevel() + 1;

}

};

// example calls

Valve v;

SlideValve sv;

DebugN(v.getClassLevel()); // Output: 1

DebugN(sv.getClassLevel()); // Output: 2

Этот пример также показывает, что если имя уже существует в базовом классе, вы можете использовать его снова для переменной-члена производного класса. В этом случае производная реализация скроет реализацию базового класса. Вы должны использовать оператор scope для вызова переменной или функции базового класса.

Нестатические функции-члены могут вызываться с помощью ключевого слова «this». Эта специальная локальная константа указывает на объект, который выполняет функцию-член.

функция Object() { [машинный код] }

У каждого класса может быть метод конструктора, у которого не объявлен возвращаемый тип и то же имя, что и у класса.

Внутри конструктора инициализируются члены класса (см. Пример ниже).

ПРИМЕР

// definitions

class Valve

{

public Valve()

{

state = 1;

}

public int getState() { return state; }

int state;

};

// example calls

Valve v;

DebugN(v.getState()); // Output: 1

Когда пользователь явно не определяет конструктор, CTRL определяет общедоступный конструктор по умолчанию внутри.

Конструктор может иметь определенные параметры. Если этот класс используется в качестве базового класса для другого производного класса, конструктор базового класса должен вызываться в соответствии с определением параметра, например:

class Base{
  public Base(int value) { ... }
};
class Derived : Base
{
  public Derived() : Base(123) { ... }
};

Для следующих примеров рассмотрите возможность использования точки данных, как показано на скриншоте ниже.

ПРИМЕР

// definitions

class Valve

{

public Valve(string s)

{

// initialize all members

// the string elements will be used

// to call dpSet and dpGet in the open and close functions

   elementSet = s + «.Command.setPosition»;

   elementGet = s + «.State.position»;

   position = -1; // note: in this example -1 means undefined

}

public open() { dpSet(elementSet, 100); } // 100 means fully open

public close() { dpSet(elementSet, 0); } // 0 means fully closed

public int getPosition()

{

   dpGet(elementGet, position);

   return position;

}

// produce output for e.g. debugging

public print()

{

   DebugN(«DPE set», elementSet);

   DebugN(«DPE get», elementGet);

}

// members

protected string elementSet;

protected string elementGet;

protected int position;

};

// example calls

Valve v = Valve(«valve_001»);

v.print();

// Output:

// [«DPE set»][«valve_001.Command.setPosition»]

// [DPE get»][«valve_001.State.position»]

Derived class:

// definitions

class SlideValve : Valve

{

public SlideValve(string s) : Valve(s)

{

}

// adds a method to set any value between 0 and 100

public setPosition(int pos)

{

if (pos > 100 || pos < 0)

{

DebugN(«Illegal value», pos);

return;

}

dpSet(elementSet, pos);

}

}

// example calls

SlideValve sv=SlideValve(«valve_001»);

sv.print(); // produces same output as before

sv.setPosition(80); // OK

sv.setPosition(200); // Output: [«Illegal value»][200]

ПРИМЕЧАНИЕ

Вы также можете проверить команду.Элемент setPosition из ранее описанного примера в пункте.

Если вызов конструктора базового класса явно не задан, CTRL вызывает конструктор базового класса без каких-либо аргументов. Может быть вызван только прямой конструктор базового класса.

Конструктор производного класса автоматически вызовет все конструкторы базового класса, начиная с первого базового класса и заканчивая используемым производным классом.

Конструктор также может быть определен как закрытый, что означает, что никакой экземпляр этого класса не может быть создан, кроме как некоторой статической функцией класса. Если конструктор определен защищенным, класс может использоваться только как базовый класс для какого-либо другого производного класса, у которого есть открытый конструктор.

ПРИМЕЧАНИЕ

Инициализаторы переменных-членов запускаются сначала внутри конструктора класса.

Выражение конструктора класса

Конструктор класса можно использовать как выражение для создания временного экземпляра класса. Выражение конструктора можно использовать везде, где требуется экземпляр класса, например, в качестве аргумента вызова функции.

Пример:

foo(const MyClass &c) {...}
 
foo(MyClass("some value")); //creates a temporary instance of MyClass

деструктор

Деструктор должен иметь то же имя, что и класс, с символом ~ в качестве префикса.

Пример

class Valve
{
~Valve(){}
}

Деструктор всегда является закрытым. Перед именем деструктора не должно быть определено ключевое слово (например, private, public или static). Деструктор запускается при удалении экземпляра этого класса, например, если переменная класса выходит за пределы области видимости или удаляется последний указатель на экземпляр, если экземпляр был создан с помощью «new».

Деструктор производного класса запускается перед деструктором базового класса. Обратите внимание, что деструктор вызывается только в том случае, если конструктор также был выполнен. Например, это не тот случай, когда копируется объект.:

Base c;   //constructor will be executed
Base b=c; //no constructor for b will be executed since this is a copy; 
            only the destructor for c is executed, no destructor for b is executed

ПРИМЕЧАНИЕ

В случае аргументов shared_ptr для функций и т.д. запущенный поток CTRL по-прежнему будет иметь ссылку на экземпляр, пока поток не будет удален. Это также верно для временных объектов, например, в случае func (новый MyClass); деструктор будет запущен не сразу после завершения вызова функции, а когда весь поток CTRL будет остановлен.

Блокирующие функции, такие как dpGet() или delay(), не могут быть выполнены в деструкторе. Таким образом, это позволяет всем деструкторам во всех сценариях панели выполняться и завершаться при закрытии панели до открытия следующей панели. Код в деструкторе должен быть как можно короче для выполнения, поскольку при его выполнении блокируется весь менеджер.

Полиморфизм (виртуальные функции в C ++)

Все функции-члены всегда являются виртуальными, что означает, что интерпретатор вызывает функцию, которая была определена в классе, для которого был создан экземпляр.

ПРИМЕР

// definitions

class Valve

{

// constructor without any parameter

// ensures that member is initialized

// still we can derive from that class

public Valve() { position = 0; }

public open() { position = 100; }

public close() { position = 0; }

public int getPosition() { return position; }

protected int position;

};

class RotoValve : Valve

{

// ranges from -90 to 90

public open() { position = 90; }

public close() { position = -90; }

};

// now we define two functions which take a reference to an instance as parameter

// type must be the base class

openValve(Valve &valve)

{

   Valve.open();

}

closeValve(Valve &valve)

{

   Valve.close();

}

// example calls

Valve v;

RotoValve rv;

openValve(v);

openValve(rv);

DebugN(v.getPosition()); // Output: 100

DebugN(rv.getPosition()); // Output: 90

closeValve(v);

closeValve(rv);

DebugN(v.getPosition()); // Output: 0

DebugN(rv.getPosition()); // Output: -90

}

То есть: вы можете передать любой производный класс по ссылке базового класса, но выполняемая функция будет использоваться из переданного класса (если он существует). Вы также можете назначить производный класс экземпляру базового класса, но не наоборот.

ПРИМЕР

v = rv;  // works

rv = v;  // error

function_ptr

Новый тип данных: function_ptr (указатель на функцию) может содержать указатель на некоторую функцию CTRL script, например, на функцию-член класса. Функция-член должна быть объявлена статической.

ПРИМЕР

// definitions

class Valve

{

public static int getClassId() { return 255; }

};

// example calls

// note: no brackets after getClassId

function_ptr ptr_getId = Valve::getClassId;

DebugN(callFunction(ptr_getId)); // Output: 255

Нестатические функции невозможны, поскольку вызываемая функция не имеет объекта (экземпляра базового класса) и, следовательно, переменной «this».

Тип данных общего указателя

Тип данных shared_ptr, доступный в «КАСКАД Цифра», может использоваться как шаблон.

shared_ptr<int> object1;

Создает объект shared_ptr с именем object1, который указывает на новый объект именованного типа данных. Чтобы создать такой объект, используйте выражение «new».

shared_ptr<Base> object1 = new Base;

В данном случае <Base> — это новый класс. Тип данных, используемый для shared_ptr, может быть либо определяемым пользователем типом (class / struct / enum), либо также стандартным типом данных, таким как int, float и т.д.

shared_ptr<int> object1 = new int;

Вы можете назначить shared_ptr другому shared_ptr того же (или совместимого) типа.

shared_ptr<Base> object2 = object1;

Таким образом, два указателя указывают на один-единственный базовый экземпляр. Когда object1 выходит из области видимости (или ему присваивается nullptr), object2 по-прежнему сохраняет указатель на экземпляр, пока он также не выйдет из области видимости.

ОСТОРОЖНО

Использование указателя на указатель типа shared_ptr< shared_ptr<тип> > не допускается. Также невозможен параметр ссылки на shared_ptr, например: int foo(shared_ptr<int> &ref_to_ptr).

Вы также можете использовать тип данных «void», который создает указатель, который может указывать на любой другой тип данных.

shared_ptr<void> p = new string;

Также допускается понижающая передача shared_ptr:

Классы B наследуются от класса A

shared_ptr<A> pA = new B;
shared_ptr<B> pB;
 
pB = pA;

Новое выражение

Выражение «new» может использоваться как с аргументами, так и без них, например:

shared_ptr<int> pi = new int;                 // no argument
shared_ptr<int> pi = new int();               // no argument
shared_ptr<int> pi = new int(77);             // with initial value
shared_ptr<int> pi = new int(someFunction()); // with initial value
shared_ptr<Enum> pe = new Enum(Enum::One);    // with initial value
shared_ptr<Base> pb = new Base;               // constructor without argument
shared_ptr<Base> pb = new Base(1, 2, 3);      // constructor with arguments

ОСТОРОЖНО

Примечание: указатель на указатель типа shared_ptr< shared_ptr<тип> > не допускается.Также невозможен параметр ссылки на shared_ptr, например: int foo(shared_ptr<int> &ref_to_ptr).

ПРИМЕЧАНИЕ

Вы также можете имитировать эталонный регистр, но только в том случае, если объекты, которые вы хотите сократить, также являются экземплярами shared_ptr, созданными с помощью «new». например:

mapping m;
m[1] = new int;
shared_ptr<int> p = m[1];
p = 77;
 
DebugN(m[1]);  => 77

Автоматическое разыменование

Доступно автоматическое разыменование переменных shared_ptr. Автоматическое разыменование отличается от C++. Это работает аналогично anytype / mixed, поскольку внутренне они также указывают на экземпляр какого-либо другого типа данных, но при их использовании они автоматически получают реальный / конечный объект. например:

mapping m;
 
m["object"] = new Base;
 
m.object.x = 88;  // not as in C++:  m.object->x = 88;

Автоматическое удаление ссылки выполняется всегда, когда есть цель, на которую указывает указатель. например

shared_ptr<string> s1;               // nullptr
shared_ptr<string> s2 = new string;  // points to a string
s1 = s2;    

Новая функция equalPtr(shared_ptr, shared_ptr) позволяет сравнивать, указывают ли оба заданных указателя на один и тот же объект в памяти:

bool equalPtr(s1, s2)

Осторожно

Когда указатели указывают на разные объекты одного типа и им затем присваивается одинаковое значение, функция возвращает «false», поскольку это не один и тот же объект. например:

s1 = new string;
s2 = new string("Hello");
s1 = s2;  /* auto-dereferencing is done. 
            s1 and s2 point to different string objects 
            but both strings now contain the same value "Hello" */
 
equalPtr(s1, s2)  => false

При сравнении с константой nullptr автоматическое удаление ссылки не выполняется, а сравнивается указатель.

Вы можете, например, проверить, содержит ли указатель значение:

Shared_ptr<XY> spTest; 
 
if( spTest ) //compared with nullptr
{
  //...
}

Nullptr — освободить память

Чтобы освободить объект, на который указано, просто присвоите указателю значение nullptr. Когда последний указатель, указывающий на объект, выходит за пределы области видимости или ему присваивается значение nullptr, указанный объект удаляется.

shared_ptr<string> s = new string;   // memory allocated
s = "Hello";                         // memory written
s = nullptr;                         // memory freed

 

ПРИМЕЧАНИЕ

Прежде чем указателю можно будет назначить новый указатель, ему должно быть присвоено значение nullptr, в противном случае старый объект будет перезаписан. Переназначение также может быть выполнено с помощью функции assignPtr().

ОСТОРОЖНО

Обратите внимание, что некоторые ключевые слова CTRL используются внутри компании и, следовательно, их больше нельзя использовать для других целей, таких как имена переменных: shared_ptr, new, delete, nullptr

ОСТОРОЖНО

Если вы создаете класс, и объект этого класса содержит shared_ptr, и этот shared_ptr указывает на сам объект, эта ссылка никогда не выходит за пределы области видимости и не удаляется автоматически. Вы должны установить переменной-члену значение nullptr .Это также применимо, если два объекта указывают друг на друга, объекты не удаляются автоматически и оба никогда не выйдут за пределы области видимости. Установите для переменных значение nullptr.

Вектор

Вектор — это настраиваемый тип данных (аналогичный shared_ptr или переменным dyn_), который может содержать несколько элементов указанного типа.

Синтаксис:

vector<data_type>

Пример:

Вектор , содержащий список элементов int:

vector<int>

Типом также может быть другой вектор:

vector< vector<int> > 

ПРИМЕЧАНИЕ

Запись vector<вектор<int>> синтаксически некорректна, поскольку два «>» подряд без пробела друг между другом обрабатываются как оператор сдвига.

К элементам внутри вектора можно обращаться через их индекс. Оператор индекса [] вектора начинается с 0 (НУЛЯ). В векторах оператор индекса может использоваться только для элементов, которые уже есть в векторе. Невозможно присвоить значение индексу, которого еще нет. Это означает, что дополнительные элементы должны быть добавлены с помощью определенных функций (например: функции-члены append() или prepend()).

vector<int> v = makeVector(1, 2, 3, 4);
 
v.append(5);    // adds element as last element
v.prepend(0);   // adds element as first element

Векторы могут содержать элементы только одного определенного типа, что означает, что вектор<int> может содержать только значения int. Исключением являются vector<void>, vector<anytype> и vector<mixed> . Если вектор содержит класс или shared_ptr к классу, также возможно добавить производные классы (или shared_ptr к нему) к вектору.

ПРИМЕЧАНИЕ

Вектор нельзя преобразовать напрямую в строку. Используйте jsonEncode (vector), если вы хотите сериализовать вектор в строку.

Векторы и типы данных dyn

Функциональность вектора сравнима с различными типами данных dyn_. Пока типы данных одинаковы, вектор может быть назначен _dyn и наоборот. Это означает, что вектор также может быть инициализирован функциями makeDyn. например.:

vector<string> v = makeDynString("a","b","c");

В качестве особого случая также возможно присвоить любому вектору значение dyn_anytype или dyn_mixed .

Самая большая разница между векторами и переменными dyn_ — это индекс. Для векторов этот индекс начинается с 0, в то время как переменные dyn_ начинаются с 1. Это влияет, например, на итерацию с циклом for.:

vector<int> v = makeVector(1, 2, 3, 4);
for (int i = 0; i < v.count(); i++)
{
  DebugN("new value:", v[i]);
}
 
dyn_int dynInt = makeDynInt(1, 2, 3, 4);
for (int i = 1; i <= dynlen(dynInt); i++)
{
  DebugN("new value:", dynInt[i]);
}

вектор<пустота>

Тип данных void для вектора позволяет добавлять любой тип данных. Вектор<void> похож на dyn_anytype, но dyn_anytype — это dyn_, содержащий переменные anytype , которые сами указывают на любой другой тип. С точки зрения производительности и потребления памяти вектор<void> лучше, поскольку он напрямую содержит конечные указатели на любой заданный тип данных. Следующая функция позволяет создать вектор<void>:

vector<void> makeVector(...)

Вектор<void> может быть назначен любому другому вектору или dyn_ при условии, что типы данных всех элементов соответствуют необходимому типу данных. В противном случае генерируется исключение.

dyn_int di = makeVector(1,2,3);
 
vector< shared_ptr<MyObj> > myVec = makeVector(new MyObj(1), new MyObj(2), new MyObj(3));

Псевдоним типа

Чтобы упростить работу с вложенными типами данных, такими как vector< вектор< shared_ptr<SomeClass> > >, ключевое слово «using» можно использовать следующим образом:

using AliasName = existingType

Псевдоним типа также может использоваться для любого другого типа (например, int, vector, class и т.д.). Он определяется как объявление класса в скрипте вне какой-либо функции. Затем псевдоним можно использовать тем же способом, что и полное выражение, поскольку анализатор преобразует псевдоним в исходный тип. например:

using MyVec = vector < vector< shared_ptr<SomeClass> > >
using myInt = int;
using MyClass = SomeClass;
using vec_str = vector<string>;

Функции-члены стандартных типов данных

Следующие стандартные типы данных предоставляют функции-члены. Они могут вызываться с использованием точечной нотации, аналогично вызову функции класса. В случае функций, которые возвращают целое число без явного объяснения, возвращаемое значение можно игнорировать, поскольку все ошибки будут вызывать исключение. T в следующих списках отображает тип данных. T& показывает, что возвращаемое значение является ссылкой на значение внутри вектора и может быть присвоено ему напрямую. Например. vec.at (12) = 123;

ОСТОРОЖНО

Все функции-члены используют нулевой (0) индекс. Это также относится к dyn_types .

Все функции выдадут исключение, если будет передано неправильное количество аргументов.

отображение

Список всех отображающих функций-членов можно найти здесь.

строка

Список всех строковых функций-членов можно найти здесь.

Вектор и dyn_

Список всех функций-членов vector / dyn_* можно найти здесь.

перечисление

Все элементы enum имеют тип «int» и могут быть определены с инициализирующим значением или без него. Повторяющиеся имена недопустимы, допускаются повторяющиеся значения.

Синтаксис:

enum EngineState

{

  Startup,

  Off = 2,

  On = 4,

  Error

};

Инициализирующими значениями могут быть только отдельные постоянные значения или другие, уже определенные имена в перечислении. Как и в C, если элемент не имеет инициализирующего значения, он автоматически получает значение предыдущих элементов + 1, начиная с 0.

В приведенном выше примере результатом является следующий список элементов:

Запуск = 0, Выключено = 2, Включено = 4, Ошибка = 5

Чтобы использовать значение enum где-нибудь в коде, используйте синтаксис:

EnumName::Значение

Вы также можете объявлять переменные типа enum, но им может быть присвоено только значение, которое находится в списке элементов. Если присвоено другое значение, предыдущее значение сохраняется.

Начальным значением экземпляра enum является значение первого элемента.

ПРИМЕР

EngineState eState; // is initialized with 0

eState = 2; // OK, since EngineState::Off is 2

eState = EngineState::On; // OK, since On is in the list

eState = 64; // Not allowed, since 64 is not defined. Value remains 4

Вы не можете напрямую присвоить значение enum значению int. Следовательно, необходимо явное приведение.

ПРИМЕР

int x = EngineState::On; // Not allowed, an explicit cast is required

int x = (int)EngineState::On; // OK

Это также относится к прямому сравнению между значением enum и значением int.

ПРИМЕР

if ((int)_eCONNECION_STATE::STATE_Connected == 1)

ПРИМЕЧАНИЕ

Если целочисленное значение сравнивается со значением enum без явного приведения, в LogViewer можно найти следующее предупреждение:WCCOAui (1), ГГГГ.ММ.ДД hh:mm:ss.ms, IMPL, ПРЕДУПРЕЖДЕНИЕ, 50, вызывается ветвь по умолчанию, IntegerVar, operator=, не удается назначить переменную типа class_vart, код продолжит выполняться, и прерываний не произойдет.

Вызов функции экземпляра класса через объект внутри массива или отображение

Экземпляр класса «Person» внутри массива используется НАПРЯМУЮ (не копируется), и поэтому вызов функции напрямую использует этот экземпляр.

Вы не можете вызвать функцию другого объекта, возвращаемого функцией. Например.

 personMap["katja"].getFriend().sayHello();

В следующем примере показано, как вызвать функцию экземпляра класса напрямую через объект внутри массива или отображение:

class Person
{
  private string m_name;
  private string m_begr;
  private shared_ptr m_friend;
 
  public Person(string name, string begr)
  {
    m_name = name;
    m_begr = begr;
  }
 
  public void setFriend(shared_ptr friend)
  {
    m_friend = friend;
  }
 
  public shared_ptr getFriend()
  {
    return m_friend;
  }
 
  public void sayHello()
  {
    DebugTN(m_begr + ", My name is " + m_name);
  }
};
void main()
{
  shared_ptr katja = new Person("Katja", "Hei");
  shared_ptr josh = new Person("Josh", "Servus");
  shared_ptr john = new Person("John", "Hello");
 
  katja.setFriend(josh);
  josh.setFriend(john);
  john.setFriend(katja);
 
  dyn_anytype persons;
  dynAppend(persons, katja);
  dynAppend(persons, josh);
  dynAppend(persons, john);
 
  for(int i = 1; i <= dynlen(persons); i++)
  {
    persons[i].sayHello();
  }
 
  mapping personMap;
  personMap["katja"] = katja;
  personMap["josh"] = josh;
  personMap["john"] = john;
  personMap["katja"].sayHello();
  //This does not work:
  //personMap["katja"].getFriend().sayHello();
}

RTTI (информация о типе среды выполнения)

CTRL предоставляет следующие две функции для получения информации о типе среды выполнения:

GetType() и getTypeName()

Оба варианта также можно использовать с пользовательскими типами.

  • GetType(x) всегда возвращает CLASS_VAR для пользовательских типов данных (enum, struct и class)
  • getTypeName(x) возвращает либо <имя класса>, либо <имя перечисления>

ПРИМЕР

testenum myEnum;
 
DebugN(getType(myEnum));
 
DebugN(getTypeName(myEnum));
WCCOAui1:[5570560]     //Integer Value of CLASS_VAR
WCCOAui1:["testenum"]  

Пользовательские типы должны быть определены перед использованием, а затем доступны в области действия скрипта, который их определяет.

Определения новых типов в библиотеке делают тип доступным глобально, когда библиотека включена в #uses . В этом случае экземпляры типа должны иметь уникальное имя. Определение в любом другом скрипте, запущенном в CTRL-Manager, аналогичным образом позволяет использовать везде, где доступен файл. Если определение выполняется в ScopeLib, класс доступен только на панели, к которой он прикреплен, и имена могут дублироваться на разных панелях.

Мы рекомендуем хранить библиотеки, содержащие классы, в каталоге проекта в разделе scripts/libs/classes .

Объектно-ориентированный скриптинг (CTRL++)

Усовершенствование CTRL ++ обеспечивает объектную ориентацию для языка сценариев «КАСКАД Цифра».

  • Определяемые пользователем типы переменных struct и class
  • Модификаторы доступа
  • Наследование
  • Полиморфизм
  • Конструкторы
  • Деструкторы
  • Перечисление

Типы переменных, определяемые пользователем — struct & class

Типы переменных struct и class позволяют создавать пользовательские типы данных, которые могут содержать переменные-члены и функции-члены. Члены структуры или класса должны иметь уникальные имена внутри структуры или класса. Следовательно, вы не можете использовать одно и то же имя для функции-члена и переменной-члена.

Модификаторы доступа

Функции-члены и переменные-члены могут быть определены следующим образом

  • private: к закрытым членам может быть доступен только класс или структура, которые их определяют.
  • защищенные: доступ к защищенным элементам может осуществляться классом или структурой, которые их определяют, и всеми производными классами или структурами.
  • public: доступ к открытым элементам может осуществляться любым скриптом в пределах области, в которой определен экземпляр этого класса или структуры.

Разница между структурой и классом заключается в модификаторе доступа по умолчанию:

  • Члены структуры по умолчанию являются общедоступными.
  • Членам класса по умолчанию присвоено значение private.

Пример

// definitions
struct EngineHandle
{
  string dpName; // defaults to public
};

// example calls
  EngineHandle eHandle;
  eHandle.dpName = “engine_001"; // OK, sets dpName of eHandle to engine_001

// definitions
class Valve
{
  public open() { state = 2; }
  public close() { state = 1; }
  public int getState() { return state; }
  int state; // defaults to private
};

// example calls
  Valve v;
  int valveState;
  v.open(); // OK, sets state of v to 2
  valveState = v.state; // Error: accessing private member
  valveState = v.getState(); // OK, assigns 2 to valveState

Кроме того, также возможны вложенные типы данных (например, класс в классе, структура в классе и т.д.).

Пример

// definitions
class Engine
{
  public Valve v; // nested class: public member v is of type Valve
};

// example calls
  Engine eng;
  int valveState;
  eng.v.open(); // OK
  valveState = eng.v.getState(); // OK

Инициализация

Переменные-члены (статические и нестатические) могут быть инициализированы с помощью выражения. Если выражение является константой, анализатор напрямую вычислит значение и использует его как значение по умолчанию для элемента.

Инициализаторы, которые не являются постоянными, будут оцениваться во время создания объекта, что означает, что вы можете использовать вызов функции для инициализации элемента.

Рекомендуется выполнять это только в функции Object() { [машинный код]}:

Пример

// definitions
struct EngineHandle
{
  string dpName = “undefined";
};

Статические члены класса инициализируются запуском их выражения инициализатора в временной внутренней функции таким же образом, как и глобальные скрипты. Следовательно, также в принципе разрешен вызов других статических функций и т.д. Учтите следующее ограничение:

Статические классы-члены классов CTRL ++, определенных в библиотеках CTRL, глобальные переменные менеджера («global» в библиотеках) и переменные, которые копируются из библиотек в каждый скрипт (например, int x = init(); в библиотеке lib), не должны использовать функции ожидания в инициализаторах.

В качестве первого шага каждый scopeLib и скрипт запускают инициализаторы в следующем порядке:

  • статические члены класса (порядок следования в классе или между несколькими классами не определен)
  • глобальные экземпляры / переменные в порядке объявления

Это означает, что всякий раз, когда конструктор класса использует статическую переменную своего собственного или другого класса, гарантируется, что статическая переменная уже полностью инициализирована. Пока все инициализаторы не будут завершены, никакие другие потоки не будут выполняться в конкретном сценарии. Если это скрипт на панели и у него есть scopeLib, скрипт не будет выполнять ни один поток в нем, пока не будут завершены все инициализаторы scopeLib.

Кроме того, когда скрипт на панели пытается запустить общедоступную функцию в PanelRef или другой панели (см. Также invokeMethod()), вызываемая функция не запустится до тех пор, пока scopeLib, содержащий функцию, не завершит инициализаторы.

Наследование

Класс может быть производным от другого класса (только одиночное наследование). Производный класс наследует все члены базового класса.

Пример

// definitions
class Valve
{
  public open() { state = 2; }
  public close() { state = 1; }
  public int getState() { return state; }
  protected string baseType = “Valve"; // can be accessed from derived class
  int state = 1; // private
};

class SlideValve : Valve
{
  // access protected base member and own private member
  public string getType() { return derivedType + baseType; }
  string derivedType = “Slide";
};

// example calls
  SlideValve sv;
  int valveState;
  sv.open(); // OK, access inherited base method
  valveState = sv.getState(); // OK, access inherited base method
  DebugN(valveState); // Output: 2
  DebugN(sv.getType()); // Output: SlideValve

Примечание:

Производный класс всегда является производным от базового класса «public».

Примечание:

В случае, если все еще существует экземпляр класса, который сам по себе уже был удален, пользовательский интерфейс выдаст сбой сегментации. Чтобы избежать этой проблемы, классы должны быть определены только в файлах библиотеки.

Члены класса или структуры могут быть статическими, что означает: для всех экземпляров класса существует только один экземпляр этого члена. Можно обращаться к статическому общедоступному члену с помощью обозначения области видимости, например Name::member (см. Пример ниже).

Пример

// definitions
class Valve
{
  public static int classId = 255; // can be accessed through scope notation
};

class SlideValve : Valve
{
  // inherits static member
};

// example calls
  DebugN(Valve::classId); // Output: 255
  DebugN(SlideValve::classId); // Output: 255

Перегрузка

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

Пример

// definitions
class Valve
{
  public int getClassLevel() { return1; }
};

class SlideValve : Valve
{
  public int getClassLevel()
  {
    return Valve::getClassLevel() + 1;
   }
};

// example calls
  Valve v;
  SlideValve sv;
  DebugN(v.getClassLevel()); // Output: 1
  DebugN(sv.getClassLevel()); // Output: 2

Этот пример также показывает, что если имя уже существует в базовом классе, вы можете использовать его снова для переменной-члена производного класса. В этом случае реализация derived скроет реализацию базового класса. Для вызова переменной или функции базового класса необходимо использовать оператор scope .

Нестатические функции-члены могут вызываться с помощью ключевого слова «this». Эта специальная локальная константа указывает на объект, который выполняет функцию-член.

функция Object() { [машинный код] }

Каждый класс может иметь метод конструктора, у которого не объявлен возвращаемый тип и то же имя, что и у класса.

Внутри конструктора инициализируются члены класса (см. пример ниже).

Пример

// definitions
class Valve
{
  public Valve()
  {
    state = 1;
  }
  public int getState() { return state; }
  int state;
};

// example calls
  Valve v;
  DebugN(v.getState()); // Output: 1

Когда пользователь явно не определяет конструктор, CTRL определяет общедоступный конструктор по умолчанию внутри. конструктор.

Конструктор может иметь определенные параметры. Если этот класс используется в качестве базового класса для другого производного класса, конструктор базового класса должен вызываться в соответствии с определением параметра, например:


class Base{
  public Base(int value) { ... }
};

class Derived : Base
{
  public Derived() : Base(123) { ... }
};

Для следующих примеров рассмотрите возможность использования точки данных, как показано на скриншоте ниже.

Рисунокпример точки данных

Пример

// definitions
class Valve
{
  public Valve(string s)
  {
    // initialize all members
    // the string elements will be used
    // to call dpSet and dpGet in the open and close functions
    elementSet = s + ".Command.setPosition";
    elementGet = s + ".State.position";
    position = -1; // note: in this example -1 means undefined
  }
  public open() { dpSet(elementSet, 100); } // 100 means fully open
  public close() { dpSet(elementSet, 0); } // 0 means fully closed
  public int getPosition()
  {
    dpGet(elementGet, position);
    return position;
  }
  // produce output for e.g. debugging
  public print()
  {
    DebugN("DPE set", elementSet);
    DebugN("DPE get", elementGet);
   }
   // members
   protected string elementSet;
   protected string elementGet;
   protected int position;
};

// example calls
  Valve v = Valve("valve_001");
  v.print();
  // Output:
  // ["DPE set"]["valve_001.Command.setPosition"]
  // [DPE get"]["valve_001.State.position"]
  Derived class:
  // definitions
  class SlideValve : Valve
  {
    public SlideValve(string s) : Valve(s)
    {
    }
    // adds a method to set any value between 0 and 100
    public setPosition(int pos)
    {
      if (pos > 100 || pos < 0)
      {
        DebugN("Illegal value", pos);
        return;
       }
       dpSet(elementSet, pos);
     }
   }

// example calls
  SlideValve sv=SlideValve("valve_001");
  sv.print(); // produces same output as before
  sv.setPosition(80); // OK
  sv.setPosition(200); // Output: ["Illegal value"][200]

Вы также можете проверить Command.SetPosition элемент ранее описанного примера в пункте.

Если вызов конструктора базового класса явно не задан, CTRL вызывает конструктор базового класса без каких-либо аргументов. Может быть вызван только прямой конструктор базового класса.

Конструктор производного класса автоматически вызовет все конструкторы базового класса, начиная с первого базового класса и заканчивая используемым производным классом.

Конструктор также может быть определен как закрытый, что означает, что никакой экземпляр этого класса не может быть создан, кроме как некоторой статической функцией класса. Если конструктор определен защищенным, класс может использоваться только как базовый класс для какого-либо другого производного класса, у которого есть открытый конструктор.

Инициализаторы переменных-членов запускаются сначала внутри конструктора класса.

Выражение конструктора класса

Конструктор класса можно использовать как выражение для создания временного экземпляра класса. Выражение конструктора можно использовать везде, где требуется экземпляр класса, например, в качестве аргумента вызова функции.

Пример

foo(const MyClass &c) {...}

foo(MyClass("some value")); //creates a temporary instance of MyClass

деструктор

Деструктор должен иметь то же имя, что и класс, с символом ~ в качестве префикса.

Пример

class Valve
{
~Valve(){}
}

Деструктор всегда является закрытым. Перед деструктором не должно быть определено ключевое слово имя (например, private, public или static). Деструктор запускается, когда экземпляр этот класс удаляется, например, если переменная класса выходит за пределы области видимости или последний указатель на экземпляр удаляется, если экземпляр был создан с помощью «new».

Деструктор производного класса запускается перед деструктором базового класса. Обратите внимание, что деструктор вызывается только в том случае, если конструктор также был выполнен. Например, это не тот случай, если объект копируется:

Base c;   //constructor will be executed
Base b=c; //no constructor for b will be executed since this is a copy;
  only the destructor for c is executed, no destructor for b is executed

В случае использования аргументов shared_ptr для функций и т.д. запущенный поток CTRL по-прежнему будет иметь ссылку на экземпляр, пока поток не будет удален. Это также верно для временных объектов, например, в случае func(новый MyClass); деструктор не будет запущен сразу после завершения вызова функции, но когда весь поток CTRL будет остановлен.

Блокирующие функции, такие как dpGet() или delay(), не могут быть выполнены в деструкторе. Таким образом, это позволяет всем деструкторам во всех сценариях панели выполняться и завершаться при закрытии панели до открытия следующей панели. Код в деструкторе должен быть как можно короче для выполнения, поскольку при его выполнении блокируется весь менеджер.

Полиморфизм (виртуальные функции в C ++)

Все функции-члены всегда являются виртуальными, что означает, что интерпретатор вызывает функцию, которая была определена в классе, для которого был создан экземпляр.

Пример

// definitions
class Valve
{
  // constructor without any parameter
  // ensures that member is initialized
  // still we can derive from that class
  public Valve() { position = 0; }
  public open() { position = 100; }
  public close() { position = 0; }
  public int getPosition() { return position; }
  protected int position;
};

class RotoValve : Valve
{
  // ranges from -90 to 90
  public open() { position = 90; }
  public close() { position = -90; }
};

// now we define two functions which take a reference to an instance as parameter
// type must be the base class
openValve(Valve &valve)
{
  Valve.open();
}

closeValve(Valve &valve)
{
  Valve.close();
}

// example calls
  Valve v;
  RotoValve rv;
  openValve(v);
  openValve(rv);
  DebugN(v.getPosition()); // Output: 100
  DebugN(rv.getPosition()); // Output: 90
  closeValve(v);
  closeValve(rv);
  DebugN(v.getPosition()); // Output: 0
  DebugN(rv.getPosition()); // Output: -90
}

То есть: вы можете передать любой производного класса в ссылку на базовый класс, но выполняется функция будет использоваться от переданного класса (если он существует). Вы также можете назначить производный класс экземпляру базового класса, но не наоборот.

Пример

v = rv; // works
rv = v; // error

function_ptr

Новый тип данных: function_ptr (указатель на функцию) может содержать указатель на некоторую функцию CTRL script, например, на функцию-член класса. Функция-член должна быть объявлена статической.

Пример

// definitions
class Valve
{
  public static int getClassId() { return 255; }
};

// example calls
  // note: no brackets after getClassId
  function_ptr ptr_getId = Valve::getClassId;
  DebugN(callFunction(ptr_getId)); // Output: 255

Нестатические функции невозможны, поскольку вызываемая функция не имеет объекта (экземпляра базового класса) и, следовательно, переменной «this».

Вызов функции экземпляра класса через объект внутри массива или отображение

Экземпляр класса «Person» внутри массива используется НАПРЯМУЮ (не копируется), и поэтому вызов функции напрямую использует этот экземпляр.

Вы не можете вызвать функцию другого объекта, возвращаемого функцией. Например.

personMap["katja"].getFriend().sayHello();

В следующем примере показано, как вызвать функцию экземпляра класса напрямую через объект внутри массива или отображение:

class Person
{
  private string m_name;
  private string m_begr;
  private shared_ptr m_friend;

  public Person(string name, string begr)
  {
    m_name = name;
    m_begr = begr;
  }

  public void setFriend(shared_ptr friend)
  {
    m_friend = friend;
  }

  public shared_ptr getFriend()
  {
    return m_friend;
  }

  public void sayHello()
  {
    DebugTN(m_begr + ", My name is " + m_name);
  }
};

void main()
{
  shared_ptr katja = new Person("Katja", "Hei");
  shared_ptr josh = new Person("Josh", "Servus");
  shared_ptr john = new Person("John", "Hello");

  katja.setFriend(josh);
  josh.setFriend(john);
  john.setFriend(katja);

  dyn_anytype persons;
  dynAppend(persons, katja);
  dynAppend(persons, josh);
  dynAppend(persons, john);

  for(int i = 1; i <= dynlen(persons); i++)
  {
    persons[i].sayHello();
  }

  mapping personMap;
  personMap["katja"] = katja;
  personMap["josh"] = josh;
  personMap["john"] = john;
  personMap["katja"].sayHello();
  //This does not work:
  //personMap["katja"].getFriend().sayHello();
}

RTTI (информация о типе среды выполнения)

CTRL предоставляет следующие две функции для получения информации о типе среды выполнения:

GetType() и getTypeName()

Оба варианта также могут использоваться с пользовательскими типами.

  • GetType(x) всегда возвращает CLASS_VAR для пользовательских типов данных (enum, struct и class)
  • getTypeName(x) возвращает либо <имя класса>, либо <имя перечисления>

Пример

testenum myEnum;
DebugN(getType(myEnum));
DebugN(getTypeName(myEnum));
WCCOAui1: [5570560] //Integer Value of CLASS_VAR
WCCOAui1: ["testenum"]
 

Пользовательские типы должны быть определены перед использованием и затем доступны в области действия скрипта, который их определяет.

Определения новых типов в библиотеке делают тип доступным глобально, когда библиотека включена в #uses . В этом случае экземпляры данного типа должны иметь уникальное имя. Определение в любом другом скрипте, запущенном в CTRL-Manager, аналогичным образом позволяет использовать везде, где доступен файл. Если определение выполняется в ScopeLib, класс доступен только на панели, к которой он прикреплен, и имена могут дублироваться на разных панелях.

Мы рекомендуем хранить библиотеки, содержащие классы, в каталоге проекта в разделе scripts/libs/classes.

КПЭ использования кода

Покрытием кода языка программирования CONTROL можно:

  • определять то, какие части кода сценариев и библиотек были действительно выполнены (прогнаны)
  • выполнять подсчет частоты исполнения выражения
  • оценивать эффективность сценария

Производительность кода

Необходимо использовать флаг формирования отчета «CTRL_COVERAGE«. Результаты, обозначающие абсолютное число исполнений на функцию, сохраняются в XML файле. Выражение считается построчно. Интерпретатор языка программирования CONTROL не может различать несколько выражений в строке.
Файл отчета необходимо определять (например, -coveragereportfile coverage.xml -report CTRL_COVERAGE). Данное выражение возвращает статистические данные на момент запуска менеджера. Пользовательский интерфейс может только считывать полный, синтаксически правильный XML файл.

В случае необходимости перезаписи существующего файла отчета (*.xml), нужно использовать следующие символы-заполнители. Если имя файла отчета покрытия (-coveragereportfile) содержит эти символы-заполнители, они заменяются следующим образом:

$DATE$ ==> ггггммдд

$TIME$ ==> ЧЧММСС

$MAN$ ==> Имяменеджера

Производительность

Для оценки производительности необходимо использовать флаги формирования отчетов «-dbg ctrl_perf» или «-report ctrl_perf«, соответственно.

Информацию о производительности предоставляют следующие атрибуты:

  •  perfTime — время в интерпретаторе
  •  perfTimeDependent — период времени, включая вызванные в интерпретаторе функции
  •  perfBlockedTime — время ожидания (напр., для ответа «dpGet()»)
  •  perfBlockedTimeDependent — аналогично предыдущему, но с учетом вызванных функций
  •  perfCount —  количество вызовов во время оценки производительности

ПРИМЕЧАНИЕ

Информацию о периодах времени можно сбросить, используя флаг формирования отчетов «-report ctrl_perf,reset».

Результаты формируются только если «perfCount» или «perfTime» не равны 0. Таким образом, без оценки производительности общий отчет не изменяется. Но, кроме того, при выполнении оценки производительности, не каждый оператор имеет атрибут «time» в отчете покрытия.

Оцениваются только функции, написанные в CTRL.

Строки сценария, которые занимают более 50% времени одной функции, в сценарии выделяются темно-зеленым цветом — учитываются только те функции, которые состоят из более чем двух определений.

Чтение файла можно осуществлять редактором сценариев File (Файл) -> Load Code Coverage Report (Загрузка отчета покрытия кода). Выполненные части обозначены зеленым цветом, не выполненные части обозначены красным цветом. Если функция не была выполнена вообще, выделения цветом отсутствуют. Выделения цветом можно удалять с помощью меню View (Вид) -> Remove Coverage Markers (Удалить маркеры покрытия).

При помощи дополнительной опции командной строки «-dumpCoverageOnExit» в менеджере сценариев, отчет покрытия записывается при остановке менеджера, независимо от того, записывался ли отчет до этого. Для возврата данных покрытия панелей необходимо использовать флаг отчета «-report CTRL_COVERAGE». Возвращать данные покрытия для панелей при помощи опции командной строки «-dumpCoverageOnExit» не представляется возможным.

ВНИМАНИЕ!

Сценарии панели корректно сохраняются в XML файле. Тем не менее, редактор сценария не может осуществлять считывание данных этого отчета покрытия кода. Редактор сценариев способен считывать только отчеты покрытия кода библиотек и сценариев, но не панелей.

ПРОЦЕСС ОПРЕДЕЛЕНИЯ ПОКРЫТИЯ КОДА С ПОМОЩЬЮ МЕНЕДЖЕРА СЦЕНАРИЕВ

  1. Добавить на консоль новый менеджер сценариев проекта, который должен осуществить загрузку определенного сценария (опция командной строки: <наименование сценария>.ctl).
  2. Инфицировать запуск менеджера сценариев.
  3. Используя комбинацию клавиш Ctrl+D установить менеджеру сценариев t параметры отладки coveragereportfile coverage.xml -report CTRL_COVERAGE.
  4. Открыть сценарий в редакторе сценариев.
  5. Выполнить загрузку отчета покрытия кода (coverage.xml) при помощи пункта меню редактора сценариев File (Файл) -> Load Code Coverage Report (Загрузить отчет покрытия кода) . Этот отчет находится в каталоге журналов регистрации проекта.
  6. Сценарий автоматически будет отображен в редакторе сценария с тем или иным выделением цветом.
  7. В файле отчета (coverage.xml) можно получить информацию о частоте выполнения оператора.

Содержание файла отчета (* .xml) удаляется перед записью нового отчета покрытия кода. Если необходимо, чтобы это не происходило, используются символы-заполнители (см. описание символов-заполнителей в начале этого раздела).

ПРИМЕЧАНИЕ

Переменные, объявленные и инициализированные в той же строке (например, int i = 60), не будут помечены, даже если они были выполнены.

Многоязычные функции

Эти функции предназначены для управления языками, текстами и лексикой в случае многоязычных проектов.
errorText()
getActiveLang()
getCatStr()
getDictionary()
getGlobalLangId()
getLangIdx()
getLocale()
getMetaLang()
getNoOfLangs()
langEditor()
mergeDictionary()
readDictionary()
setLangString()
switchLang()
translate()
writeDictionary()