Пишу, как и Вы, - исходники методов внутри классов. Но бывают ситуации, когда это "неудобно".
Например, у вас определен класс ЧАСТИЧНО (не все методы имеют исходники) в mqh-файле. И вы его (файл) подключаете (include) к разным своим прогам, не меняя. Но при этом исходники нескольких методов этого класса прописываете в mq4-файле, тем самым давая каждой своей проге различный функционал подключаемого класса.
Такое может понадобиться в различных ситуациях. Чаще всего - это какие-нибудь конвертеры/преобразователи.
Представление классов в таком виде воспринимаю как бардак. Это терпимо, если методов немного. Но если список их велик, то при таком способе представления не видно чёткой структуры класса и изучение его затрудняется.
Хорошим примером считаю стандартную библиотеку MQL. Всё очень аккуратно оформлено. Можно даже сказать - идеально.
Пишу, как и Вы, - исходники методов внутри классов. Но бывают ситуации, когда это "неудобно".
Например, у вас определен класс ЧАСТИЧНО (не все методы имеют исходники) в mqh-файле. И вы его (файл) подключаете (include) к разным своим прогам, не меняя. Но при этом исходники нескольких методов этого класса прописываете в mq4-файле, тем самым давая каждой своей проге различный функционал подключаемого класса.
Такое может понадобиться в различных ситуациях. Чаще всего - это какие-нибудь конвертеры/преобразователи.
А чем вам виртуальные функции не подходят, идеальный же вариант..
Представление классов в таком виде воспринимаю как бардак. Это терпимо, если методов немного. Но если список их велик, то при таком способе представления не видно чёткой структуры класса и изучение его затрудняется.
Хорошим примером считаю стандартную библиотеку MQL. Всё очень аккуратно оформлено. Можно даже сказать - идеально.
Я вот на C# давно программирую и бардака не ощущаю. А знаете почему? Потому есть мощная IDE с кучей вариантов представления информации. Конечно, MQ молодцы и проделали отличную работу, но за командой MS им, понятное дело, не угнаться.
В Visual Studio можно нажать collaps to definition и все в выбранной области сжимается до определений.
Хотя, мне в 95% хватает вот такого простейшего средства. Это мой проект на MQL4, который я загнал в студию, потому что иногда не хватает редактора MQL для анализа и, тем более, рефакторинга.
Я вот на C# давно программирую и бардака не ощущаю. А знаете почему? Потому есть мощная IDE с кучей вариантов представления информации. Конечно, MQ молодцы и проделали отличную работу, но за командой MS им, понятное дело, не угнаться.
В Visual Studio можно нажать collaps to definition и все в выбранной области сжимается до определений.
Хотя, мне в 95% хватает вот такого простейшего средства. Это мой проект на MQL4, который я загнал в студию, потому что иногда не хватает редактора MQL для анализа и, тем более, рефакторинга.
Представление классов в таком виде воспринимаю как бардак. Это терпимо, если методов немного. Но если список их велик, то при таком способе представления не видно чёткой структуры класса и изучение его затрудняется.
VDev:
Мысль понял, подход рабочий, хотя и неправильный. Например, меняете что-то в .mqh, хотя бы одну переменную в метод добавили и все, надо перелопачивать все .mq4.
Как-то не испытал на себе подобной проблемы.
Вообще, для этого в других языках существуют интерфейсы и абстрактные классы, но в MQL их нет. А чем вам виртуальные функции не подходят, идеальный же вариант..
Представление классов в таком виде воспринимаю как бардак. Это терпимо, если методов немного. Но если список их велик, то при таком способе представления не видно чёткой структуры класса и изучение его затрудняется.
Хорошим примером считаю стандартную библиотеку MQL. Всё очень аккуратно оформлено. Можно даже сказать - идеально.
Полностью поддерживаю.
Лично я метод внутри класса описываю только если он содержит одну строку. (Как правило, что-то типа int GetValue() ). В крайнем случае - три-пять строк, не более.
Во всех остальных случаях пишу классы в паре файлов mqh-mq5.
Раздельную компиляцию MQL не поддерживает, нет даже возможности создать проект с несколькими .mq4/5 файлами. Выносить в отдельный заголовочник определения методов класса тоже вроде нельзя (или можно?). В общем было бы интересно услышать мотивацию от представителей MQ.
P.S Кстати, как вы знаете, в современных языках типа C# вообще отказались от заголовочных файлов ввиду ненужности и код пишется в нижеприведенном стиле.
У меня ВСЕ классы имеют исключительно mqh-mq5 структуру. Для примера, моя реализация вашего класса CLogFile:
Файл LOG.MQH:
#include <Files\FileTxt.mqh>
#include <MyLib\Common\GlobalConsts.mqh>
// Дефайн, управляющий расположением глобального лог-файла (если он есть)
// Если нужен отдельный лог-файл для каждого терминала - определить этот дефайн.
// #define GLOBAL_LOG_IS_IN_PRIVATE_FOLDER
#define TIMEDATE_TIMEFORMAT_MODE TIME_DATE | TIME_SECONDS
#define TIME_TIMEFORMAT_MODE TIME_SECONDS
#define DATE_TIMEFORMAT_MODE TIME_DATE
class CLogFile:public CObject
{
public:
enum ELogMode
{
LM_NONE,
LM_OVERWRITE,
LM_APPEND
};
enum ETimestampMode
{
TM_NONE,
TM_TIMEDATE,
TM_TIME,
TM_DATE
};
protected:
static const string LOG_OPEN_STR;
static const string LOG_CLOSE_STR;
static const string LOG_PATH_STR;
CFileTxt m_ftFile;
ELogMode m_lmLogMode;
bool m_bWriteToLogOpenClose;
bool m_bFlushEveryOperation;
int m_iTimeToStringMode;
bool m_bCommon;
// Функция просто пишет очередную строку в конец файла c добавлением символа "\n".
bool _LogString(string strLogStr);
// Функции добавляют строку с сообщением об открытии файла
bool _LogOpen();
bool _LogClose();
// Функция, которая в DEBUG версии выдает указанный комментарий
// В RELEASE версии ничего не происходит.
void _DebugComment(string strMessage);
public:
CLogFile(ELogMode lmMode=LM_APPEND,ETimestampMode tmTimeMode=TM_TIMEDATE,bool bWriteToLogOpenCloseOfLogFile=false,bool bFlushEveryOperation=false,bool bCommon = true);
virtual ~CLogFile();
bool Open(string sLogFileName);
void Close();
void Flush();
bool IsOpened() { return(m_ftFile.Handle()!=INVALID_HANDLE);};
string GetName() { return(m_ftFile.FileName()); };
void SetTimestampMode(ETimestampMode tsMode);
bool LogString(string strLogStr);
bool LogString(string strLogStr1,string strLogStr2,string strSeparator = " ");
bool LogEmptyString();
// Функция добавляет строку в глобальный логфайл
static void GlobalLogString(string strOutput,string strFilename = MAIN_LOG_NAME);
// Функция добавляет строку в логфайл с указанным названием
static bool AddStringToLog(string strOutput,string strFilename,bool bClearContentsBeforeAdding = false);
};
Файл .MQ5:
#property library
#include <MyLib\Common\GlobalConsts.mqh>
#include <MyLib\Common\Log.mqh>
const string CLogFile::LOG_OPEN_STR = "Log file has opened.";
const string CLogFile::LOG_CLOSE_STR = "Log file has closed.";
const string CLogFile::LOG_PATH_STR = "Log file path: ";
// Gеременная логфайла, в которую складываются все трассировочные сообщения
// Спецификатор static отсутствует - билд 930 перестал позволять использовать в статических переменных нестатические члены класса.
#ifdef GLOBAL_LOG_IS_IN_PRIVATE_FOLDER
CLogFile lfGMainLogFile(LM_OVERWRITE,TM_TIMEDATE,true,true,false);
#else // GLOBAL_LOG_IS_IN_PRIVATE_FOLDER
CLogFile lfGMainLogFile(LM_OVERWRITE,TM_TIMEDATE,true,true,true);
#endif // GLOBAL_LOG_IS_IN_PRIVATE_FOLDER
bool CLogFile::_LogString(string strLogStr)
{
if(m_lmLogMode == LM_NONE)
return(true);
m_ftFile.Seek(NULL,SEEK_END);
if(m_ftFile.IsEnding() !=true)
return(false);
long lStrLen = StringLen(strLogStr);
long lStrWritten = m_ftFile.WriteString(strLogStr+STRING_TERMINATOR);
// В данной проверке важно, что STRING_TERMINATOR_LENGTH у нас 2 символа (CRLF), но в строке эти символы считаются за один "\n"
if(lStrLen + STRING_TERMINATOR_LENGTH != lStrWritten)
return(false);
if(m_bFlushEveryOperation)
m_ftFile.Flush(); // exception
return(true);
}
bool CLogFile::_LogOpen()
{
if(m_bWriteToLogOpenClose == false)
return(true);
string strResStr;
if(m_iTimeToStringMode != NULL)
strResStr = TimeToString(TimeLocal(),m_iTimeToStringMode) + SPACE_SYMBOL;
strResStr += LOG_OPEN_STR;
strResStr += SPACE_SYMBOL;
strResStr += LOG_PATH_STR;
strResStr += m_ftFile.FileName();
return(_LogString(strResStr));
}
bool CLogFile::_LogClose()
{
if(m_bWriteToLogOpenClose == false)
return(true);
string strResStr;
if(m_iTimeToStringMode != NULL)
strResStr = TimeToString(TimeLocal(),m_iTimeToStringMode) + SPACE_SYMBOL;
strResStr += LOG_CLOSE_STR;
strResStr += SPACE_SYMBOL;
strResStr += LOG_PATH_STR;
strResStr += m_ftFile.FileName();
return(_LogString(strResStr));
}
void CLogFile::_DebugComment(string strMessage)
{
#ifdef ASSERTION_CODE_ON
Comment(strMessage);
#endif // ASSERTION_CODE_ON
};
CLogFile::CLogFile(ELogMode lmMode,ETimestampMode tmTimeMode,bool bWriteToLogOpenCloseOfLogFile,bool bFlushEveryOperation,bool bCommon)
{
m_lmLogMode=lmMode;
m_bWriteToLogOpenClose = bWriteToLogOpenCloseOfLogFile;
m_bFlushEveryOperation = bFlushEveryOperation;
m_bCommon = bCommon;
SetTimestampMode(tmTimeMode);
};
CLogFile::~CLogFile()
{
Close();
};
void CLogFile::Flush()
{
if(m_ftFile.Handle() !=INVALID_HANDLE)
m_ftFile.Flush();
}
void CLogFile::Close()
{
if(m_ftFile.Handle() ==INVALID_HANDLE)
return;
_LogClose();
m_ftFile.Close();
}
void CLogFile::SetTimestampMode(ETimestampMode tmMode)
{
switch(tmMode)
{
case TM_TIMEDATE:
m_iTimeToStringMode=TIMEDATE_TIMEFORMAT_MODE;
return;
case TM_TIME:
m_iTimeToStringMode=TIME_TIMEFORMAT_MODE;
return;
case TM_DATE:
m_iTimeToStringMode=DATE_TIMEFORMAT_MODE;
return;
default:
//ASSERT(false);
case TM_NONE:
m_iTimeToStringMode=NULL;
}
};
bool CLogFile::Open(string sLogFileName)
{
//ASSERT(sLogFileName != NULL);
//ASSERT(INVALID_HANDLE == -1); // Данная проверка необходима, потому, что в разных местах указывается то одно, то другое значения.
// Сперва закроем файл, если он открыт.
if(m_ftFile.Handle()!=INVALID_HANDLE)
{
string strOpenedFilename = m_ftFile.FileName();
_LogClose();
m_ftFile.Close();
if(m_ftFile.Handle()!=INVALID_HANDLE)
{
_DebugComment("Был открыт файл " + strOpenedFilename + ", но его не удалось закрыть !");
Print("Был открыт файл " + strOpenedFilename + ", но его не удалось закрыть !");
return(false);
}
};
int iFlags;
if(m_bCommon)
iFlags = FILE_COMMON;
else
iFlags = 0;
// Удалим файл, если это необходимо.
if(m_lmLogMode == LM_OVERWRITE && FileIsExist(sLogFileName,iFlags))
{
if(FileDelete(sLogFileName,iFlags) != true)
{
_DebugComment("Файл " + sLogFileName + " не удалось удалить !");
Print("Файл " + sLogFileName + " не удалось удалить !");
return(false);
};
}
if(m_bCommon)
iFlags = FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ|FILE_COMMON;
else
iFlags = FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ;
if(m_ftFile.Open(sLogFileName,iFlags)==INVALID_HANDLE)
{
Print("Файл " + sLogFileName + " не удалось открыть !");
_DebugComment("Файл " + sLogFileName + " не удалось открыть !");
return(false);
};
_LogOpen();
return(true);
};
bool CLogFile::LogString(string strLogStr)
{
string strResStr;
if(m_iTimeToStringMode != NULL)
strResStr = TimeToString(TimeLocal(),m_iTimeToStringMode) + SPACE_SYMBOL;
strResStr += strLogStr;
bool bRes = _LogString(strResStr);
if(bRes == false)
{
_DebugComment("Ошибка CLogFile::LogString() !!! Не удалось записать строоку: " + strLogStr);
Print("Ошибка CLogFile::LogString() !!! Не удалось записать строоку" + strLogStr);
}
return(bRes);
};
bool CLogFile::LogEmptyString()
{
return(LogString(""));
};
bool CLogFile::LogString(string strLogStr1,string strLogStr2,string strSeparator)
{
string strBuffer;
StringConcatenate(strLogStr1,strSeparator,strLogStr2);
return(LogString(strBuffer));
};
void CLogFile::GlobalLogString(string strOutput,string strFilename="MainLog.log")
{
if(lfGMainLogFile.IsOpened() == false || lfGMainLogFile.GetName() != strFilename)
{
lfGMainLogFile.Close();
ResetLastError();
bool bRes = lfGMainLogFile.Open(strFilename);
if(bRes == false)
{
Print("Необходимо вывести в лог-файл строку: " + strOutput);
Print("Однако, логфайл " + strFilename + " не удалось открыть.");
Print("Код ошибки: " + IntegerToString(GetLastError()));
};
};
lfGMainLogFile.LogString(strOutput);
};
bool CLogFile::AddStringToLog(string strOutput,string strFilename,bool bClearContentsBeforeAdding)
{
ELogMode lmMode = LM_NONE;
if(bClearContentsBeforeAdding)
lmMode = LM_OVERWRITE;
else
lmMode = LM_APPEND;
CLogFile lfFile(lmMode);
if(lfFile.Open(strFilename) != true)
return(false);
bool bRes = lfFile.LogString(strOutput);
lfFile.Close();
return(bRes);
};
Оба файла - можно "сходу" использовать.
В зависимом файле GlobalConsts.h определены дефайны: MAIN_LOG_NAME, STRING_TERMINATOR и SPACE_SYMBOL.
Дефайн ASSERTION_CODE_ON используется в файлах поддержки DEBUG-RELEASE вариантов.
В сложных классах в .mqh файлах перед каждой public- функцией стоит описание, порой на десятки строк, поясняющее, как ее использовать. На мой взгляд, неразумно "размазывать" эти описания по всему файлу.
Я уж не говорю о том, что, скажем, размер файла .mq5 классa CExpertT (наследний CExpert из Стандартной библиотеки) у меня более 50 килобайт - в таком длинном файле непросто находить нужные функции даже с поиском.
Конечно, если загнать файл в VisualStudio - там, скорее всего, в браузере будет искать проще. Но, как я понимаю, в Visual Studio напрямую комилировать не выйдет, надо постоянно переключаться...
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Хотел кое-что выложить в codebase и возникли вопросы. Я записываю классы в MQL способом, показанным ниже. Кстати, запустил стилизатор, чтобы привести скобки к стилю K&R, он выдал какие-то гиганские отступы, так что оставлю свой стиль от MS, это несуществено. Итак вопросы:
И мои соображения. Исторически такое разделение пошло в Си, там, как вы знаете, объявления выносятся в отдельный .h файл. Вызвано было в первую очередь тем, что на слабых компах того времени делали раздельную компиляцию модулей, которые хранились на магнитных лентах. Процесс, понятно, не быстрый. И уже потом линкер собирал из .obj - файлов конечный код. То есть при изменении одного модуля не надо было 3 часа перекомпилировать весь проект, достаточно было только сделать сборку. И для этого линкеру нужны были .h файлы с объявлениями.
Второе, при передаче кода на сторону можно было передать скомпилированные .obj и .lib файлы, закрывая тем самым свои исходники.
Раздельную компиляцию MQL не поддерживает, нет даже возможности создать проект с несколькими .mq4/5 файлами. Выносить в отдельный заголовочник определения методов класса тоже вроде нельзя (или можно?). В общем было бы интересно услышать мотивацию от представителей MQ.
P.S Кстати, как вы знаете, в современных языках типа C# вообще отказались от заголовочных файлов ввиду ненужности и код пишется в нижеприведенном стиле.