Обсуждение статьи "Использование утверждений (assertions) при разработке программ на MQL5" - страница 3

 
Пояснение. На момент написания этой статьи в MQL5 не было механизма, позволяющего программе выполнить аварийную остановку. В качестве альтернативы срабатывал runtime error, что гарантировало аварийное завершение программы.
Это неправда. Советника можно остановить с помощью ExpertRemove(), индикатор - с помощью ChartIndicatorDelete(), а для скрипта это тривиально.
 
Alain Verleyen:
Это неправда. Советник можно остановить с помощью ExpertRemove(), индикатор - с помощью ChartIndicatorDelete(), и это тривиально для скрипта.

Пожалуйста, покажите мне пример, как выйти с помощью ExpertRemove() в цикле.

Например, у нас есть такой код:

#property version   "1.00"
#property strict

int OnInit()
  {
   for(int i = 0; i < 100; i++)
   {
      if(i == 2)
      {
         ExpertRemove();
      }
      Print(i);
   }
      
//---
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {

      
  }

void OnTick()
  {
//---
   
  }

Нам нужно выйти, если i == 2, а все остальные шаги не должны выполняться. В журнале мы должны видеть только "0" и "1". Как это можно сделать с помощью данной функции?

Сейчас ExpertRemove() не останавливает советника в нужный момент, все шаги будут выполняться, а после этого советник будет остановлен. Но это неправильно для механизма Assertion, мы должны останавливать советника мгновенно. И да, мы не можем использовать только "break", потому что нам нужен универсальный макрос или функция для любой части любого советника.


Насчет индикаторов - пожалуйста, покажите мне универсальный механизм определения ShortName индикаторов. Потому что без этого механизма мы не сможем использовать эту функцию для Assertions. Да, мы можем определить ShortName в конкретном индикаторе (например, с помощью глобальной переменной, как это делают многие, хотя это и плохая практика), но у нас нет, например, универсальной функции "GetShortName()". Поэтому мы не можем сделать универсальный механизм (я имею в виду какой-то макрос или функцию для абсолютно любых индикаторов, где мы можем добавить всего одну строчку "assert(...)") с ChartIndicatorDelete().


И покажите мне, пожалуйста, "тривиальный" вариант для скриптов для любых частей кода. Это должна быть одна (!) функция или макрос для любой части Скрипта:

1) Для циклов
2) Для функций с любым возвращаемым типом
3) Для функций без возвращаемого типа (void).

Таким образом, мы должны просто добавить одну строчку "assert(...)" в любую часть Sript, вот так:

#property version   "1.00"
#property strict

void OnStart()
  {
   assert(...);

  }

double SomeDouble()
  {
   assert(...);
   return 0.0;
  }

color SomeColor()
  {
   assert(...);
   return clrNONE;
  }

string SomeString()
  {
   assert(...);
   return "";
  }

void SomeVoid()
  {
   assert(...);
   return;
  }

void SomeCicle()
  {
   while(!IsStopped())
     {
      assert(...);
     }
  }

P.S. извините за мой плохой английский.

 

Или другой пример советника с ExpertRemove(), а не с cicle :)

Например, мы должны останавливать приложение, если объем больше, чем нам нужно (это просто пример, в реальной ситуации мы должны работать с этой ситуацией иначе):

#property version   "1.00"
#property strict

int OnInit()
  {
   OpenTrade();
//---
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {

      
  }

void OnTick()
  {
//---
   
  }
bool OpenTrade()
{
   double volume = GetVolume();
   
   if(volume > 1)
   {
      Print("Volume > 1, stop EA!");
      ExpertRemove();
   }
   
   Print("Opening position ...");
   
   return true;
}

double GetVolume()
{
   return 999.0;
}

Мы должны мгновенно остановить советника в нужном месте кода (в этом смысл утверждений). Но ExpertRemove() в данном случае не корректный вариант, имеем:

2015.12.06 10:53:22.253 Test EURUSD,H1: Volume > 1, stop EA!<br/ translate="no">2015.12.06 10:53:22.253 Тест EURUSD,H1: Вызвана функция ExpertRemove
2015.12.06 10:53:22.253 Тест EURUSD,H1: Открытие позиции ...


Подскажите, пожалуйста, как использовать функцию ExpertRemove() (без return, break и т.д., она должна быть универсальной для любой части кода) для этого ceil.

 
Sergey Eremin:

Пожалуйста, покажите мне пример, как выйти с помощью ExpertRemove() в цикле.

Например, у нас есть такой код:

Нам нужно выйти, если i == 2, а все остальные шаги не должны выполняться. В журнале мы должны видеть только "0" и "1". Как это можно сделать с помощью данной функции?

Сейчас ExpertRemove() не останавливает советника в нужный момент, все шаги будут выполняться, а после этого советник будет остановлен. Но это неправильно для механизма Assertion, мы должны останавливать советника мгновенно. И да, мы не можем использовать только "break", потому что нам нужен универсальный макрос или функция для любой части любого советника.

Вам не нужно использовать ExpertRemove() в OnInit(), просто используйте return(INIT_FAILED);

int OnInit()
  {
//---
    ...

         if(somethign wrong)
           {
            //ExpertRemove(); 
            return(INIT_FAILED);    //--- Нет необходимости использовать ExpertRemove() в OnInit()
           }
    ...
  }

в других частях кода просто возвращайте :

            ExpertRemove();
            return;           //--- Просто вернитесь, чтобы завершить обработчик текущего события

or

            ExpertRemove();
            return(x);        //--- Просто вернитесь, чтобы завершить обработчик текущего события

Насчет индикаторов - пожалуйста, покажите мне универсальный механизм для определения ShortName индикаторов. Потому что без этого механизма мы не сможем использовать эту функцию для Assertions. Да, мы можем определить ShortName в нашем конкретном индикаторе (например, с помощью глобальной переменной, как это делают многие, хотя это плохая практика), но у нас нет, например, универсальной функции "GetShortName()". Поэтому мы не можем сделать универсальный механизм (я имею в виду какой-то макрос или функцию для абсолютно любых индикаторов, где мы можем добавить всего одну строчку "assert(...)") с ChartIndicatorDelete().

В чем проблема? Вы работаете над своим индикатором, это ваш код, поэтому вы знаете его короткое название.

Я написал, что вы не правы, говоря, что нет способа завершить программу немедленно. Вы сами должны найти решение для своего проекта Assertion.

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

И покажите мне, пожалуйста, "тривиальный" вариант для скриптов для любых частей кода. Это должна быть одна (!) функция или макрос для любой части скрипта:

1) Для циклов
2) Для функций с любым возвращаемым типом
3) Для функций без возвращаемого типа (void).

Таким образом, мы должны просто добавить одну строку "assert(...)" в любую часть Sript, вот так:

Аналогично для EA.

Файлы:
 
Alain Verleyen:

Я хотел сказать, что вы не правы, говоря, что нет способа немедленно завершить программу. Вы сами должны найти решение для своего проекта Assertion.

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

Хорошо, я понял вас. Спасибо, вы правы.

Но в своей статье я имею в виду решения для Assertions: универсальный механизм остановки MQL4/5 приложений в любом месте кода (включая OnInit и cicles). Просто добавьте одну строчку в любой части и готово. Как это работает в любом механизме Assertions во многих языках программирования ;)

Да, ваши варианты верны. Но не для моего видения Assertions, потому что это не универсальное решение для любой части любого кода.

Спасибо за ваш пример с EA.

 
Sergey Eremin:

Хорошо, я понял вас. Спасибо, вы правы.

Но в своей статье я имел в виду решения для Assertions: универсальный механизм остановки MQL4/5 приложений в любом месте кода (включая OnInit и циклы). Просто добавьте одну строчку в любой части и готово. Как это работает в любом механизме Assertions во многих языках программирования ;)

Да, ваши варианты верны. Но не для моего видения Assertions, потому что это не универсальное решение для любой части любого кода.

Спасибо за ваш пример с EA.

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

Проанализировав контекст вызова в вашем макросе (макросах), определив, является ли он советником или индикатором, и разобрав __FUNCSIG__.

Как сделать это универсальным механизмом - решать вам.

 
Alain Verleyen:

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

Анализируя контекст вызова в вашем макросе (макросах), определяя, является ли это советником или индикатором, и разбирая __FUNCSIG__.

Как сделать это универсальным механизмом - решать вам.

Да, сначала я думал о таких вещах, но в итоге сделал так, как мы видим в статье :)

Спасибо за комментарии!

 

Если кто-то собирается использовать этот код, имейте в виду такой момент: следующий скрипт

   if(true)
      assert(1==1, "")
   else
      Print("Never executed");

приводит к выводу сообщения "Never executed" из ветки else.

Чтобы можно было использовать assert корректно, нужно исправить его, например в таком виде:

#define assert(condition, message) \
       do if(!(condition)) \
        { \
         string fullMessage= \
                            #condition+", " \
                            +__FILE__+", " \
                            +__FUNCSIG__+", " \
                            +"line: "+(string)__LINE__ \
                            +(message=="" ? "" : ", "+message); \
         \
         Alert("Assertion failed! "+fullMessage); \
         double x[]; \
         ArrayResize(x, 0); \
         x[1] = 0.0; \
        } while(false)
#else
#define assert(condition, message) 
#endif

(в ветке #else также макрос скорректирован : возвращает пустую строку (а не ";").

В таком варианте после assert(..) нужно ставить ";"