По поводу надежности ПО. Exception handling = must have - страница 3

 
Renat :

 

ps: не забывайте, что мы профессиональные программисты и у многих из нас по 15-20 лет опыта именно в программировании.

Сам по себе срок не означает уровня. Не зря тут люди удивляются.

C++ подобный язык без exception handling - ещё те грабельки. 

 
simpleton :

Код возврата?

Зачем тогда было создавать урезанный С++ ?

 

Вполне хватило бы доработки до C.

Добавили бы структур, которых так не хватало, условное выражение и нормальный return, и - всё.

 

Условное выражение добавили, return сделали нормальный, но вместо добавления структур зачем-то решили доусложнять до C++.

 

 


Интересно как вы разрулите векторный вызов ( new CObject[100] )?

Советую Вам использовать отдельную функцию инициализации, вместо сложного конструктора.

 
simpleton :

Сам по себе срок не означает уровня. Не зря тут люди удивляются.

C++ подобный язык без exception handling - ещё те грабельки. 

Вот-вот. Такое впечатление, что все указанные годы варились в каком-то собственном соку, и понятия не имеют, что делается в мире. А там в принципе уже десятки других программистских фирм (наверно они полные кретины?) наваяли свои собственные программы-платформы со встроенными языками, и везде, где язык поднимался до уровня ООП, поддерживались эксепшены. Ладно меня можно не слушать, ну так хоть изучите то, что по вашей теме уже делалось и какие решения принимались, проверенные временем. Не надо считать себя умнее других. Если просто нет возможности сделать (ресурсов не хватает или еще чего), так и напишите, а не сочиняйте вздорные отписки. Не надо считать всех программистов ламерами, и Эйнштейн может ошибиться, и ваша задача, как поставщиков платформы, сделать в ней контроль ошибок на уровне. Пенять на программиста, дескать он нифига не проверяет - последнее дело, особенно учитывая, что Вы сами не желаете централизованно сделать то же самое.


P.S.Можете не отвечать. Я эту ветку больше не читаю. Надоело доказывать, что 2+2=4. ;-)
 
mql5 :


Интересно как вы разрулите векторный вызов ( new CObject[100] )?


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

 

#include <iostream>

using namespace std;

class CObject
{
private:
  static unsigned int x;

public:
  enum { v_size = 3 };

  CObject()
  { cout << "CObject(), this = " << reinterpret_cast<void *>(this) << ": ";
    if(!x--) { cout << "NOT constructed, throwing..." << endl; throw 1; }
    else cout << "constructed successfully" << endl; }

  ~CObject()
  { cout << "~CObject(), this = " << reinterpret_cast<void *>(this) << endl; }

  void *operator new[](size_t size)
  { void *p = ::operator new[](size); cout << "void * = " << p;
    cout << " CObject::operator new[](size_t size = " << size << ")" << endl;
    return p;}

  void operator delete[](void *p)
  { cout << "void CObject::operator delete[](void *p = " << p << ")" << endl;
    ::operator delete[](p); }

  void work(void)
  { cout << "I am working, this = " << reinterpret_cast<void *>(this) << endl; }

  static void test(unsigned int x)
  { cout << "Entering test" << endl; CObject::x = x;
    try { cout << "Creating..." << endl; CObject *p = new CObject[v_size];
          cout << "Created successfully, working..." << endl;
          for(unsigned u = 0; u < v_size; u++) p[u].work();
          cout << "Working is finished, deleting..." << endl;
          delete[] p; cout << "Deleted successfully" << endl; }
    catch(int i) { cout << "Exception caught: int i = " << i << endl; }
    catch(...) { cout << "Some exception caught" << endl; }
    cout << "Exiting test" << endl; }
};

unsigned int CObject::x;

int main(void)
{
  cout << "test(" << CObject::v_size << "): started" << endl;
  CObject::test(CObject::v_size);
  cout << "test(" << CObject::v_size << "): finished" << endl << endl;

  cout << "test(" << CObject::v_size - 1 << "): started" << endl;
  CObject::test(CObject::v_size - 1);
  cout << "test(" << CObject::v_size - 1 << "): finished" << endl;

  return 0;
}

 

Статический метод test() принимает количество объектов, которое будет создано прежде, чем конструктор выбросит исключение.

Программа выдаёт следующий отчёт о деталях своей работы:

 

test(3): started
Entering test
Creating...
void * = 0x106c010 CObject::operator new[](size_t size = 11)
CObject(), this = 0x106c018: constructed successfully
CObject(), this = 0x106c019: constructed successfully
CObject(), this = 0x106c01a: constructed successfully
Created successfully, working...
I am working, this = 0x106c018
I am working, this = 0x106c019
I am working, this = 0x106c01a
Working is finished, deleting...
~CObject(), this = 0x106c01a
~CObject(), this = 0x106c019
~CObject(), this = 0x106c018
void CObject::operator delete[](void *p = 0x106c010)
Deleted successfully
Exiting test
test(3): finished

test(2): started
Entering test
Creating...
void * = 0x106c010 CObject::operator new[](size_t size = 11)
CObject(), this = 0x106c018: constructed successfully
CObject(), this = 0x106c019: constructed successfully
CObject(), this = 0x106c01a: NOT constructed, throwing...
~CObject(), this = 0x106c019
~CObject(), this = 0x106c018
void CObject::operator delete[](void *p = 0x106c010)
Exception caught: int i = 1
Exiting test
test(2): finished

 

Легко видеть, что в первом ("нормальном") случае все объекты вектора успешно создаются, каждый элемент отрабатывает (метод work(), строка "I am working"), потом все объекты штатно уничтожаются. По распечатке значения this можно видеть, какой объект - какой, что в каком порядке происходит.

 

Во втором случае, при исполнении в функции test() кода "CObject *p = new CObject[v_size];" во время создания третьего элемента вектора из конструктора выбрасывается исключение.

Обращаю внимание, что тот код в функции test(), который первым получит после этого события управление, находится в блоке "catch(int i)".

Это значит, что работа со всем вектором объектов отменится, даже если хотя бы один из объектов не сконструируется доконца.

Вместо этого управление получит код в блоке "catch(int i)", ответственный за действия в случае неудачи. Обращаю внимание, что этот код не выполняется в случае штатной работы, как это видно по отчёту программы в случае вызова test(3) из main().

 

Вот тут люди жаждут позиции позакрывать в случае нештатной ситуации - пожалуйста.

 

Что же происходит между выбросом исключения и конструктора третьего объекта и получением управления блоком "catch(int i)"?

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

Все НЕдоконца сконструированные объекты, то есть, третий объект, не уничтожаются. Они не созданы, потому что их конструктор, будучи вызван, не вернул управление. И это неуничтожение НЕдоконца сконструированных объектов тоже гарантируется соответствующим кодом, вставляемым компилятором C++.

Далее, был вызван "CObject::operator delete[]()" - причём, именно парный соответствующему "CObject::operator new[]()", - и тоже только благодаря тому, что компилятор C++ вставил соответствующий код. Только после этого управление передаётся в блок "catch(int i)".

 

Итак, в случае штатной ситуации всё работает в соответствии с кодом, находящимся в блоке "try", блоки "catch()" не исполняются.

 

В случае же нештатной ситуации в работу вступают автоматизмы C++,  гарантирующие (при их правильном применении) неисполнение кода, который иначе работал бы с (полу)несозданными объектами и приводил к так называемым "крешам". Эти автоматизмы гарантируют откат в смысле удаления полностью сконструированных объектов, выходящих из области видимости, освобождение выделенной памяти и передачу управления в правильное место обработки возникшей нештатной ситуации.


Вам всё ещё интересно?

Надеюсь, пример объясняет, почему нет проблемы и в случае вектора объектов.

Кстати, можно вектор объектов заменить на "невектор", то есть, на один объект - поведение кода (естественно, без пробежки по вектору в цикле for, а с вызовом методов одного объекта и с соответствующей заменой new и delete на невекторные версии) в смысле реакции на исключения останется тем же самым.

 

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

 

Что теперь делать?

Вводить обработку исключений (возможно, в будущем) или выбросить конструирование?

Оставить, всё как есть?

 

Кстати, язык C++ - сложен и зачастую неподъёмен даже для профессиональных программистов. Это значит, что грамотно пользоваться они им не умеют.

Вот C - другое дело. Этот - многим по силам. Чтобы грамотно пользоваться, я имею ввиду. Хватило бы его вполне в терминале.

Документация по MQL5: Основы языка / Объектно-ориентированное программирование / Статические члены класса
Документация по MQL5: Основы языка / Объектно-ориентированное программирование / Статические члены класса
  • www.mql5.com
Основы языка / Объектно-ориентированное программирование / Статические члены класса - Документация по MQL5
 
mql5 :


Интересно как вы разрулите векторный вызов ( new CObject[100] )?

Советую Вам использовать отдельную функцию инициализации, вместо сложного конструктора.



А теперь рассмотрим то же самое, но с позиций MQL5.

 

Отдельную функцию инициализации использовать можно, но вызывать-то её придётся руками!

А вызов конструктора компилятор (особенно, отлаженный компилятор) уж точно не забудет вставить. Стоит только объявить перенную-объект - и конструктор обязательно будет вызван. Ну не забудет компилятор этого сделать!

А деструктор? Руками - запросто забыть можно. Опыт это показывает. А компилятор MQL5 уж точно не забудет заботливо вставить соответствующий код при завершении времени жизни переменной-объекта.

 

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

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

 

А если у меня объект (A) состоит из многих других объектов (B, C, D), то я должен из его функции инициализации (A::init()) не забыть вызвать функции инициализации (B::init(), C::init(), D::init()) каждого из объектов, из которых он состоит?

А если я добавлю ещё один объект (E) в A, то как мне не забыть модифицировать A::init(), вызвав оттуда E::init()? 

 

Добавим сюда наследование - инициализацию подобъектов тоже руками выполнять?

И код отката в случае невозможности дальнейшей инициализации объекта - тоже самому писать? 

А когда тогда собственно программированием поставленной задачи заниматься? 


Зачем в MQL5 существует автоматизм конструкторов/деструкторов, если пользоваться по-человечески этим автоматизмом нельзя?

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

 

Хороши грабельки - выбросить обработку исключений...

Документация по MQL5: Основы языка / Переменные / Область видимости и время жизни переменных
Документация по MQL5: Основы языка / Переменные / Область видимости и время жизни переменных
  • www.mql5.com
Основы языка / Переменные / Область видимости и время жизни переменных - Документация по MQL5
 

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

Поэтому всерьез сравнивать их с С и тем более с С++ это бред. Но почему-то про это постоянно забывают.

simpleton :

Отдельную функцию инициализации использовать можно, но вызывать-то её придётся руками!

И что? Религия не позволяет? Неужели ни разу не видели в C++ коде метод SetHandler(...)? Тем более защиту от дурака прикрутить -- 2 пальца об асвальт.

Да, это проблема, но она вполне решаема. Имхо, отсутствие виртуального и множественного наследования -- гораздо бОльшая проблема.

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

Что мешает пользоваться именно таким набором и забить на классы?

Хороши грабельки - выбросить обработку исключений...

Необходимость в исключениях в подконтрольном коде вообще под вопросом.

Может, шаблоны еще вспомнить?


Дальше, пример. Притянутый за уши по самое никуда. ТАК исключения использовать НЕЛЬЗЯ.

Кстати, язык C++ - сложен и зачастую неподъёмен даже для профессиональных программистов. Это значит, что грамотно пользоваться они им не умеют.

Судя по всему, Вы именно из этой категории.

 
TheXpert :

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

Поэтому всерьез сравнивать их с С и тем более с С++ это бред. Но почему-то про это постоянно забывают.



Точно... Хороший язык. О таких вещях раньше и мечтать было нельзя.

А теперь и наследование, виртуальные функции.

Разработчики могли вообще сделать расширенный MQL4.

Мне очень понравился MQL5. (спасибо). Очень многое упростилось.

За отладчик отдельное спасибо.

А без исключений можно обойтись.


Человек по натуре такое существо. Будем сражаться, добиваться,

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

а в catch напишем Print("Error "...) и скажем: "Вот, с исключениями другое дело". :)

Документация по MQL5: Основы языка / Объектно-ориентированное программирование / Виртуальные функции
Документация по MQL5: Основы языка / Объектно-ориентированное программирование / Виртуальные функции
  • www.mql5.com
Основы языка / Объектно-ориентированное программирование / Виртуальные функции - Документация по MQL5
 

Снова возвращась к "проблеме" деления на 0.

(т.н.  zero divide )

 

Неужели это принципильное условие???

Вот сегодня, из-за частого появления случаев в параметрах 0 пришлось лопатить

и уж простите тулить лажу... иного слова не знаю... если на 0.00000001 можно, а на 0 нельзя.

Ну и пусть себе вместо истиного расчёта всё тот-же 0, чем галиматью в виде например бешеных процентов...

 

double beginmont=НаНачалоМесяца();
if(beginmont==0.0) {beginmont=0.00000001;}
double percprofmont=(AccountEquity()/beginmont)*100-100;

 

 

ИЕ. 

 
TheXpert :

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

Поэтому всерьез сравнивать их с С и тем более с С++ это бред. Но почему-то про это постоянно забывают.

И что? Религия не позволяет? Неужели ни разу не видели в C++ коде метод SetHandler(...)? Тем более защиту от дурака прикрутить -- 2 пальца об асвальт.

Да, это проблема, но она вполне решаема. Имхо, отсутствие виртуального и множественного наследования -- гораздо бОльшая проблема.

Что мешает пользоваться именно таким набором и забить на классы?

Необходимость в исключениях в подконтрольном коде вообще под вопросом.

Может, шаблоны еще вспомнить?


Дальше, пример. Притянутый за уши по самое никуда. ТАК исключения использовать НЕЛЬЗЯ.

Судя по всему, Вы именно из этой категории.



В чём неполноценность программ MQL5? Нельзя объектник получить и слинковать с ним свою программу или прилинковать статически к программе MQL5 объектник, полученный трансляцией с C++? Нет ключевого слова asm?

Вот тогда именно в этом смысле и следует понимать бредовость сравнения MQL5 и C++. Но я же не эти особенности сравниваю.

 

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


Дело не в религии. Я для кого объяснял, что происходит дублирование механизма инициализации/деинициализации, причём именно ручное дублирование?

Для кого объяснял, что это является дополнительным источником ошибок и нивелирует преимущество, предоставляемое автоматизмом конструкторов/деструкторов?

Для кого объяснял, что в случае сложных объектов это ручное дублирование тоже становится сложным, и допустить ошибку в нём становится намного проще?

И для кого объяснял, что отлаженный компилятор будет безошибочно строить логику инициализации/деинициализации, какими бы сложными не были объекты, и можно будет сосредоточиться на собственно программировании задачи и не отвлекаться на не связанные с ней вещи, что положительно повлияет на качество этого программирования и на количество допущенных при этом ошибок (их будет меньше)?

Апелляции к тому, что так пишут, - странны, по крайней мере. Мало ли чего делают кое-как?


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

 

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

Это бы поднять, - то, что есть. Какие шаблоны, когда то, что есть, уже на костылях ещё в стадии дизайна?

 

Необходимость в исключениях ещё больше вырастет, когда введут (уже обещали) перегрузку операторов. По той же самой причине вырастет необходимость.

 

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

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

Вот, к примеру, первый же минимальный тест, который пришёл мне  в голову, выявил bug MQL5 в реализации инициализации/деинициализации сложных объектов. То есть, и там ещё всё находится в стадии альфа. И разработчики вынуждены будут прилагать усилия для поддержки этой части, чтобы сначала вывести язык в стадию бета, а потом, хочется надеяться, и в стадию релиза. Кстати, успеют ли до чемпионата 2010 года?

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


Пример - не притянутый за уши, пример демонстрирует, причём, очень наглядно, как работают механизмы инициализации/деинициализации, в том числе, в случае исключения для случая посложнее одиночного объекта.

Почему так исключения использовать нельзя? Стандарт C++ запрещает?

Где аргументы-то? Хотя бы для того, чтобы понять мысль, что имелось ввиду под ТАК и почему НЕЛЬЗЯ?

 

Заметили, что я аргументирую свои высказывания, причём стараюсь это сделать так, чтобы любой мог проверить эти аргументы?

 

У вас какие-то проблемы с логикой.

Человек, который в состоянии продемонстрировать работу механизмов C++ и объяснить их смысл и значение за счёт того, что хорошо понимает и разбирается в языке, именно поэтому и не умеет им грамотно пользоваться?

 

Логика в стиле - "Всё учёные - неучи, потому что они - учёные"?

Ну, и насколько тогда близко к истине ваше утверждение, основанное на подобной логике?

Automated Trading Championship 2010
  • championship.mql5.com
Automated Trading Championship 2010
 
kombat :

Снова возвращась к "проблеме" деления на 0.

(т.н.  zero divide )

 

Неужели это принципильное условие???

Вот сегодня, из-за частого появления случаев в параметрах 0 пришлось лопатить

и уж простите тулить лажу... иного слова не знаю... если на 0.00000001 можно, а на 0 нельзя.

Ну и пусть себе вместо истиного расчёта всё тот-же 0, чем галиматью в виде например бешеных процентов...

 

 

 

ИЕ. 

 

 

 

 


В парадигме "если возникла проблема, то её следует 'замять', даже за счёт достоверности вычислений" данная проблема решается примерно так:

Пользоваться значком "/" для операции деления запрещается, вместо этого используется следующая функция:

 

double div(double a, double b, double e = 1e-8)
{
  return a / (MathAbs(b) < e ? e : b); /* Как я рад введению условных выражений и нормальному return'у, без обязательных скобок, - теперь можно писать красивый и компактный код!!! */
}

Значение по умолчанию для третьего параметра выбрать таким, чтобы в большинстве случаев устраивало, и можно было вызывать функцию div(), передавая ей лишь два параметра:


double beginmont=НаНачалоМесяца();
double percprofmont=div(AccountEquity(),beginmont)*100-100;

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

 

Если же нужно возвращать 0 в случае 'нехорошего' делителя, то можно использовать, например, такую функцию:

Документация по MQL5: Основы языка / Функции / Вызов функции
Документация по MQL5: Основы языка / Функции / Вызов функции
  • www.mql5.com
Основы языка / Функции / Вызов функции - Документация по MQL5
Причина обращения: