Библиотека Generic классов - ошибки, описание, вопросы, особенности использования и предложения - страница 29

 
Alexey Volchanskiy:

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

ссылку в студию

Alexey Navoykov:

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

могут быть проблемы с литералами

 
TheXpert:

ссылку в студию

могут быть проблемы с литералами

Лень снова искать ту статью, но я немного натянул. Кольцевой одно или двух-связанный лист строится на базе linked list, но в последнем элементе хранится ссылка на самый первый.

 
TheXpert:

могут быть проблемы с литералами

Да не, у меня класс "значений" наследуется от базового класса "ссылок", чисто как обёртка. Поэтому просто выбираем требуемую версию класса - и вперёд
 
Alexey Volchanskiy:

Кольцевой одно или двух-связанный лист строится на базе linked list, но в последнем элементе хранится ссылка на самый первый.

Так вот в том и дело, что строится "на базе", а не взамен.  Никто не мешал разработчикам и тут сделать это как дополнительный контейнер. А не подменять общепринятые понятия
 
Alexey Navoykov:

Скорее всего, тот кто портировал эти классы, решил просто облегчить себе жизнь, упростив код.  Вместо двух указателей m_first и m_last сделал один указатель m_head…

Посмотрел исходники dotnet, и понял причину.  Сам класс LinkedList портирован верно, там действительно только один указатель head, являющийся  одновременно и началом, и концом. Т.е. внутренняя реализация действительно является зацикленной.  А вот внешнее поведение - нет.  У MQ ошибка в классе CLinkedNode (методы Next и Previous).  Вот как выглядит их оригинальная реализация:

        public LinkedListNode<T>? Next
        {
            get { return next == null || next == list!.head ? null : next; }
        }

        public LinkedListNode<T>? Previous
        {
            get { return prev == null || this == list!.head ? null : prev; }
        }

А вот как портировано в MQL:

   CLinkedListNode<T>* Next(void)                      { return(m_next); }
   CLinkedListNode<T>* Previous(void)                  { return(m_prev); }

Возможно по невнимательности перепутали.

Но вообще я также немного удивлён насчёт реализации в dotnet.  Почему было не сделать два указателя в самом списке (first и last) вместо одного head. Это позволило бы избежать лишних проверок в вышеперечисленных методах узла списка, т.е. они бы выглядели в точности как сейчас у MQ, но при этом всё бы работало правильно.  Это конечно чуть замедлит процесс вставки/удаления узлов, но зато значительно ускорит итерацию по ним, что гораздо приоритетней.  Next и Previous вызываются несомненно чаще, чем добавляются узлы.   Я именно так себе и сделал,  думал что и у мелкомягких будет аналогично.  Ну да ладно )

Кстати, у MQ ещё один косяк в реализации CLinkedListNode и CRedBlackTreeNode.  Их поля m_next, m_prev и др. (кроме m_value)  предназначены для изменения исключительно самим классом списка.  Никто другой не должен менять их.  Поэтому в dotnet они являются internal полями. Пользователю доступны лишь get-методы.  А MQ зачем-то сделали публичные методы для изменения этих полей, причём ладно бы хоть назвали бы их как-нибудь специфично... но нет же, обычные названия:

void              Next(CLinkedListNode<T>*value)     { m_next=value; }
 

Всем привет!

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

 

Вот ещё пару дровишек в топку кривой реализации библиотеки.  Класс CKeyValuePair у них зачем-то наследует интерфейс IComparable (а соответственно и IEqualityComparable),  требуя в свою очередь чтобы и пользовательский тип Key тоже поддерживал эти интерфейсы.  Хотя в этом нет никакой надобности, если мы задаём собственный Comparer.  В оригинале естественно у KeyValuePair нет никаких интерфейсов.

Продолжив копаться в этом, обнаруживашь ещё более странные вещи:

template<typename TKey,typename TVal>
CHashMap::CHashMap(IEqualityComparer<TKey>*comparer): m_count(0),
                                                      m_free_list(0),
                                                      m_free_count(0),
                                                      m_capacity(0)
  {
//--- check equality comaprer
   if(CheckPointer(comparer)==POINTER_INVALID)
     {
      //--- use default equality comaprer    
      m_comparer=new CDefaultEqualityComparer<TKey>();
      m_delete_comparer=true;
     }
   else
     {
      //--- use specified equality comaprer
      m_comparer=comparer;
      m_delete_comparer=false;
     }
  }

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

А должен конструктор выглядеть вот так:

template<typename TKey,typename TVal>
CHashMap::CHashMap(IEqualityComparer<TKey>*comparer): m_count(0),
                                                      m_free_list(0),
                                                      m_free_count(0),
                                                      m_capacity(0),
                                                      m_comparer(comparer),
                                                      m_delete_comparer(false)
{ }

И никаких POINTER_INVALID . А для дефолтного компарера есть другая сигнатура конструктора.

 
Есть ли пример, как правильно применять шаблон CQueue<T> с произвольным классом в качестве параметра T ?
 
Уже было в параллельной ветке. Проситесь на бан. 
 

Подскажите, почему не компилируется код?

#include <Generic\HashMap.mqh>
void OnStart()
  {
   CHashMap<ENUM_CHART_PROPERTY_INTEGER,int> mapI;    // эта срока компилируется без ошибок
   CHashMap<ENUM_CHART_PROPERTY_DOUBLE,double> mapD;  // здесь ошибки компиляции: 'NULL' - cannot convert enum  HashMap.mqh     21      39. 'NULL' - cannot convert enum        HashMap.mqh     462     30
   CHashMap<ENUM_CHART_PROPERTY_STRING,string> mapS;  // здесь ошибки компиляции: 'NULL' - cannot convert enum  HashMap.mqh     21      39. 'NULL' - cannot convert enum        HashMap.mqh     462     30

  }

Проблема в системных перечислениях: ENUM_CHART_PROPERTY_DOUBLE, ENUM_CHART_PROPERTY_STRING что-то с ними не так. Если в качестве типа ключа использовать свой enum, то компиляция тоже  проходит.

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