Войти
ПрограммированиеФорумОбщее

Логгер с автоматическими секциями

Страницы: 1 2 3 4 Следующая »
#0
6:41, 4 апр. 2016

Всем привет. Свелосипедил простой логгер, синтаксис использования такой:

  GetLogger() << "Program started\n";
  if (1)
  {
    GetLogger().SectionStart("if (1)");
    GetLogger() << "Starting loop now\n";
    for (int i = 0; i < 3; i++)
    {
      GetLogger().SectionStart("Iteration");
      GetLogger() << "Executing iteration " << i << "\n";
      GetLogger().SectionEnd();
    }
    GetLogger() << "Condition execution complete\n";
    GetLogger().SectionEnd();
  }
результат в консоли:
Изображение

Я уже очень давно им пользуюсь, он себя хорошо зарекомендовал, но хотелось бы обезопасить себя от забывания вызова Logger::SectionEnd(), используя автоматические секции. Написав несложный scope guard, можно сделать так:

class SectionGuard
{
public:
  SectionGuard(Logger *logger, std::string sectionName);
  {
    this->logger = logger;
    logger->SectionStart(sectionName);
  }
  ~SectionGuard();
  {
    logger->SectionEnd();
  }
};

SectionGuard Logger::AutoSection(std::string sectionName)
{
  return SectionGuard(this, sectionName);
}
И потом для достижения того же результата код использования можно упростить:
  GetLogger() << "Program started\n";
  if (1)
  {
    auto s0 = GetLogger().AutoSection("if (1)");
    //SectionGuard s0(GetLogger(), "if (1)"); //same
    GetLogger() << "Starting loop now\n";
    for (int i = 0; i < 3; i++)
    {
      auto s1 = GetLogger().AutoSection("Iteration");
      //SectionGuard s1(GetLogger(), "Iteration"); //same
      GetLogger() << "Executing iteration " << i << "\n";
    }
    GetLogger() << "Condition execution complete\n";
  }
Пара вопросов:
1) В этом коде я понадеялся на использование return value optimization и действительно при вызове AutoSection() конструктор SectionGuard'а вызывается ровно один раз и ровно один раз вызывается деструктор. Проверил в студии и gcc — везде работает. Можно ли 100% надеяться, что в таком коде гарантированно не будут создаваться лишние объекты SectionGuard при возвращении временной переменной из AutoSection()
2) Имеет ли право оптимизатор посчитать себя умнее всех и удалить переменные s0/s1, которые в явном виде нигде не используются?
3) Как можно избежать придумывания ненужных имён для каждого SectionGuard'а вроде s0/s1? Разумеется, желательно без макросов и __LINE__.

#1
6:55, 4 апр. 2016

Suslik
> Как можно избежать придумывания ненужных имён для каждого SectionGuard'а вроде
> s0/s1?

на вскидку: локальные имена переменных внутри циклов могут повторяться

for (auto s0 = GetLogger().AutoSection("one");;)
{
  //...
  break;
}

for (auto s0 = GetLogger().AutoSection("two");;)
{
  //...
  break;
}

Но выглядит страшно =)

#2
6:57, 4 апр. 2016

хотелось бы в идеале вообще избежать придумывания имён. хоть уникальных, хоть неуникальных.

#3
10:49, 4 апр. 2016

Suslik
> 1) В этом коде я понадеялся на использование return value optimization и
> действительно при вызове AutoSection() конструктор SectionGuard'а вызывается
> ровно один раз и ровно один раз вызывается деструктор. Проверил в студии и gcc
> — везде работает. Можно ли 100% надеяться, что в таком коде гарантированно не
> будут создаваться лишние объекты SectionGuard при возвращении временной
> переменной из AutoSection()
Можно запретить копирование и описать move-constructor/move-assign operator.

Например так:

SectionGuard(const SectionGuard&) = delete;
SectionGuard& operator=(const SectionGuard&) = delete;

SectionGuard(SectionGuard&& other)
{
    this->logger = other.logger;
    other.logger = nullptr;
}

SectionGuard& operator=(SectionGuard&& other)
{
    if (this != &other)
    {
        this->logger = other.logger;
        other.logger = nullptr;
    }

    return *this;
}

Ещё не забыть проверять указатель logger на nullptr там, где его используешь (в твоём случае в деструкторе).

Если у тебя MSVC++, то он всегда генерит тривиальные конструкторы / assign операторы, даже если ты описал кастомный конструктор. Поэтому нужно явно их запрещать.
В других компиляторах этого делать не нужно.

Suslik
> 2) Имеет ли право оптимизатор посчитать себя умнее всех и удалить переменные
> s0/s1, которые в явном виде нигде не используются?
Однозначно нет. Это RAII парадигма, таким образом работает большое кол-во объектов из std и boost.

Suslik
> 3) Как можно избежать придумывания ненужных имён для каждого SectionGuard'а
> вроде s0/s1? Разумеется, желательно без макросов и __LINE__.
Так у них же разные scope'ы, по идее можно использовать одно и то же имя.

#4
11:43, 4 апр. 2016

Alexander K
>move-assign operator.
Не нужен.

Suslik
> Как можно избежать придумывания ненужных имён для каждого SectionGuard'а вроде
> s0/s1? Разумеется, желательно без макросов и __LINE__.
Без макросов не обойтись.

Alexander K
> Так у них же разные scope'ы, по идее можно использовать одно и то же имя.
И получать варнинги от компилятора.

#5
14:34, 4 апр. 2016

Предлагаю вариант на рассмотрение:
http://ideone.com/MDu43o

#6
14:46, 4 апр. 2016

Suslik
Глянь реализацию https://github.com/philsquared/Catch, они там магию какую-то делали внутри... Но на макросах, да.

#7
18:01, 4 апр. 2016

Suslik
> 2) Имеет ли право оптимизатор посчитать себя умнее всех и удалить переменные
> s0/s1, которые в явном виде нигде не используются?
Нет, т.к. там есть сайд-эффекты в конструкторе.

#8
18:28, 4 апр. 2016

Che@ter
> Предлагаю вариант на рассмотрение:
-Eugene-
> Но на макросах, да.
не, ребят, с макросами не катит. с ними и так понятно. надеялся, что можно обойтись без препроцессора.

#9
18:43, 4 апр. 2016

Suslik
макрос был для того, чтобы не писать лишний раз конструкцию "start_block(x,[&]"

#10
19:13, 4 апр. 2016

Che@ter
> макрос был для того, чтобы не писать лишний раз конструкцию "start_block(x,[&]"
да, ну тут главное — никому не показывать код с

BLOCK("block")
{
}); //wtf?? :D
#11
19:37, 4 апр. 2016

Suslik

Я знаю, как избавиться от круглой скобочки в конце, но от точки с запятой не избавишься уже никак, видимо.

upd Хотя не, можно же с фором угореть. Только макросы останутся.
#12
19:46, 4 апр. 2016

что за не любовь к макросам)
https://ideone.com/tsToCZ

вариант Che@ter-а плох еще тем, что в блоке перестают работать return, break и тд, ведь они внутри лямбды оказываются

#13
19:51, 4 апр. 2016

Suslik

// Example program
#include <iostream>
#include <string>

class scope {
    private:
      int m_v;
   public:
     explicit scope(int v) : m_v(v) { std::cout << "-> " << m_v << std::endl; }
     ~scope() { std::cout << "-< " << m_v << std::endl; }
     scope& operator << (const char* str) const {
       std::cout << m_v << ":" << str << std::endl;
     }
};

int main()
{
  scope log(0);
  log << "a";
  {
      scope log(1);
      log << "b";
      for (int i=0;i<3;++i) {
        scope log(2);
        log << "c";
      }
      log << "d";
  }
  log << "e";
}
не написал скоп - ошибка, продублировал скоп - ошибка, написал скоп и не используешь - варнинг
#14
19:59, 4 апр. 2016

Хотя да, локальная переменная с уникальным именем, создаваемым из __LINE__, все завернутое в макрос, будет наверно наилучшим вариантом.

Страницы: 1 2 3 4 Следующая »
ПрограммированиеФорумОбщее

Тема в архиве.