Разработка/доработка стандартной библиотеки

 

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

Как затеявший тему начну. С истоков :) . CObject

Итак при рассмотрении CObject видим не описанный тут, на сайте

   //--- method of identifying the object
   virtual int       Type(void)                                    const { return(0);      }

 Очень нужная штука, скажу я вам, особенно, когда юзаешь свои унаследованные классы совместно со стандартными и хочется определить, что же у меня тут такое. Однако непонятки - и вопрос разработчикам - ну почему int??? Не кажется ли что string было бы ловчее??? Ну посудите сами - разные разработчики с гораздо большей вероятностью присвоят один и тот же int для объектов, нежели string, имя собственное???

 
YAndrey: Однако непонятки - и вопрос разработчикам - ну почему int??? Не кажется ли что string было бы ловчее??? Ну посудите сами - разные разработчики с гораздо большей вероятностью присвоят один и тот же int для объектов, нежели string, имя собственное???

Предположим? Начните с того каком месте у Вас возникают проблемы от того, что идентификатор типа объектов целочисленный?

Если не возникают, тогда какого ... бодягу разводить?

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

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

 
YAndrey:

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

Мне очень нравится идеология Стандартной Библиотеки, и очень жаль, что на МТ4 она еще сильно недоделана, а кроме того, существенно иначе работают некоторые классы таймсерий.

Как затеявший тему начну. С истоков :) . CObject

Итак при рассмотрении CObject видим не описанный тут, на сайте

 Очень нужная штука, скажу я вам, особенно, когда юзаешь свои унаследованные классы совместно со стандартными и хочется определить, что же у меня тут такое. Однако непонятки - и вопрос разработчикам - ну почему int??? Не кажется ли что string было бы ловчее??? Ну посудите сами - разные разработчики с гораздо большей вероятностью присвоят один и тот же int для объектов, нежели string, имя собственное???

int - на мой взгляд, гораздо лучше, чем string, поскольку позволяет использовать его в операторе switch(). Кроме того, int позволяет проводить хитрые преобразования, скажем, простым прибавлением константы и генерацией нового объекта полученного типа (хотя, последний пример, мне кажется, весьма надуманным, да и вобще опасным для использования).

string - хорош только для контроля объектов самим человеком во время отладки.

 
YAndrey:

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

Как затеявший тему начну. С истоков :) . CObject

Итак при рассмотрении CObject видим не описанный тут, на сайте

 Очень нужная штука, скажу я вам, особенно, когда юзаешь свои унаследованные классы совместно со стандартными и хочется определить, что же у меня тут такое. Однако непонятки - и вопрос разработчикам - ну почему int??? Не кажется ли что string было бы ловчее??? Ну посудите сами - разные разработчики с гораздо большей вероятностью присвоят один и тот же int для объектов, нежели string, имя собственное???

Топикстартер, сам не осознавая, затронул довольно насущную проблему динамической идентификации типов в MQL5. 

Сама стандартная библиотека - профессиональный и продуманный программный код высочайшего качества. Ее архитектор поистине профессионал программирования. Это чувствуется буквально в каждой строке кода. Я плотно работаю с стандартной библиотекой и не раз и не два погружался в ее логику. Любое желание что-то изменить в ней  должно подвергаться сильному сомнению. Однако действительно есть несколько "но".

Первое и основное "Но" это идентификация типа. Топикстарет правильно заметил что string в качестве основополагающего типа был бы более предпочтителен, но не правильно понял почему.

Дело в том, что во-времена написания CObject еще не было шаблонов и int как тип был наиболее правильным решением, потому что как минимум прозрачно позволял использовать строго типизированный enum в качестве идентификатора типа. Любой потомок CObject переопределявший Type() и возвращавший вместо int свой enum гарантировал свою уникальность. Поясню на примере:

#include <Object.mqh>
#include <Arrays\ArrayObj.mqh>

///
/// Каждый класс проекта переопределяет 
///
enum ENUM_TYPE_CLASS
{
   ///
   /// Идентификатор класса A
   ///
   CLASS_A,
   ///
   /// Идентификатор класса B
   ///
   CLASS_B
};

class BaseClass : public CObject
{
   public:
      ///
      /// Возвращает тип класса.
      ///
      virtual int Type()
      {
         return type;
      }
      ///
      /// Выводит тип класса в терминал.
      ///
      void PrintType()
      {
         printf(EnumToString(type));
      }
  protected:
      ///
      /// Гарантируем создание экземпляра с определенным типом.
      ///
      BaseClass(ENUM_TYPE_CLASS mType){ type = mType;}
      ///
      /// Содержит защищенный идентификатор класса.
      ///
      ENUM_TYPE_CLASS type;
};
///
/// Класс A.
///
class ClassA : public BaseClass
{
   public:
      ClassA() : BaseClass(CLASS_A){;}   
};
///
/// Класс B.
///
class ClassB : public BaseClass
{
   public:
      ClassB() : BaseClass(CLASS_B){;}   
};

void OnStart()
{
   //Создадим десять случайных классов и запишем их в массив:
   CArrayObj objects;
   int i = 0;
   do 
   {
      CObject* obj = NULL;
      switch(rand()%2)
      {
         case 0:
            obj = new ClassA();
            break;
         default:
            obj = new ClassB();
      }
      objects.Add(obj);
   }while(i++<9);
   i = 0;
   do
   {
      BaseClass* baseClass = objects.At(i);
      baseClass.PrintType();
   }while(i++<objects.Total()-1);
}

 Т.е. если все классы в проекте будут наследоваться от BaseClass c защищенным конструктором, то получить неправильный идентификатор не получиться. Однако по-прежнему есть необходимость в использовании дополнительной прослойки BaseClass. Потому что в противном случае приведение Type к ENUM_TYPE_CLASS не гарантируется. Использование же int как есть не представляется возможным, потому что этот тип не строго типизирован и может принимать потенциально любые значения.

В целом, если полагаться на дисциплину программиста такой способ вполне приемлем, однако с ведением шаблонов теоретически можно повысить типобезопасность объектов и для этого Type действительно надо перевести на string. Дело в том, что в шаблонах есть замечательный оператор typename и значит возможно динамическая идентификация по имени самого класса:

//+------------------------------------------------------------------+
//| Возвращает в строковом виде тип                                  |
//+------------------------------------------------------------------+
template<typename T>
string GetTypeName(const T &t)
  {
//--- вернем тип в виде строки
   return(typename(T));
//---
  }

С этой функцией не имеет значение принадлежит класс к CObject или нет. Эта функция вернет гарантированный и уникальный идентификатор класса пусть и в виде строки. Думаю по схожим причинам в .NET и в C# - вершине всех языков программирования, в основе рефлексии и динамической идентификации лежит именно string а не другой тип данных.

Однако  с typename связан неприятный момент. Функция возвращает не фактический тип класса, а тот тип, который был ей передан. Т.е. если создать производный класс и присвоить его объект указателю базового класса, то  typename вернет имя базового класса, в то время как на самом деле ей будет передан объект производного класса. Поэтому для того, что бы изменить идентификацию типов в лучшую сторону необходимо изменить не только CObject а еще typename, что бы он возвращал фактический тип объекта, не зависимо от текущего типа указателя на него. Во-вторых, должна быть конструкция позволяющая получать доступ к типу родительского класса лежащего о основе производного класса. Это поднимет безопасность и прозрачность динамической идентификации типов на порядок! Это позволит писать например такую штуку:

do
   {
      BaseClass* baseClass = objects.At(i);
      if(baseClass.TypeIs(CassA))
         printf("This class A");
      if(baseClass.TypeIs(CassB))
         printf("This class B");
      if(baseClass.TypeIs(BaseClass))
         printf("This Base Class");
   }while(i++<objects.Total()-1);
 

Омг...

Стандартная функция typeof порешает все проблемы. И это единственный нормальный способ.

 
М-да, забиди ногами, обозвали ламером.... Вот только мой опыт программирования уже приближается к 20 годам. Злые вы, уйду я от вас...
 
YAndrey:
М-да, забиди ногами, обозвали ламером.... Вот только мой опыт программирования уже приближается к 20 годам. Злые вы, уйду я от вас...
Ну я как раз за строку. В любом случае удобнее инта.
 
YAndrey:
М-да, забиди ногами, обозвали ламером.... Вот только мой опыт программирования уже приближается к 20 годам. Злые вы, уйду я от вас...

Что-то не похоже.

А как же отстаивание своего мнения ? Давай аргументы, почему string лучше ?

По мне - так enum типов объектов - это самое правильное решение, и лично я давно уже сделал CMyObject, в котором объявлено перечисление EMyObjectTypes, внутренняя переменная этого перечисления, которая присваивается в конструкторе,  и функция EMyObjectTypes GetType(); там, где невозожно использование механизма виртуальных функций - использую switch(), а со строкой пришлось бы использовать операторы сравнения строк, работающие значительно медленнее.

 
Laryx:

Что-то не похоже.

А как же отстаивание своего мнения ? Давай аргументы, почему string лучше ?

По мне - так enum типов объектов - это самое правильное решение, и лично я давно уже сделал CMyObject, в котором объявлено перечисление EMyObjectTypes, внутренняя переменная этого перечисления, которая присваивается в конструкторе,  и функция EMyObjectTypes GetType(); там, где невозожно использование механизма виртуальных функций - использую switch(), а со строкой пришлось бы использовать операторы сравнения строк, работающие значительно медленнее.

Непохоже потому что диалекты Си я очень поверхностно юзаю последние 15 лет. Сначала это была специализированная банковская система, потом 1С. Так, что, да, кое в чем я новичок. Но не в технологиях.

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

 Насчет int и строки. Имеется в виду, что разные разработчики могут писать свои классы, например, унаследованные от ExpertSignal, вы, подключая их к своему эксперту и добавляя их с массив фильтров, можете работать с ними по разному. int для этого тоже подходит, конечно, но, как я уже писал с очень большой вероятностью разные разработчики присвоят один и тот же код (например 666) - типа вот так, оригинально... Точно также я считаю некорректным использование целочисленного magic. Все хорошо, пока юзер не запустит два эксперта на одном символе с одинаковым маджиком...

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

Я вообще-то не с этой целью ветку начинал.

Стандартная библиотека хороша для изучения основ mql, работы с  таймсериями и специализированными функциями. Но ведь в ней и много недостатков, а? Так давайте сообща делать предложения о доработке, можно ведь ее (библиотеку) значительно расширить...

Насчет свитч и сравнения строк, которые медленнее... А это ничего, что в стандартной библиотеке метод Direction вызывается для каждого из фильтров по нескольку раз на каждом тике??? А ведь это самый тяжелый метод, где зачастую рассчитываются дивергенции осциляторов...

Ладно, пусть каждый свою нетленку изобретает...

 
YAndrey:
 

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

Предложи свой вариант.

И если будет string - чем оно лучше ? Опять же - добавляется новый тип, надо его назвать в конструкторе.

Я просто наследую от CMyObject'а далеко не все свои объекты. Исходя из принципов Стандартой Библиотеки CObject - это "объект множества", а не любой объект. В нем есть функции передвижения по списку, сравнения и зарузки-сохранения. Разумно использовать наследников от CObject'а именно тогда, когда требуется множество объектов.  

с очень большой вероятностью разные разработчики присвоят один и тот же код (например 666) - типа вот так, оригинально...

На мой взгляд, в одном проекте такое пересечение все же маловерятно. Но, тем не менее, опасность, конечно, есть. А со строкой - такой опасности меньше ?

Стандартная библиотека хороша для изучения основ mql, работы с  таймсериями и специализированными функциями. Но ведь в ней и много недостатков, а? Так давайте сообща делать предложения о доработке, можно ведь ее (библиотеку) значительно расширить...

Никаких проблем, только поддерживаю. Лично мне СБ в МТ5 очень даже нравится, и я надеюсь, что она будет по-максимуму перенесена на МТ4, торговыми классами и классами экспертов (чего сейчас пока нет). Кроме этого момента - я особо недостатков не вижу. Мне, скажем, крайне не нравится, что я не могу хранить указатель или ссылку на массив, но это, скорее претензия не к СБ, а к самому MQL

 
Текст ниже - редактор глючит....
#include <Object.mqh>
#include <Arrays\ArrayObj.mqh>


class BaseClass : public CObject
{
   public:
      ///
      /// Возвращает тип класса.
      ///
      virtual string Type1()
      {
         return type;
      }
      ///
      /// Выводит тип класса в терминал.
      ///
      void PrintType()
      {
         printf(type);
      }
  protected:
      ///
      /// Гарантируем создание экземпляра с определенным типом.
      ///
      template<typename T>
      BaseClass( T &mType){ type = typename(T);}
      ///
      /// Содержит защищенный идентификатор класса.
      ///
      string type;
      
      
};
///
/// Класс A.
///
class ClassA : public BaseClass
{
   public:
      ClassA() : BaseClass(this){;}   
            
};
///
/// Класс B.
///
class ClassB : public BaseClass
{
   public:
      ClassB() : BaseClass(this){;}   
};

void OnStart()
{
   //Создадим десять случайных классов и запишем их в массив:
   CArrayObj objects;
   int i = 0;
   do 
   {
      CObject* obj = NULL;
      switch(rand()%2)
      {
         case 0:
            obj = new ClassA();
            break;
         default:
            obj = new ClassB();
      }
      objects.Add(obj);
   }while(i++<9);
   i = 0;
   do
   {
      BaseClass* baseClass = objects.At(i);
      
      if(baseClass.Type1()=="ClassA")
         printf("This class A");
      if(baseClass.Type1()=="ClassB")
         printf("This class B");
      if(baseClass.Type1()=="BaseClass")
         printf("This Base Class");      
      
  
   }while(i++<objects.Total()-1);
}
C-4:

Топикстартер, сам не осознавая, затронул довольно насущную проблему динамической идентификации типов в MQL5. 

Сама стандартная библиотека - профессиональный и продуманный программный код высочайшего качества. Ее архитектор поистине профессионал программирования. Это чувствуется буквально в каждой строке кода. Я плотно работаю с стандартной библиотекой и не раз и не два погружался в ее логику. Любое желание что-то изменить в ней  должно подвергаться сильному сомнению. Однако действительно есть несколько "но".

Первое и основное "Но" это идентификация типа. Топикстарет правильно заметил что string в качестве основополагающего типа был бы более предпочтителен, но не правильно понял почему.

Дело в том, что во-времена написания CObject еще не было шаблонов и int как тип был наиболее правильным решением, потому что как минимум прозрачно позволял использовать строго типизированный enum в качестве идентификатора типа. Любой потомок CObject переопределявший Type() и возвращавший вместо int свой enum гарантировал свою уникальность. Поясню на примере:

 Т.е. если все классы в проекте будут наследоваться от BaseClass c защищенным конструктором, то получить неправильный идентификатор не получиться. Однако по-прежнему есть необходимость в использовании дополнительной прослойки BaseClass. Потому что в противном случае приведение Type к ENUM_TYPE_CLASS не гарантируется. Использование же int как есть не представляется возможным, потому что этот тип не строго типизирован и может принимать потенциально любые значения.

В целом, если полагаться на дисциплину программиста такой способ вполне приемлем, однако с ведением шаблонов теоретически можно повысить типобезопасность объектов и для этого Type действительно надо перевести на string. Дело в том, что в шаблонах есть замечательный оператор typename и значит возможно динамическая идентификация по имени самого класса:

С этой функцией не имеет значение принадлежит класс к CObject или нет. Эта функция вернет гарантированный и уникальный идентификатор класса пусть и в виде строки. Думаю по схожим причинам в .NET и в C# - вершине всех языков программирования, в основе рефлексии и динамической идентификации лежит именно string а не другой тип данных.

Однако  с typename связан неприятный момент. Функция возвращает не фактический тип класса, а тот тип, который был ей передан. Т.е. если создать производный класс и присвоить его объект указателю базового класса, то  typename вернет имя базового класса, в то время как на самом деле ей будет передан объект производного класса. Поэтому для того, что бы изменить идентификацию типов в лучшую сторону необходимо изменить не только CObject а еще typename, что бы он возвращал фактический тип объекта, не зависимо от текущего типа указателя на него. Во-вторых, должна быть конструкция позволяющая получать доступ к типу родительского класса лежащего о основе производного класса. Это поднимет безопасность и прозрачность динамической идентификации типов на порядок! Это позволит писать например такую штуку:

Спасибо, насчет "сам не сознавая", позабавило :)

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

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

 Только, походу, я неверно интерпретировал сущность метода Type, он, как оказалось, несколько для иного, действительно int и enum тут как раз.

Чтож, предлагаю ввести метод TypeObject, начиная с CExpertBase я думаю.

Причина обращения: