- Любые вопросы новичков по MQL4 и MQL5, помощь и обсуждение по алгоритмам и кодам
- Ошибки, баги, вопросы
- Особенности языка mql5, тонкости и приёмы работы
Это стандартное поведение MQL5: static-переменные начинают работать после глобальных.
Можно очень серьезно нарваться из-за этого.Продолжим тему насчёт этого "стандартного поведения". Никто не придумал универсальное решение, как с ним бороться? Я пока вижу только замену всех статических переменных на глобальные (MQ прям вынуждают выносить всё на глобальный уровень )) Но этого не сделать в шаблонах и в макросах.
Статические поля шаблона тоже инициализируются после глобальных переменных.
В общем, задача решена. С помощью дополнительного статического флага проверяем, инициализирована ли наша переменная. Если нет, то ищем её в списке ранее сохранённых значений. Если там нет, то добавляем туда. Всё обёрнуто в макрос 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(); }
Результат:
mql static value: 0
my static value: 940354171
OnStart
mql static value: 940354218
my static value: 940354171
Это стандартное поведение MQL5: static-переменные начинают работать после глобальных.
Можно очень серьезно нарваться из-за этого.static переменные в MQL инициализируются в глобальном скопе, а не по месту объявления как это в C++
По моему мнению, разницы, что инициализировать первым, static или глобальные, нет (кроме как на вкус) - в любом случае найдутся пострадавшие.
Правильнее будет сначала инициализировать static и глобальные переменные, которые инициализируются константами, затем все остальные в порядке обнаружения компилятором (это уже имеется в плане, но к сожалению не в календарном).
А порядок этот отличается от порядка C++.
Условно, компиляция в MQL проходит в два прохода: сначала собираются все глобальные определения, затем происходит компиляция тел функций - именно поэтому static переменные добавляются в пул после глобальных
(это уже имеется в плане, но к сожалению не в календарном).
Я доработал код, в том числе для статических массивов. На этот раз прикрепляю в виде файла.
Для использования в кодах предусмотрено 4 макроса:
1) STATIC_SET(var, data) - присваивает статической переменной var значение data (через оператор=), либо копирует массив data в статический массив var
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 значением data (через конструктор или оператор=), либо инициализирует массив var константами - задаются в фигурных скобках и дополнительно обрамляются обычными скобками:
static int arr[5]; STATIC_INIT(arr, ({1, 2, 3, 4, 5}));
3) STATIC(type, var, value) - объявление и инициализация статической переменной:
STATIC(int, a, 10);
4) STATIC_ARR(type, var, values) - объявление и инициализация статического массива:
STATIC_ARR(int, arr, ({1, 2, 3, 4, 5}));
При этом в первых двух пунктах нужно учитывать, что объявленный размер статического массива должен быть не менее числа инициализирующих значений, иначе будет ошибка.
И инициализировать макросами динамические массивы невозможно. Их размер не изменить, пока штатный инициализатор не доберётся до функции.
И по поводу вопросов, зачем это всё надо, поясняю: это надо для того, что наш код работал правильно, именно так, как задумано в алгоритме, а не так, как задумали разработчики MQL или кто-либо ещё.
А можете показать пример когда вот это всё может облегчить написание кода, сократить его или хотя-бы оградить от ошибки. И пожалуйста не абстрактными функциями, а максимально приближёнными к реалиям торговли, в советнике или индикаторе.
А можете показать пример когда вот это всё может облегчить написание кода, сократить его или хотя-бы оградить от ошибки. И пожалуйста не абстрактными функциями, а максимально приближёнными к реалиям торговли, в советнике или индикаторе.
В смысле максимально приближёнными? Вы хотите чтобы я специально для вас написал такой код, или выкладывал свои проекты? Мне это незачем.
Я просто объясню на пальцах. Вот у нас есть глобальный объект программы или советника: CExpert Expert; или CProgram Program; Он естественно как-то внутри инциализируется по умолчанию (включая все внутренние объекты, коих немало), возможно где-то используются для этого вспомогательные глобальные функции, а в этих функциях возможно присутствуют статические переменные. А также в работе используются классы, у которых есть статические поля - они тоже относятся к статическим переменным, и соответственно работа этих классов завязана на значениях статических полей. Неверные значения полей - неверно инициализированный объект класса. Дальше логическую цепочку сами продолжите. И самое плохое, что мы об этом узнаём только в процессе выполнения программы.
Я же не с пустого места всё это придумывал. Часто приходится натыкаться на битые указатели, которые должны были быть инициализированы, или незаполненные массивы, которые должны были быть инциализированы значениями. А постоянно ковыряться в этих мелочах и переделывать код в угоду алгоритму инициализации, принятому разработчиками MQ - поднадоело порядком.
По факту - это баг, и никак иначе. Коль разработчиками принята определённая последовательность инициализации переменных, то и код должен исполняться в соответствии с этой последовательностью, а не в обход её.
Если для вас это всё незнакомо, вы не пользуетесь описанным функционалом, а пишете например в стиле Петра Конова, то флаг вам в руки, эти проблемы вас не коснулись, с чем и поздравляю.
Я же не с пустого места всё это придумывал. Часто приходится натыкаться на битые указатели, которые должны были быть инициализированы, или незаполненные массивы, которые должны были быть инциализированы значениями. А постоянно ковыряться в этих мелочах и переделывать код в угоду алгоритму инициализации, принятому разработчиками MQ - поднадоело порядком.
У меня было множество ситуаций объявления стат. полей в классах, инициализируемых на глобальном уровне (до OnInit), при условии повторного объявления стат. поля сразу после описания класса, и до объявления глобальной переменной его экземпляра, никогда не возникало проблем с инициализацией статик-поля (поскольку в этом случае она считается глобальной и инициализируется до экземпляра класса как я понимаю). То есть нужно только отказаться от объявления статик-переменных внутри методов и функций и никакой проблемы нет.
У меня было множество ситуаций объявления стат. полей в классах, инициализируемых на глобальном уровне (до OnInit), при условии повторного объявления стат. поля сразу после описания класса, и до объявления глобальной переменной его экземпляра, никогда не возникало проблем с инициализацией статик-поля (поскольку в этом случае она считается глобальной и инициализируется до экземпляра класса как я понимаю). То есть нужно только отказаться от объявления статик-переменных внутри методов и функций и никакой проблемы нет.
Я конечно с ООП не очень-то дружу, потому и не могу внятно объяснить, но всё-же хочу поправить ваше утверждение. Можно не полностью отказаться от объявления статик-переменных внутри методов и функций, а хотя-бы не инициализировать другие статик-переменные теми методами или функциями в которых присутствуют статик-переменные.
int a(int n) { static int f=7; return(f+=n); } void OnTick() { static int b=a(9); }В этом примере сначала будет инициализирована переменная static int b но пока ещё не инициализирована переменная static int f находящаяся внутри функции int a(int n) и в результате получим белиберду.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования