OOP, mql5'te şablonlar ve makrolar, incelikler ve kullanım teknikleri

 
" Mql5 dilinin özellikleri, incelikleri ve çalışma yöntemleri " ile ilgili olmayan yorumlar bu konuya taşınmıştır.
 
fxsaber :

Bu, MQL5'in standart davranışıdır: statik değişkenler, global olanlardan sonra çalışmaya başlar.

Bu konuda çok ciddi olabilirsiniz.

Bu "standart davranış" konusuna devam edelim. Hiç kimse evrensel bir çözüm bulamadı, bununla nasıl başa çıkılır? Şimdiye kadar, sadece tüm statik değişkenlerin global olanlarla değiştirildiğini görüyorum (MQ, her şeyi doğrudan global seviyeye taşımak zorunda kalıyor)) Ancak bu, şablonlarda ve makrolarda yapılamaz.

Statik şablon alanları da global değişkenlerden sonra başlatılır.

 

Genel olarak, sorun çözüldü. Ek bir statik bayrak yardımıyla değişkenimizin başlatılıp başlatılmadığını kontrol ediyoruz. Değilse, önceden kaydedilmiş değerler listesinde arayın. Orada değilse, oraya ekleyin. Her şey bir makroya sarılmış STATIC_SET

 class CData
{
 public : 
   string name;
};

template < typename T>
class CDataT : public CData
{
 public :
  T      value;
  CDataT( string name_, T const & value_) { name= name_;  value=value_; }
};


class CDataArray
{
  CData*_data[];
 public : 
  ~CDataArray()                    { for ( int i= 0 ; i< ArraySize (_data); i++) delete _data[i]; }
  
  CData* AddPtr(CData* ptr)        { int i= ArraySize (_data); return ArrayResize (_data, i+ 1 )> 0 ? _data[i]=ptr : NULL ; }
  CData* GetPtr( string name) const { for ( int i= 0 ; i< ArraySize (_data); i++) if (_data[i].name==name) return _data[i];   return NULL ; }
   template < typename T>
   bool Set( string name, T const &value){ CData* ptr= new CDataT<T>(name, value);   return ptr!= NULL && AddPtr(ptr)!= NULL ; }   
   template < typename T>   // Данная перегрузка необходима для передачи указателей, ибо баг в MQL
   bool Set( string name, T &value)      { CData* ptr= new CDataT<T>(name, value);   return ptr!= NULL && AddPtr(ptr)!= NULL ; }   
   template < typename T>
   bool Get( string name, T &value) const
                                   { CDataT<T>* ptr= dynamic_cast <CDataT<T>*> ( GetPtr(name) );
                                     if (ptr) value= ptr.value;
                                     return ptr!= NULL ;
                                   }
 private : 
   template < typename T>
   bool Get( string name, T const &value) const ; // =delete;
}; 

CDataArray __GlobData;


#define __STATIC_SET(var, name, value) \
{ \
   static bool inited= false ; \
   if (!inited && !__GlobData.Get(name, var)) { \   // Если копия переменной ещё не сохранена, то сохраняем её
    var=value;   if (!__GlobData.Set(name, var)) MessageBox ( "Failed to set static var:  " +name, "Error" , 0 ); \
  }\  
  inited= true ; \
}

#define STATIC_SET (var, value) __STATIC_SET(var, __FUNCSIG__ + "::" + #var, value)



//--- Проверка ---


class __CPrint { public : __CPrint( string str) { Print (str); } } __Print( "=======" );


uint StaticTickCount()
{
   static uint tickcount = GetTickCount ();
   Print ( "mql static value: " ,tickcount);

   STATIC_SET (tickcount, GetTickCount ());
   Print ( "my static value: " ,tickcount);
   Sleep ( 50 );
   return tickcount;
}

uint TickCount= StaticTickCount();

void OnStart ()
{  
   Print ( "OnStart" );
  StaticTickCount();
}

Sonuç:

mql static value: 0
my static value: 940354171
OnStart
mql static value: 940354218
my static value: 940354171

 
fxsaber :

Bu, MQL5'in standart davranışıdır: statik değişkenler, global olanlardan sonra çalışmaya başlar.

Bu konuda çok ciddi olabilirsiniz.

MQL'deki statik değişkenler, C++'da olduğu gibi bildirim yerinde değil, global kapsamda başlatılır

Benim düşünceme göre, ilk olarak neyin başlatılacağı, statik veya global (tat hariç) hiçbir fark yoktur - her durumda kurbanlar olacaktır.

Önce sabitlerle başlatılan static ve global değişkenleri , ardından geri kalan her şeyi derleyicinin bulduğu sırayla başlatmak daha doğru olacaktır (bu zaten planda, ancak ne yazık ki takvimde değil).

Ve bu sıralama C++ sıralamasından farklıdır.
Geleneksel olarak, MQL'de derleme iki geçişte gerçekleşir: önce tüm global tanımlar toplanır, ardından fonksiyon gövdeleri derlenir - bu nedenle havuza global olanlardan sonra statik değişkenler eklenir.

 
Ilyas :

(bu zaten planda ama ne yazık ki takvimde yok).

Ve bence bu en önemli öncelik. Çünkü mevcut durumda, bir programlama dili için kabul edilemez olan program yürütme mantığı ihlal edilmektedir. Ve her türlü zil ve ıslık ve yeni özellikler - bu ikincildir.
 

Statik diziler de dahil olmak üzere kodu sonlandırdım. Bu sefer dosya olarak ekliyorum.

Kodlarda kullanılmak üzere 4 makro vardır:

1) STATIC_SET(var, data) - değer verisini var statik değişkenine atar (= operatörü aracılığıyla) veya veri dizisini statik var dizisine kopyalar

 static int a;
STATIC_SET(a, 10 );

static int arr[ 5 ];
const int data[]= { 1 , 2 , 3 , 4 , 5 };
STATIC_SET(arr, data);

2) STATIC_INIT(var, data) - var değişkenini veri değeriyle başlatır (kurucu veya işleç = aracılığıyla) veya var dizisini sabitlerle başlatır - bunlar küme parantezleri içinde belirtilir ve ek olarak normal parantezlerle çerçevelenir:

 static int arr[ 5 ];
STATIC_INIT(arr, ({ 1 , 2 , 3 , 4 , 5 }));

3) STATIC(type, var, value) - statik bir değişkenin bildirilmesi ve başlatılması:

STATIC( int , a, 10 );

4) STATIC_ARR(tür, değişken, değerler) - statik bir dizinin bildirimi ve başlatılması:

STATIC_ARR( int , arr, ({ 1 , 2 , 3 , 4 , 5 }));


Aynı zamanda, ilk iki paragrafta, statik bir dizinin bildirilen boyutunun en az başlatma değerlerinin sayısı kadar olması gerektiği dikkate alınmalıdır, aksi takdirde bir hata olacaktır.

Ve dinamik diziler makrolarla başlatılamaz. Normal başlatıcı işleve gelene kadar boyutları değiştirilemez.

Dosyalar:
StaticVar.mqh  12 kb
 
Ve tüm bunların neden gerekli olduğu sorularına gelince, açıklıyorum: kodumuzun doğru çalışması için, tam olarak algoritmada amaçlandığı gibi ve MQL geliştiricileri veya başka biri tarafından amaçlandığı gibi değil.
 
Alexey Navoykov :
Ve tüm bunların neden gerekli olduğu sorularına gelince, açıklıyorum: kodumuzun doğru çalışması için, tam olarak algoritmada amaçlandığı gibi ve MQL geliştiricileri veya başka biri tarafından amaçlandığı gibi değil.

Veya tüm bunların kod yazmayı kolaylaştırabileceği, kısaltabileceği veya en azından hatalardan koruyabileceği bir örnek gösterebilir misiniz? Ve lütfen, soyut işlevlerle değil, bir danışman veya göstergede ticaretin gerçeklerine mümkün olduğunca yakın.

 
Alexey Viktorov :

Veya tüm bunların kod yazmayı kolaylaştırabileceği, kısaltabileceği veya en azından hatalardan koruyabileceği bir örnek gösterebilir misiniz? Ve lütfen, soyut işlevlerle değil, bir danışman veya göstergede ticaretin gerçeklerine mümkün olduğunca yakın.

Mümkün olduğunca yakın mı demek istiyorsun? Sana özel böyle bir kod yazmamı mı yoksa projelerimi yayınlamamı ister misin? Buna ihtiyacım yok.

Sadece parmaklarımda açıklayacağım. Burada programın veya danışmanın global bir amacı var: CExpert Expert; veya CProgram Programı; Doğal olarak bir şekilde varsayılan olarak dahili olarak başlatılır (birçokları olan tüm dahili nesneler dahil), belki bir yerde bunun için yardımcı global fonksiyonlar kullanılır ve bu fonksiyonlar statik değişkenler içerebilir. Ayrıca, çalışma statik alanlara sahip sınıfları kullanır - bunlar ayrıca statik değişkenlere aittir ve buna göre bu sınıfların çalışması statik alanların değerlerine bağlıdır. Geçersiz alan değerleri - yanlış başlatılmış sınıf nesnesi. Ardından mantıksal zincire kendiniz devam edin. Ve en kötüsü, bunu yalnızca programın yürütülmesi sırasında öğrenmemizdir.

Tüm bunları sıfırdan düşünmedim. Genellikle, başlatılması gereken bozuk işaretçilerle veya değerlerle başlatılması gereken boş dizilerle karşılaşırsınız. Ve sürekli olarak bu küçük şeyleri kurcalamak ve MQ geliştiricileri tarafından benimsenen başlatma algoritmasını memnun etmek için kodu yeniden yapmak - siparişten bıkmış.

Aslında, bu bir hata ve başka bir şey değil. Geliştiriciler belirli bir değişken başlatma dizisini benimsemişse, kod bu diziye göre yürütülmeli ve atlanmamalıdır.

Tüm bunlar size yabancıysa, açıklanan işlevselliği kullanmazsınız, ancak örneğin Peter Konov tarzında yazarsanız, bayrak sizin elinizde, bu sorunlar sizi etkilemedi, sizi tebrik ediyorum.

 
Alexey Navoykov :

Bütün bunları sıfırdan düşünmedim. Genellikle, başlatılması gereken bozuk işaretçilerle veya değerlerle başlatılması gereken boş dizilerle karşılaşırsınız. Ve sürekli olarak bu küçük şeylerle uğraşmak ve MQ geliştiricileri tarafından benimsenen başlatma algoritmasını memnun etmek için kodu yeniden çalışmak - siparişten bıkmış.

Bir sürü stat beyan durumu yaşadım. stat'ın yeniden bildirilmesi koşuluyla, genel düzeyde (OnInit'ten önce) başlatılan sınıflardaki alanlar. alanları sınıf tanımından hemen sonra ve örneğinin global bir değişkenini bildirmeden önce, statik bir alanı başlatma ile ilgili hiçbir zaman sorun olmadı (çünkü bu durumda global olarak kabul edilir ve anladığım kadarıyla sınıf örneğinden önce başlatılır). Yani, metotlar ve fonksiyonlar içinde sadece statik değişken tanımlamayı reddetmeniz yeterlidir ve herhangi bir problem yoktur.

 
Ilya Malev :

Bir sürü stat beyan durumu yaşadım. stat'ın yeniden bildirilmesi koşuluyla, genel düzeyde (OnInit'ten önce) başlatılan sınıflardaki alanlar. alanları sınıf tanımından hemen sonra ve örneğinin global bir değişkenini bildirmeden önce, statik bir alanı başlatma ile ilgili hiçbir zaman sorun olmadı (çünkü bu durumda global olarak kabul edilir ve anladığım kadarıyla sınıf örneğinden önce başlatılır). Yani, metotlar ve fonksiyonlar içinde sadece statik değişkenler tanımlamayı reddetmeniz yeterlidir ve herhangi bir problem yoktur.

Tabii ki, OOP ile pek samimi değilim, bu yüzden net bir şekilde açıklayamam ama yine de ifadenizi düzeltmek istiyorum. Yöntemler ve işlevler içinde statik değişkenler tanımlamayı tamamen reddedemezsiniz, ancak en azından diğer statik değişkenleri, statik değişkenlerin bulunduğu yöntemler veya işlevlerle başlatamazsınız.

 int a( int n)
{
 static int f= 7 ;
 return (f+=n);
}

void OnTick ()
{
 static int b=a( 9 );
}
Bu örnekte, önce static int b değişkeni başlatılacaktır, ancak int a (int n) işlevinin içinde yer alan static int f değişkeni henüz başlatılmamış ve sonuç olarak çöp alıyoruz.
Neden: