Discussion of article "MQL5 Cookbook: Implementing an Associative Array or a Dictionary for Quick Data Access" - page 3

 
Forex gold technical analysis exchanges, group number is 375124107, into the group verification 77, thank you for cooperation!
 

I decided to use this class in my EA as a container of strategies of the form (StrategyGUID, Strategy), where StrategyGUID is an identifier of ulong type; Strategy is a strategy created on the basis of a class with inheritance from CObject.

Accordingly, I declare the necessary variables, including CDictionary StrategyBook - the book of active strategies; I create the necessary strategies Strategy01, Strategy02, which in the OnInit() function of the EA initiate various parameters, including assigning them unique GUIDs. At the very end of OnInit() I decide to save the strategies in StrategyBook by calling the code

StrategyBook.AddObject(Strategy01.strGUID,Strategy01);

StrategyBook.AddObject(Strategy02.strGUID,Strategy02); 

And then I get the error "StrategyXX - parameter conversion not allowed" on both lines.

Based on my experience with C#, everything is done correctly. But in MQL5 I get this error.

What can it be related to?

 
Сергей Лебедев:

I decided to use this class in my EA as a container of strategies of the form (StrategyGUID, Strategy), where StrategyGUID is an identifier of ulong type; Strategy is a strategy created on the basis of a class with inheritance from CObject.

Accordingly, I declare the necessary variables, including CDictionary StrategyBook - the book of active strategies; I create the necessary strategies Strategy01, Strategy02, which in the OnInit() function of the EA initiate various parameters, including assigning them unique GUIDs. At the very end of OnInit() I decide to save the strategies in StrategyBook by calling the code

StrategyBook.AddObject(Strategy01.strGUID,Strategy01);

StrategyBook.AddObject(Strategy02.strGUID,Strategy02); 

And then I get the error "StrategyXX - parameter conversion not allowed" on both lines.

Based on my experience with C#, everything is done correctly. But in MQL5 I get this error.

What can it be related to?

There can be several reasons. Most likely, either type incompatibility or incorrect work with pointers. Also, we cannot exclude the peculiarities of working with template methods (they also have their own nuances).

In general, it is better to use a ready-made trading engine, you will save a lot of time and nerves: https://www.mql5.com/en/articles/2166.

Универсальный торговый эксперт: Торговые режимы стратегий (Часть 1)
Универсальный торговый эксперт: Торговые режимы стратегий (Часть 1)
  • 2016.01.07
  • Vasiliy Sokolov
  • www.mql5.com
Каждый экспертописатель, независимо от уровня своей подготовки, ежедневно сталкивается с одними и теми же торговыми задачами и алгоритмическими проблемами, которые так или иначе приходится решать для организации надежного торгового процесса. Данная статья описывает возможности торгового движка CStrategy, способного взять на себя решение этих задач и предоставить пользователю удобные механизмы для описания своей торговой идеи.
 

Yeah, that's what I eventually decided to do.

 

Thanks for all the effort, but I cannot seem to get any of the code examples working.

It seems Dictionary dict; should be CDictionary dict;

How about a simple working example? 

[Deleted]  

Regarding new MT4 (Build 1080, 12 May 2017) these errors occur while compiling and prevents execution:

'm_array' - structures containing objects are not allowed Dictionary.mqh 303 25

cannot cast 'DoubleValue' to 'ULongValue' Dictionary.mqh 252 14

 

Hello,

As previously said by another person, there is no compilable example.

Only a file with comments in Russian.

 
Hi Vasily,

thank you for your helpful article. When downloading and then compiling your dictionary.mqh with MQL5 build 2063, I receive the following errors:

'GetKey' - funcion already defined and has different type
'Compress' - function already defined and has different type

Why is it like this?

Best regards
Gerik
 

I'm gratefully using Vasiliy's library successfully. It's a real help!

I remember I had some compiler errors too and could fix them. However, I can't really say which they were and what I did.

And bc I'm extremely annoyed by the formatting of MQL5 code & I always reformat everything to a modern C++ format (proper indenting, avoiding unnecessary brackets, no comment clutter, and so on), it's impossible for me to say what was the real change in code. All I remember is, that the changes were small.

In case you want to use the proper formatted library, here it is:

(Note, that I am using tabs, so the formatting looks broken here, but in the MetaEditor it isn't!)

//+------------------------------------------------------------------+
//|                                                                                              CDictionary.mqh |
//|                                                                     Copyright 2015, Vasiliy Sokolov. |
//|                                                                                      http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link          "http://www.mql5.com"

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

#define FOREACH_DICT(dict) for (CObject* node = (dict).GetFirstNode(); node != NULL; node = (dict).GetNextNode())

class KeyValuePair : public CObject
{
        private:
                string                          m_string_key;
                double                          m_double_key;
                ulong                           m_ulong_key;
                ulong                           m_hash;
                bool                            m_free_mode;
        public:
                CObject*                        object;
                KeyValuePair*           next_kvp;
                KeyValuePair*           prev_kvp;
                template <typename T>
                void                            KeyValuePair(T key, ulong hash, CObject *obj);
                                                        ~KeyValuePair();
                template <typename T>
                bool                            EqualKey(T key);
                template <typename T>
                void                            GetKey(T& gkey);
                ulong                           GetHash()                                       { return m_hash; }
                void                            FreeMode(bool free_mode)        { m_free_mode = free_mode; }
                bool                            FreeMode(void)                          { return m_free_mode; }
};

template <typename T>
void KeyValuePair::KeyValuePair(T key, ulong hash, CObject *obj)
{
        m_hash = hash;
        string name = typename(key);
        if (name == "string")
                m_string_key = (string)key;
        else if
                (name == "double" || name == "float")
        
        m_double_key = (double)key;
        else
                m_ulong_key=(ulong)key;
        
        object = obj;
        m_free_mode = true;
}

template <typename T>
void KeyValuePair::GetKey(T& gkey)
{
        string name = typename(gkey);
        if (name  ==  "string")
                gkey = (T)m_string_key;
        else if (name == "double" || name == "float")
                gkey = (T)m_double_key;
        else
                gkey = (T)m_ulong_key;
}

KeyValuePair::~KeyValuePair()
{
        if (m_free_mode)
                delete object;
}

template <typename T>
bool KeyValuePair::EqualKey(T key)
{
        string name=typename(key);
        if (name == "string")
                return m_string_key  ==  (string)key;
        if (name == "double" || name == "float")
                return m_double_key  ==  (double)key;
        else
                return m_ulong_key  ==  (ulong)key;
}

class CDictionary : public CObject
{
        private:
                int                                     m_array_size;
                int                                     m_total;
                bool                            m_free_mode;
                bool                            m_auto_free;
                int                                     m_index;
                ulong                           m_hash;
                CList*                          m_array[];
                union casting_struct
                {
                        double d_value;
                        ulong  l_value;
                } casting;
                
                KeyValuePair*           m_first_kvp;
                KeyValuePair*           m_current_kvp;
                KeyValuePair*           m_last_kvp;
        
                ulong                           Adler32(string line);
                int                                     GetIndexByHash(ulong hash);
                template <typename T>
                ulong                           GetHashByKey(T key);
                void                            Resize();
                int                                     FindNextSimpleNumber(int number);
                int                                     FindNextLevel();
                void                            Init(int capacity);
        
        public:
                                                        CDictionary();
                                                        CDictionary(int capacity);
                                                        ~CDictionary();
                void                            Compress(void);

                int Total(void)         { return m_total; }

                template <typename T>
                CObject*                        GetObjectByKey(T key);
                template <typename T>
                bool                            AddObject(T key, CObject *value);
                template <typename T>
                bool                            DeleteObjectByKey(T key);
                template <typename T>
                bool                            ContainsKey(T key);
                template <typename T>
                void                            GetCurrentKey(T &key);
                bool                            DeleteCurrentNode(void);
                bool                            FreeMode(void)                                          { return(m_free_mode); }
                void                            FreeMode(bool free_mode);
                void                            AutoFreeMemory(bool autoFree)           { m_auto_free = autoFree; }
                void                            Clear();
        
                CObject*                        GetNextNode(void);
                CObject*                        GetPrevNode(void);
                CObject*                        GetCurrentNode(void);
                CObject*                        GetFirstNode(void);
                CObject*                        GetLastNode(void);
};

CDictionary::CDictionary()
{
        Init(3);
        m_free_mode = true;
        m_auto_free = true;
}

CDictionary::CDictionary(int capacity)
{
        if (capacity < 3)
                Init(3);
        else
                Init(capacity);

        m_free_mode = true;
        m_auto_free = true;
}

CDictionary::~CDictionary()
{
        Clear();
}

void CDictionary::FreeMode(bool free_mode)
{
        if (free_mode == m_free_mode)
                return;

        m_free_mode = free_mode;

        for (int i=0; i < ArraySize(m_array); i++)
        {
                CList *list = m_array[i];
                if (CheckPointer(list) == POINTER_INVALID)
                        continue;

                for (KeyValuePair* kvp = list.GetFirstNode(); kvp != NULL; kvp = list.GetNextNode())
                        kvp.FreeMode(m_free_mode);
        }
}

void CDictionary::Init(int capacity)
{
        m_array_size = ArrayResize(m_array,capacity);
        m_index = 0;
        m_hash  = 0;
        m_total = 0;
}

int CDictionary::FindNextLevel()
{
        double value=4;
        for (int i=2; i <= 31; i++)
        {
                value = MathPow(2.0,(double)i);
                if (value > m_total)
                        return (int)value;
        }
        
        return (int)value;
}

ulong CDictionary::Adler32(string line)
  {
        ulong s1 = 1;
        ulong s2 = 0;
        uint buflength = StringLen(line);
        uchar char_array[];
        ArrayResize(char_array, buflength, 0);
        StringToCharArray(line, char_array, 0, -1, CP_ACP);
        for (uint n=0; n < buflength; n++)
        {
                s1 = (s1 + char_array[n]) % 65521;
                s2 = (s2 + s1) % 65521;
        }
        return ((s2  <<  16) + s1);
}

template <typename T>
ulong CDictionary::GetHashByKey(T key)
{
        ulong ukey = 0;
        string name = typename(key);
        if (name == "string")
                return Adler32((string)key);
        if (name == "double" || name == "float")
        {
                casting.d_value = (double)key;
                ukey = casting.l_value;
        }
        else
                ukey = (ulong)key;
        return ukey;
}

template <typename T>
void CDictionary::GetCurrentKey(T &key)
{
        m_current_kvp.GetKey(key);
}

int CDictionary::GetIndexByHash(ulong key)
{
        return (int)(key % m_array_size);
}

void CDictionary::Clear(void)
{
        int size = ArraySize(m_array);
        for (int i=0; i < size; i++)
        {
                if (CheckPointer(m_array[i]) != POINTER_INVALID)
                {
                        m_array[i].FreeMode(true);
                        delete m_array[i];
                }
        }
        ArrayFree(m_array);
        if (m_auto_free)
                Init(3);
        else
                Init(size);
                
        m_first_kvp = m_last_kvp = m_current_kvp = NULL;
  }

void CDictionary::Resize(void)
  {
        int level = FindNextLevel();
        int n = level;
        CList* temp_array[];
        ArrayCopy(temp_array, m_array);
        ArrayFree(m_array);
        m_array_size = ArrayResize(m_array,n);
        int total = ArraySize(temp_array);
        KeyValuePair* kv = NULL;
        
        for (int i=0; i < total; i++)
        {
                if (temp_array[i] == NULL)
                        continue;

                CList* list = temp_array[i];
                int count = list.Total();
                list.FreeMode(false);
                kv = list.GetFirstNode();

                while (kv != NULL)
                {
                        int index = GetIndexByHash(kv.GetHash());
                        if (CheckPointer(m_array[index]) == POINTER_INVALID)
                        {
                                m_array[index] = new CList();
                                m_array[index].FreeMode(true);  // Элементы KeyValuePair удаляются всегда
                        }
                        list.DetachCurrent();
                        m_array[index].Add(kv);
                        kv = list.GetCurrentNode();
                }
                delete list;
        }
        int size = ArraySize(temp_array);
        ArrayFree(temp_array);
}

void CDictionary::Compress(void)
{
        if (!m_auto_free)
                return;
        
        double koeff = m_array_size / (double)(m_total+1);
        if (koeff < 2.0 || m_total <= 4)
                return;

        Resize();
}

template <typename T>
CObject* CDictionary::GetObjectByKey(T key)
{
        if (!ContainsKey(key))
                return NULL;
        CObject *obj = m_current_kvp.object;
        return obj;
}

template <typename T>
bool CDictionary::ContainsKey(T key)
{
        m_hash = GetHashByKey(key);
        m_index = GetIndexByHash(m_hash);
        
        if (CheckPointer(m_array[m_index]) == POINTER_INVALID)
                return false;
        
        CList* list = m_array[m_index];
        
        KeyValuePair* current_kvp = list.GetCurrentNode();
        
        if (current_kvp  ==  NULL)
                return false;
        
        if (current_kvp.EqualKey(key))
        {
                m_current_kvp = current_kvp;
                return true;
        }
        
        current_kvp = list.GetFirstNode();
        while (true)
        {
                if (current_kvp.EqualKey(key))
                {
                        m_current_kvp = current_kvp;
                        return true;
                }
                current_kvp = list.GetNextNode();
                if (current_kvp == NULL)
                        return false;
        }
        return false;
  }

template <typename T>
bool CDictionary::AddObject(T key, CObject *value)
{
        if (ContainsKey(key))
                return false;
        
        if (m_total == m_array_size)
        {
                Resize();
                ContainsKey(key);
        }
        
        if (CheckPointer(m_array[m_index]) == POINTER_INVALID)
        {
                m_array[m_index] = new CList();
                m_array[m_index].FreeMode(true);
        }
        
        KeyValuePair *kv = new KeyValuePair(key,m_hash,value);
        kv.FreeMode(m_free_mode);
        
        if (m_array[m_index].Add(kv) != -1)
                m_total++;
        
        if (CheckPointer(m_current_kvp) == POINTER_INVALID)
        {
                m_first_kvp = kv;
                m_current_kvp = kv;
                m_last_kvp = kv;
        }
        else
        {
                while (m_current_kvp.next_kvp != NULL)
                        m_current_kvp = m_current_kvp.next_kvp;
                        
                m_current_kvp.next_kvp = kv;
                kv.prev_kvp = m_current_kvp;
                m_current_kvp = kv;
                m_last_kvp = kv;
        }
        return true;
}


CObject* CDictionary::GetCurrentNode(void)
{
        if (m_current_kvp == NULL)
                return NULL;

        return m_current_kvp.object;
}

CObject* CDictionary:: GetPrevNode(void)
{
        if (m_current_kvp == NULL)
                return NULL;

        if (m_current_kvp.prev_kvp == NULL)
                return NULL;

        KeyValuePair *kvp = m_current_kvp.prev_kvp;
        m_current_kvp = kvp;

        return kvp.object;
}

CObject* CDictionary::GetNextNode(void)
{
        if (m_current_kvp == NULL)
                return NULL;
        
        if (m_current_kvp.next_kvp == NULL)
                return NULL;
        
        m_current_kvp = m_current_kvp.next_kvp;

        return m_current_kvp.object;
}

CObject* CDictionary::GetFirstNode(void)
{
        if (m_first_kvp == NULL)
                return NULL;

        m_current_kvp = m_first_kvp;
        return m_first_kvp.object;
}

CObject *CDictionary::GetLastNode(void)
{
        if (m_last_kvp == NULL)
                return NULL;

        m_current_kvp = m_last_kvp;
        return m_last_kvp.object;
}

bool CDictionary::DeleteCurrentNode(void)
{
        if (m_current_kvp == NULL)
                return false;

        KeyValuePair* p_kvp = m_current_kvp.prev_kvp;
        KeyValuePair* n_kvp = m_current_kvp.next_kvp;

        if (CheckPointer(p_kvp) != POINTER_INVALID)
                p_kvp.next_kvp = n_kvp;

        if (CheckPointer(n_kvp) != POINTER_INVALID)
                n_kvp.prev_kvp = p_kvp;

        m_array[m_index].FreeMode(m_free_mode);
        bool res = m_array[m_index].DeleteCurrent();

        if (res)
        {
                m_total--;
                Compress();
        }
        return res;
}

template <typename T>
bool CDictionary::DeleteObjectByKey(T key)
{
        if (!ContainsKey(key))
                return false;
        return DeleteCurrentNode();
}

 

I think I found a bug when deleting an element and trying to get to the last element:

class CWord : CObject
{
        string m_Word;

        public:
                CWord(string word) : m_Word(word) {}
                ~CWord() {}
                
                string GetWord() { return m_Word; }
};
CDictionary* testDict = new CDictionary(10);

testDict.AddObject(0, (CObject*)new CWord("A"));
testDict.AddObject(100, (CObject*)new CWord("A"));
testDict.AddObject(200, (CObject*)new CWord("B"));
testDict.AddObject(300, (CObject*)new CWord("C"));

CWord* lastWord = (CWord*)testDict.GetLastNode();
Print(lastWord.GetWord());

CWord* firstWord = (CWord*)testDict.GetFirstNode();
Print(firstWord.GetWord());

testDict.DeleteCurrentNode();

lastWord = testDict.GetLastNode(); // will result in invalid pointer in CDictionary::GetLastNode(void) -> return m_last_kvp.object; -> m_last_kvp is invalid pointer!
Print(lastWord.GetWord());

Error in the CDictionary.mqh will be:

invalid pointer access in 'Dictionary.mqh' (463,9)

Can anyone confirm this? Any ideas how to fix?