вторник, 1 марта 2016 г.

Простой лог в области комментариев

Для вывода лога на экран в сложных случаях давно пользуюсь своим классом для вывода лога на экран XLog из библиотеки mql.xlib. Он, вроде бы, всем хорош, но в простых случаях мешает в нём одна вещь - для вывода лога на экран нужно подключить файл с классом, потом создать экземпляр и после работы удалить его. Хочется чего-то попроще.

В большинстве случаев логирование нужно только в процессе отладки, и из конечного кода оно должно убираться (обычно комментированием), поэтому сделаем так, чтобы лишнего кода было как можно меньше. Самый минимум - одна ссылка на файл с функциями логирования и по одной строке на каждый вывод лога, набрал Log("привет"), - получил привет.

Главная задача - вывести строку в комментарий и, если комментарий слишком большой (много строк), сдвинуть его вверх. Так как хочется максимально простой для использования код, то максимальную высоту будем вычислять автоматически.

    // определить максимальное количество срок (2/3 экрана)
    int chartHeight = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS) - 20; // 20 - примерный размер заголовка
    int lineHeight = 11; // вроде бы фиксированное для комментария
    int lineCount = chartHeight * 2 / 3 / lineHeight;
 
    if (lineCount < 1)
        lineCount = 1;

Почему 2/3 экрана? Это значение хорошо выглядит, не нравится - можно изменить на другое. Сверху окна есть заголовок с именем инструмента, его надо исключить из расчётов, он занимает примерно 20 пикселей. Функция быстрой торговли может добавлять ещё очень большую плашку, но её я здесь игнорирую, в отладке мне эта штука пока не нужна была, да и запаса трети экрана обычно хватает, чтобы всё влезало и с ней.

Дальше всё просто: берём текущий комментарий, разбиваем на строки, убираем лишние сверху, если их слишком много, добавляем свою, выводим на экран.

    string comment = ChartGetString(0, CHART_COMMENT);
 
    string lines[];
    StringSplit(comment, '\n', lines);
 
    ArrayRemove(lines, 0, ArraySize(lines) - lineCount + 1);
    ArrayAdd(lines, "[" + TimeToString(TimeLocal(), TIME_MINUTES | TIME_SECONDS) + "] " + (string)line, false, lineCount);
 
    Comment(ArrayToString(lines, "\n"));

ArrayAdd, ArrayRemove, ArrayToString взяты из той же mql.xlib (util\array.mqh).

Теперь возьмёмся за входные параметры. Обычно с Print("привет ещё раз") я не заморачиваюсь приведением типа параметра к строке, делаю просто Print(value), функция делает эта сама. Сделаем и мы.

template <typename T>
void Log(const T line) {}

Поэтому выше в ArrayAdd есть приведение типа (string)line.

Всё теперь работает, супер. Не хватает одной хорошей особенности принта - возможность принимать кучу параметров и складывать их в одну строку на выходе. Можно, конечно, просто ставить плюсики вместо запятых, но реализация запятых поможет быстро перевести принты в логи.

Кстати, все эти заморочки с логом в комментариях нужны лишь тем, кому не нравятся принты. Либо в тех случаях, когда информация в лог поступает с нескольких окон, что при отладке бывает часто (мой случай).

64 параметра, как у Print, сделать не получится, только 8. Если 8 мало, конкатенация строк вам в помощь (те самые плюсики). Это ограничение на количество typename в template. Каждому параметру нужен свой тип, иначе при смешанных типах входных параметров при одном лишь <typename T> в объявлении Log() компилятор будет ругаться на неявное приведение типов.

Вот перегрузка для двух переменных, остальные 6 (c добавлением T3, T4, и так до T8) делаем аналогично.

template <typename T1, typename T2>
void Log(const T1 part1, const T2 part2)
{
    Log((string)part1 + (string)part2);
}

На этом всё, можно пользоваться. Полный код файла ниже.

#include <fxcoder\xlib-v1\util\array.mqh>

template <typename T>
void Log(const T line)
{
    // определить максимальное количество срок (2/3 экрана)

    int chartHeight = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS) - 20; // 20 - примерный размер заголовка
    int lineHeight = 11; // вроде бы фиксированное для комментария
    int lineCount = chartHeight * 2 / 3 / lineHeight;
 
    if (lineCount < 1)
        lineCount = 1;

    string comment = ChartGetString(0, CHART_COMMENT);
 
    string lines[];
    StringSplit(comment, '\n', lines);
 
    ArrayRemove(lines, 0, ArraySize(lines) - lineCount + 1);
    ArrayAdd(lines, "[" + TimeToString(TimeLocal(), TIME_MINUTES | TIME_SECONDS) + "] " + (string)line, false, lineCount);
 
    Comment(ArrayToString(lines, "\n"));
}

template <typename T1, typename T2>
void Log(const T1 part1, const T2 part2)
{
    Log((string)part1 + (string)part2);
}

template <typename T1, typename T2, typename T3>
void Log(const T1 part1, const T2 part2, const T3 part3)
{
    Log((string)part1 + (string)part2 + (string)part3);
}

template <typename T1, typename T2, typename T3, typename T4>
void Log(const T1 part1, const T2 part2, const T3 part3, const T4 part4)
{
    Log((string)part1 + (string)part2 + (string)part3 + (string)part4);
}

template <typename T1, typename T2, typename T3, typename T4, typename T5>
void Log(const T1 part1, const T2 part2, const T3 part3, const T4 part4, const T5 part5)
{
    Log((string)part1 + (string)part2 + (string)part3 + (string)part4 + (string)part5);
}

template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
void Log(const T1 part1, const T2 part2, const T3 part3, const T4 part4, const T5 part5, const T6 part6)
{
    Log((string)part1 + (string)part2 + (string)part3 + (string)part4 + (string)part5 + (string)part6);
}

template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
void Log(const T1 part1, const T2 part2, const T3 part3, const T4 part4, const T5 part5, const T6 part6, const T7 part7)
{
    Log((string)part1 + (string)part2 + (string)part3 + (string)part4 + (string)part5 + (string)part6 + (string)part7);
}

template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
void Log(const T1 part1, const T2 part2, const T3 part3, const T4 part4, const T5 part5, const T6 part6, const T7 part7, const T8 part8)
{
    Log((string)part1 + (string)part2 + (string)part3 + (string)part4 + (string)part5 + (string)part6 + (string)part7 + (string)part8);
}


Некоторым дальнейшим улучшениям мешают ограничения MQL, другим - нежелание всё слишком раздувать.

Комментариев нет:

Отправить комментарий