Generic Class Library - bugs, description, questions, usage and suggestions - page 14

 
Sergey Dzyublik:

As for me, if the object doesn't implement some interface or method, it's better to generate an explicit exception than to keep silent and then search for the source of the problem.

  • In MQL5 there are no interfaces.
  • There are no exceptions in MQL5.

In the end, you get a great self-shooting and evident concealment of the problem with the code.

 
Vasiliy Sokolov:

For the same reason, the comparison is incorrect. How can you compare a custom CHashMap and work with system functions to get a trading environment?

Keep writing sub-optimal code, since you do not accept iron arguments.

Forum on trading, automated trading systems & strategy testing

Generic classes library - bugs, description, questions, usage features and suggestions

fxsaber, 2017.12.08 22:46

For a more realistic tester case (2000 trades and 1,000,000 single history accesses) the result looks like this

2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitFull))] = 122969
2017.12.05 00:00:00   Time[SetHashMap()] = 816
2017.12.05 00:00:00   4829800340.792288
2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitHashClear))] = 23852
2017.12.05 00:00:00   Time[HistorySelect(0,INT_MAX)] = 1
2017.12.05 00:00:00   4829800340.792288
2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitClear))] = 114427

Almost 100ms savings per pass! If, say, we do Optimize for 10,000 full passes, the Hash variant will end up 15 minutes faster.

Vasiliy Sokolov:

All in all, yes, it's a great self-shooting and obvious concealment of the problem with the code.

Both wrote some nonsense, without understanding that HashMap in current form doesn't work for struct and union.
 
fxsaber:

Keep writing non-optimal code if you don't accept ironclad arguments.

Some bullshit both of you wrote, not understanding one bit that HashMap in its current form doesn't work for struct and union.

Dear, if you smoke something and can not carefully read the post of the interlocutor - then it's your problem and it's not everyone else, but only the sick person who needs treatment.
Let's repeat: nobody raised the question about struct and union, nobody disputed your ideas about it....


It was specifically about these terms of the code.
From the standard library:

//+------------------------------------------------------------------+
//| Returns a hashcode for custom object.                            |
//+------------------------------------------------------------------+
template<typename T>
int GetHashCode(T value)
  {
//--- try to convert to equality comparable object  
   IEqualityComparable<T>*equtable=dynamic_cast<IEqualityComparable<T>*>(value);
   if(equtable)
     {
      //--- calculate hash by specied method   
      return equtable.HashCode();
     }
   else
     {
      //--- calculate hash from name of object
      return GetHashCode(typename(value));
     }
  }
//+------------------------------------------------------------------+



And your added ones:

Forum on trading, automated trading systems and trading strategies testing

Generic classes library - bugs, description, questions, peculiarities of use and suggestions

fxsaber, 2017.12.08 21:17

Added another overload

template<typename T>
int GetHashCode(T &value)
  {
    return GetHashCode(typename(value));
  }


What you were trying to convey:
Hash is used to speed up the search for items in a container. The speed is O(1) - it does not depend on the number of elements added to the container.
Situation - user uses his own class as a key.
1) Because of the absence of multiple inheritance of interfaces(there are no interfaces in MQL5), the user cannot inherit from IEqualityComparable;
2) The user also forgets to specify an explicit specification for the GetHashCode template function.

The consequences - the user is not informed that he/she omitted something and the code is executed successfully, without generating exceptions(there are no exceptions in MQL5).
The speed drops from O(1) with average hash calculation constant to O(n) with a quite large constant for comparison.
Only with a large number of elements in a container and wasting a lot of time searching for
a bottleneck in the implementation, the user might be able to find the cause of the problems - the lack of an explicit GetHashCode specification for his class.
No offense, thanks.

 
And still, why all these undoubtedly cool things for trading?
Well, never in my life have I had to lift thousands of deals from the history, search for the orders that generated them, etc.
 
Sergey Dzyublik:

Dear, if you smoke something and are not able to read attentively the post of the interlocutor - then it's your problem and it is necessary to treat not all the rest, but only the patient.

Apparently there is no way for teetotalers to close the mitten

#include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280
#include <crc64.mqh>       // https://www.mql5.com/en/blogs/post/683577

template<typename T>
int GetHashCode( T &value )
{
  ulong crc = 0;

  return((int)crc64(crc, _R(value).Bytes, sizeof(T)));
}

template<typename T>
int GetHashCode( T &value[] )
{
  ulong crc = 0;

  return((int)crc64(crc, _R(value).Bytes, ArraySize(value) * sizeof(T)));
}
 
Comments not relevant to this topic have been moved to "Algorithms, Solution Methods, and Performance Comparisons".
 

Although it was not originally planned to make a collection of examples from the branch, I still feel the need to add some examples so that those who have not yet used the algorithms in their practice, understand why it is convenient and, most importantly, simple.

 

Example 1: Associating a runtime error with its string description

Quite often it is necessary to translate numerical constants into string literals. For example, it is better to duplicate error codes with a clear caption describing the essence of the error. This is not a very difficult task and it is usually solved through a special function or switch-case or a set of ifs:

string ErrorDescription(int error_code)
   if(error_code == 40001)
      return ("Неожиданная внутренняя ошибка");
   //...
}

Any such solution has a right to life. But we will describe the solution based on CHashMap and show its advantages.

The algorithm may look like this:

  • We create an associative array of the <bug code - bug description> kind;
  • Add possible error codes and their description into this dictionary;
  • Directly and without intermediaries we address the dictionary to get the error description by its code
This code looks as follows:

//+------------------------------------------------------------------+
//|                                                     OrdersID.mq5 |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Generic\HashMap.mqh>
input ulong FindTicketOrder = 82479995;

CHashMap<int, string> ErrorDescription;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void AddDescriptionInfo(void)
{
   // Добавим коды системных ошибок
   ErrorDescription.Add(0,    "Операция выполнена успешно");
   ErrorDescription.Add(4001, "Неожиданная внутренняя ошибка");
   ErrorDescription.Add(4002, "Ошибочный параметр при внутреннем вызове функции клиентского терминала");
   ErrorDescription.Add(4003, "Ошибочный параметр при вызове системной функции");
   ErrorDescription.Add(4004, "Недостаточно памяти для выполнения системной функции");
   // Можно добавлять константные значения вместо чисел
   ErrorDescription.Add(ERR_STRUCT_WITHOBJECTS_ORCLASS, "Структура содержит объекты строк и/или динамических массивов и/или структуры с такими объектами и/или классы");
   ErrorDescription.Add(ERR_INVALID_ARRAY, "Массив неподходящего типа, неподходящего размера или испорченный объект динамического массива");   
   ErrorDescription.Add(ERR_ARRAY_RESIZE_ERROR, "Недостаточно памяти для перераспределения массива либо попытка изменения размера статического массива");
   //...
}
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
   AddDescriptionInfo();
   string last_error = "";
   ErrorDescription.TryGetValue(GetLastError(), last_error);
   printf("Последняя ошибка: " + last_error);
}
//+------------------------------------------------------------------+

Once the error codes are filled in, they can be accessed with just one line, without using different functions. Besides, let me remind you that in some cases this code will work even faster than several tens of ifs, since the addressing to the necessary error occurs directly, with average speed O(1).

 
Vasiliy Sokolov:

Once the error codes are populated, they can be accessed with just one line, without using different functions.

ErrorToString will have to be written anyway. So the argument, as a plus, is weak.

In addition, let me remind you that in some cases, this code will work even faster than a few dozen ifs, because addressing to the desired error is done directly, with an average speed of O(1).

That's a real plus.

 

The proposed dictionary solution has several advantages, the most important of which is not entirely obvious at first sight. When we write code like this:

string ErrorDescription(int error_code)
   if(error_code == 40001)
      return ("Неожиданная внутренняя ошибка");
   //...
}

We're hardwiring the error codes into the EA code itself. When we fill the dictionary, we do it dynamically, i.e. at the moment of program execution. The dynamic approach gives more flexibility. For example, the error codes may be contained in a special file like ErrorsCode.txt:

4001;Операция выполнена успешно
4002;Неожиданная внутренняя ошибка
4003;Ошибочный параметр при вызове системной функции
...

At the moment of launching the program may read this file and fill the dictionary with the required codes and then return the user the required variant of the string. There can be several such files: one file for each language. This way, a localization can be done where the error codes in the user's language are displayed, depending on the user's language. Furthermore, the user himself can translate these error codes into his own language once and the program itself "learns" to output the desired message in his language. This is how most programs are localized, when the translation of a menu is contained in a text file, and the program loads it, depending on the settings. That is, without any recompilation of the program and without changing its algorithm, we can significantly affect the presentation of its results.

Reason: