Questions on OOP in MQL5 - page 20

 
Koldun Zloy:
Static variable, it will be the same for all instances without inheritance.

Checked it out, well yes! it turned out to be simpler than that!

/+------------------------------------------------------------------+
string MyTerminalInfoString()
  {
   Print(__FUNCTION__);
   return("ru");
  }
//+------------------------------------------------------------------+
class B
  {
  protected:
   static const string Language;
public:
                     B() {Print(__FUNCTION__,Language); }
  };
//+------------------------------------------------------------------+
static const string B::Language=MyTerminalInfoString();
//+------------------------------------------------------------------+
B *arrB[5];
//+------------------------------------------------------------------+
int OnInit()
  {
   for(int i=0; i<5; i++)
      arrB[i] = new B;
   return(INIT_SUCCEEDED);
  }

2019.08.29 15:14:09.847 tst__ EURUSD,M15: initialized

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: B::Bru

2019.08.29 15:14:09.847 tst__ EURUSD,M15: MyTerminalInfoString

I see that the Language variable has only been initialized once, i.e. this is a simpler solution

Thank you!

 
Roman:

Please tell me, how is it different?
Creating an object, or pointer, in this way

from classic creation

The difference is that in the first case, you don't have to specify theCClass class identifier in C++.

 

I sketched out my class, which should initialise fields once with constant values, and it seems to work as intended:

class Cdeal : public CObject
  {

private:
   static const double VolumeMAX;
   static const double VolumeMIN;
   static const double VolumeSTEP;
   static const int  VolumeDIGITS;
   static int        GetDigitsInVolumeStep();
   SSettingsForOrder m_set;
public:
                     Cdeal(SSettingsForOrder &set);
  };
//____________________________________________________________________
static int Cdeal::GetDigitsInVolumeStep()
  {
   long i=10000000,k=long(::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP)/0.0000001);
   int result=0;
   while(result<7 && k%i>0)
     {
      i/=10;
      result++;
     }
   return(result);
  }
//____________________________________________________________________
static const double Cdeal::VolumeMAX = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
//____________________________________________________________________
static const double Cdeal::VolumeMIN = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
//____________________________________________________________________
static const double Cdeal::VolumeSTEP = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
//____________________________________________________________________
static const int Cdeal::VolumeDIGITS = Cdeal::GetDigitsInVolumeStep();
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
Cdeal::Cdeal(SSettingsForOrder &set)
  {
   m_set = set;
  }
//+------------------------------------------------------------------+

I don't like 2 things:

1. I call SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP) - because order of initialization is not defined, i.e. it's not sure that VolumeSTEP will be initialized first, and only thenGetDigitsInVolumeStep() will be called

2. I want to get rid of static method static int GetDigitsInVolumeStep() - I've seen a video on youtube saying that in pure OOP one should not use static methods, and now I'm fighting against windmills

the link to the video, it is essentially the samehttps://youtu.be/lfdAwl3-X_c andhttps://youtu.be/zME4SOCHT0I


how can i rewrite these 2 points that i don't like differently ?

 
Igor Makanu:

1. I repeat the call to SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP) - because the order of initialisation is not specified, i.e. it is not certain that I will have VolumeSTEP initialised first, and only then GetDigitsInVolumeStep() will be called

1. i.e. are you afraid that the order of statics initialization is undefined? Actually it's not, the pluses initialize in order of definition in the code. So don't sweat it, VolumeSTEP first and then VolumeDIGITS.

 
Vict:

1. So you are afraid that order of statics initialization is undefined? Actually it's not so, in pluses initialization is in order of definition in code. So don't sweat it, first VolumeSTEP and then VolumeDIGITS.

Yes, not that I'm afraid, let's say I'm protected, I believe that the code should have at least some guarantee against changes in compilation behavior - in general YES

ZS: here in general, why the topic for the first time a day - I want to see how realistic to make something like in the video author Egor says, while I doubt that we get something effective - now I'm using static methods, and with the initialization are already some doubts appeared

 
Vict:

1. i.e. are you afraid that the order of initialisation of statics is undefined? Actually it's not, the pluses initialize in order of definition in the code. So don't sweat it, VolumeSTEP first and then VolumeDIGITS.

In MQL too.

https://www.mql5.com/ru/docs/basis/oop/staticmembers

Документация по MQL5: Основы языка / Объектно-ориентированное программирование / Статические члены класса
Документация по MQL5: Основы языка / Объектно-ориентированное программирование / Статические члены класса
  • www.mql5.com
Члены класса могут быть объявлены с использованием модификатора класса памяти static. Такие члены данных разделяются всеми экземплярами данного класса и хранятся в одном месте. Нестатические члены данных создаются для каждой переменной-объекта класса. Отсутствие возможности объявлять статически члены класса привело бы к необходимости объявлять...
 
Igor Makanu:

Yes, not that I'm afraid, let's say I'm protected, I believe that the code should have at least some guarantee against changes in compilation behavior - in general YES

ZS: here in general, why the topic for the first time a day - I want to see how realistic to make something like in the video author Egor says, while I doubt that we will get something effective - now I'm using static methods, and with the initialization are already some doubts appeared

#ifndef _C_TRADE_CONST_
#define _C_TRADE_CONST_

#define TRADE_CONST_DECL  CTradeConst* cTradeConst
#define TRADE_CONST  cTradeConst
#define _symbol      cTradeConst.symbol
#define _lotDigits   cTradeConst.lotDigits
#define _lotStep     cTradeConst.lotStep
#define _lotMax      cTradeConst.lotMax
#define _lotMin      cTradeConst.lotMin
#define _tickSize    cTradeConst.tickSize
#define _point       cTradeConst.point
#define _stopLevel   cTradeConst.stopLevel
#define _freezeLevel cTradeConst.freezeLevel
#define _digits      cTradeConst.digits
#ifdef __MQL5__
   #define _marginMode        cTradeConst.marginMode
   #define _executionMode     cTradeConst.executionMode
   #define _expirationMode    cTradeConst.expirationMode
   #define _fillingMode       cTradeConst.fillingMode
   #define _orderMode         cTradeConst.orderMode
#endif 

class CTradeConst
  {
public:
   string                  symbol;
   int                     lotDigits;
   double                  lotStep;
   double                  lotMax;
   double                  lotMin;
   double                  tickSize;
   double                  point;
   double                  stopLevel;
   double                  freezeLevel;
   int                     digits;
#ifdef __MQL5__
   ENUM_ACCOUNT_MARGIN_MODE      marginMode;
   ENUM_SYMBOL_TRADE_EXECUTION   executionMode;
   int                           expirationMode;
   int                           fillingMode;
   int                           orderMode;
#endif   
public:
                     CTradeConst(string mSymbol):symbol(mSymbol==NULL?_Symbol:mSymbol){if (!Init()) delete GetPointer(this);}
private:
   bool              Init();
  };
bool CTradeConst::Init(void){
   if (!MQLInfoInteger(MQL_TESTER)&&!SymbolSelect(symbol,true)) return false;
   lotStep=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
   lotDigits=MathMax(-(int)MathFloor(MathLog10(lotStep)),0);
   lotMax=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
   lotMin=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
   point=SymbolInfoDouble(symbol,SYMBOL_POINT);
   tickSize=SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE);
   stopLevel=SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL)*point;
   freezeLevel=SymbolInfoInteger(symbol,SYMBOL_TRADE_FREEZE_LEVEL)*point;
   digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
#ifdef __MQL5__
   marginMode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   executionMode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(symbol,SYMBOL_TRADE_EXEMODE);
   fillingMode=(int)SymbolInfoInteger(symbol,SYMBOL_FILLING_MODE);
   expirationMode=(int)SymbolInfoInteger(symbol,SYMBOL_EXPIRATION_MODE);
   orderMode=(int)SymbolInfoInteger(symbol,SYMBOL_ORDER_MODE);
#endif 
   return true;}
   
#endif 

To use it this way:

#include "CTradeConst.mqh"

#define  FLAG_CONST_CREATE_HERE   0x1

class CExample{
   TRADE_CONST_DECL;
   int cFlag;
                  CExample(string mSymbol,CTradeConst* mTradeConst):
                     TRADE_CONST(!CheckPointer(mTradeConst)?new CTradeCons(mSymbol)t:mTradeConst)
                     {if (!CheckPointer(mTradeConst)) cFlag|=FLAG_CONST_CREATE_HERE;}
                 ~CExample()  {if (bool(cFlag&FLAG_CONST_CREATE_HERE)) delete TRADE_CONST;}
};

And in code like this:

CTradeConst* tradeConst=new CTradeConst;
CExampe* exsample[10];
for (int i=0;i<10;example[i++]=new CExample(_Symbol,tradeConst));
 
The static field option has one huge disadvantage. You cannot have different values of this field in different instances of the class.
 
Vladimir Simakov:

Use as follows:

And in the code like this:

oops, a little bit of a misdirect, that's what I can do

Here's what I want to achieve in general:

1. the CDeal class is standalone, it opens/closes/controls its order by one strategy - the strategies are enumerated, so the optimizer can scroll through the mix of strategies

2. according to step 1, I do not need anywhere else to have the terminal environment variables SYMBOL_VOLUME_MAX, SYMBOL_VOLUME_MIN,SYMBOL_VOLUME_STEP and quantity of characters in SYMBOL_VOLUME_STEP - it does not change during MQL-program operation

3. starting from items 1 and 2, i can encapsulate methods of opening/closing/trading an order and order properties themselves in one class CDeal - i'm not going to use all that anywhere later, am i? - i.e. i can use all of them in one class


pp 1-3 everything is solvable, but... OK, now you have helped me with the statics, let it be so, because there is a help, at least one way to justify my decision, now the code is like this

class Cdeal : public CObject
  {
private:
   static const double VolumeMAX;
   static const double VolumeMIN;
   static const double VolumeSTEP;
   static const int  VolumeDIGITS;
   static int        GetDigitsInVolumeStep();
   SSettingsForOrder m_set;
public:
                     Cdeal() { Print(__FUNCTION__,"VolumeMAX = ",VolumeMAX," , VolumeMIN = ",VolumeMIN," , VolumeSTEP = ",VolumeSTEP," , VolumeDIGITS = ",VolumeDIGITS); }
  };
//____________________________________________________________________
static int Cdeal::GetDigitsInVolumeStep()
  {
   long i=10000000,k=long(VolumeSTEP/0.0000001);
   int result=0;
   while(result<7 && k%i>0)
     {
      i/=10;
      result++;
     }
   Print(__FUNCTION__);
   return(result);
  }
//____________________________________________________________________
static const double Cdeal::VolumeMAX = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
//____________________________________________________________________
static const double Cdeal::VolumeMIN = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
//____________________________________________________________________
static const double Cdeal::VolumeSTEP = ::SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
//____________________________________________________________________
static const int Cdeal::VolumeDIGITS = Cdeal::GetDigitsInVolumeStep();
//+------------------------------------------------------------------+

created 3 instances of Cdeal, got it in the log:

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: initialized

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: Cdeal::CdealVolumeMAX = 100000.0 , VolumeMIN = 0.01 , VolumeSTEP = 0.01 , VolumeDIGITS = 2

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: Cdeal::CdealVolumeMAX = 100000.0 , VolumeMIN = 0.01 , VolumeSTEP = 0.01 , VolumeDIGITS = 2

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: Cdeal::CdealVolumeMAX = 100000.0 , VolumeMIN = 0.01 , VolumeSTEP = 0.01 , VolumeDIGITS = 2

2019.08.29 21:53:53.613 AnyGrid_v1.00 EURUSD,M30: Cdeal::GetDigitsInVolumeStep

So far everything has worked as planned!


I.e., now there are no sections of repeated code (calls) - it is an efficient code, but how to get away from using static function static int Cdeal::GetDigitsInVolumeStep() - in the video, the author believes that it is possible to write OOP code without static functions, I see that not only is it inconvenient, but it is not always possible to use everything, about Get and Set methods - most likely I will do without them, I'll see later


Vladimir Simakov:
The static field option has one huge disadvantage. You can't have different values of this field in different instances of the class.

Yes, thanks, I know that, the purpose is just to get an efficient code without unnecessary variables and unnecessary use of memory



I still have not solved the question:how to avoid using static function static int Cdeal::GetDigitsInVolumeStep()

 
Igor Makanu:

Here's an outline of my class, which should initialize fields once with constant values, it seems to work as intended:

Is it purely a test class or are you really going to use it?
If it's the second one, you shouldn't do it that way. Static variables are not reinitialized when the symbol changes, as far as I remember.
And in general, it's not a good idea to initialize constant statics with external values, which are not necessarily available at the time of initialization.
Reason: