Discussion de l'article "Le MQL5 Cookbook : Implémentation d'un tableau associatif ou d'un dictionnaire pour un accès rapide aux données" - page 3

 
Forex gold technical analysis exchanges, le numéro de groupe est 375124107, dans le groupe de vérification 77, merci pour votre coopération !
 

J'ai décidé d'utiliser cette classe dans mon EA en tant que conteneur de stratégies de la forme (StrategyGUID, Strategy), où StrategyGUID est un identifiant de type ulong ; Strategy est une stratégie créée sur la base d'une classe avec héritage de CObject.

En conséquence, je déclare les variables nécessaires, y compris le CDictionary StrategyBook - le livre des stratégies actives ; je crée les stratégies nécessaires Strategy01, Strategy02, qui dans la fonction OnInit() de l' EA initient divers paramètres, y compris en leur assignant des GUID uniques. A la fin de la fonction OnInit(), je décide de sauvegarder les stratégies dans le livre des stratégies en appelant le code suivant

StrategyBook.AddObject(Strategy01.strGUID,Strategy01);

StrategyBook.AddObject(Strategy02.strGUID,Strategy02); 

Et j'obtiens alors l'erreur "StrategyXX - parameter conversion not allowed" sur les deux lignes.

D'après mon expérience avec C#, tout est fait correctement. Mais en MQL5, j'obtiens cette erreur.

À quoi cela peut-il être lié ?

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

J'ai décidé d'utiliser cette classe dans mon EA en tant que conteneur de stratégies de la forme (StrategyGUID, Strategy), où StrategyGUID est un identifiant de type ulong ; Strategy est une stratégie créée sur la base d'une classe avec héritage de CObject.

En conséquence, je déclare les variables nécessaires, y compris le CDictionary StrategyBook - le livre des stratégies actives ; je crée les stratégies nécessaires Strategy01, Strategy02, qui dans la fonction OnInit() de l' EA initient divers paramètres, y compris en leur assignant des GUID uniques. A la fin de la fonction OnInit(), je décide de sauvegarder les stratégies dans le livre des stratégies en appelant le code suivant

StrategyBook.AddObject(Strategy01.strGUID,Strategy01);

StrategyBook.AddObject(Strategy02.strGUID,Strategy02); 

Et j'obtiens alors l'erreur "StrategyXX - parameter conversion not allowed" sur les deux lignes.

D'après mon expérience avec C#, tout est fait correctement. Mais en MQL5, j'obtiens cette erreur.

À quoi cela peut-il être lié ?

Il peut y avoir plusieurs raisons. Le plus probable est une incompatibilité de type ou un travail incorrect avec les pointeurs. Nous ne pouvons pas non plus exclure les particularités du travail avec les méthodes template (elles ont aussi leurs propres nuances).

En général, il est préférable d'utiliser un moteur de négociation prêt à l'emploi, ce qui vous permettra d'économiser beaucoup de temps et de nerfs : https://www.mql5.com/fr/articles/2166.

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

Oui, c'est ce que j'ai finalement décidé de faire.

 
MetaQuotes Software Corp.:

Nouvel article MQL5 Cookbook : Implementing an Associative Array or a Dictionary for Quick Data Access a été publié :

Auteur : Vasiliy Sokolov

Merci pour tous ces efforts, mais je n'arrive pas à faire fonctionner les exemples de code.

Il semble que Dictionary dict ; devrait être CDictionary dict ;

Que diriez-vous d'un exemple simple et fonctionnel ?

[Supprimé]  

Concernant le nouveau MT4 (Build 1080, 12 mai 2017) ces erreurs se produisent lors de la compilation et empêche l'exécution :

'm_array' - les structures contenant des objets ne sont pas autorisées Dictionary.mqh 303 25

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

 

Bonjour,

Comme dit précédemment par une autre personne, il n'y a pas d'exemple compilable.

Seulement un fichier avec des commentaires en russe.

 
Bonjour Vasily,

Merci pour votre article très utile. En téléchargeant puis en compilant votre dictionary.mqh avec MQL5 build 2063, je reçois les erreurs suivantes :

'GetKey' - funcion already defined and has different type
Compress' - fonction déjà définie et de type différent

Pourquoi cela se passe-t-il ainsi ?

Je vous prie d'agréer, Madame, Monsieur, l'expression de mes salutations distinguées.
Gerik
 

Je suis reconnaissant d'utiliser avec succès la bibliothèque de Vasiliy. C'est une aide précieuse !

Je me souviens que j'avais aussi des erreurs de compilation et que j'ai pu les corriger. Cependant, je ne peux pas vraiment dire lesquelles et ce que j'ai fait.

Et comme je suis extrêmement ennuyé par le formatage du code MQL5 et que je reformate toujours tout dans un format C++ moderne (indentation correcte, évitement des crochets inutiles, pas d'encombrement des commentaires, et ainsi de suite), il m'est impossible de dire quel était le véritable changement dans le code. Tout ce dont je me souviens, c'est que les changements étaient minimes.

Au cas où vous voudriez utiliser la bibliothèque correctement formatée, la voici :

(Notez que j'utilise des tabulations, donc le formatage semble cassé ici, mais dans le MetaEditor il ne l'est pas !)

//+------------------------------------------------------------------+
//|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();
}

 

Je pense avoir trouvé un bug en supprimant un élément et en essayant d'atteindre le dernier élément :

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(); // le résultat sera un pointeur invalide dans CDictionary::GetLastNode(void) -> return m_last_kvp.object ; -> m_last_kvp est un pointeur invalide !
Print(lastWord.GetWord());

L'erreur dans le CDictionary.mqh sera :

accès aupointeur invalide dans 'Dictionary.mqh' (463,9)

Quelqu'un peut-il confirmer cette erreur ? Des idées pour résoudre ce problème ?