Re[3]: Препроцессор и переменное число аргументов
От: Gleb Alexeev  
Дата: 22.08.05 08:24
Оценка: 3 (1)
Здравствуйте, Сергей, Вы писали:

С>Здравствуйте, WinterMute, Вы писали:


WM>>IMHO, лучше пользоватся потоками. Определи что-нибудь вроде такого:


WM>>
WM>>class LogOutput
WM>>{
WM>>   ...
WM>>};
WM>>LogOutput log_print;

WM>>log_print << "text: " << 5;
WM>>


С>Нет, это немного не то. Автор топика хочет, чтобы в дебажной версии были логи, а в релизной нет. Т.е. один дефайн/ундеф — и нету никакого логирования. Или тут тоже как-то можно?


Тут тоже макросами можно.

#ifdef DEBUG
#define TRACE(x) get_trace_stream()<<x
#else
define TRACE(x)
#endif
...
int n;
TRACE("n=" << n);
// в этом случае немного неудобно, но жить можно
TRACE(("someFunc returned "<<someFunc(1,2,3)));
Re[5]: Препроцессор и переменное число аргументов
От: Gleb Alexeev  
Дата: 22.08.05 08:31
Оценка:
Здравствуйте, Gleb Alexeev, Вы писали:

здесь более сложный пример того, что вы предлагали, но он не спасает от non-inline функций.
Re[4]: Препроцессор и переменное число аргументов
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 22.08.05 09:34
Оценка: 10 (3)
Здравствуйте, Gleb Alexeev, Вы писали:

С>>Нет, это немного не то. Автор топика хочет, чтобы в дебажной версии были логи, а в релизной нет. Т.е. один дефайн/ундеф — и нету никакого логирования. Или тут тоже как-то можно?


GA>Тут тоже макросами можно.


GA>
GA>#ifdef DEBUG
GA>#define TRACE(x) get_trace_stream()<<x
GA>#else
GA>define TRACE(x)
GA>#endif
GA>...
GA>int n;
GA>TRACE("n=" << n);
GA>// в этом случае немного неудобно, но жить можно
GA>TRACE(("someFunc returned "<<someFunc(1,2,3))); 
GA>


А можно и по другому макросами (ноги растут отсюда):
#ifdef DEBUG
#define TRACE() (false) ? std::cout : get_trace_stream()
#else
#define TRACE() (true) ? std::cout : std::cout
...
int n;
TRACE() << "n=" << n;

Последняя строка будет развернута либо в:
// Есть отладочная печать.
(false) ? std::cout : get_trace_stream() << "n=" << n;

либо в
(true) ? std::cout : std::cout << "n=" << n;

Соответственно, если DEBUG не определен, то хвост оператора ()?: вычислятся не будет.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[5]: Препроцессор и переменное число аргументов
От: Gleb Alexeev  
Дата: 22.08.05 09:59
Оценка:
Здравствуйте, eao197, Вы писали:

Отличная идея , только я бы std::cout на какой-нибудь null_stream заменил бы (в случае, когда трассировка отключена).
Re[6]: Препроцессор и переменное число аргументов
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 22.08.05 10:03
Оценка:
Здравствуйте, Gleb Alexeev, Вы писали:

GA>Отличная идея , только я бы std::cout на какой-нибудь null_stream заменил бы (в случае, когда трассировка отключена).


Нет смысла. Ведь никуда ничего не выводится. Эта запись в случае !DEBUG будет эквивалентна:
std::cout;


А std::cout нужен для того, чтобы тип выражения в обоих ветках ?: был одинаковый -- std::ostream & (к примеру, или конкретный тип, к которому принадлежит std::cout).
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[7]: Препроцессор и переменное число аргументов
От: Gleb Alexeev  
Дата: 22.08.05 10:06
Оценка:
Здравствуйте, eao197, Вы писали:

E>Здравствуйте, Gleb Alexeev, Вы писали:


GA>>Отличная идея , только я бы std::cout на какой-нибудь null_stream заменил бы (в случае, когда трассировка отключена).


E>Нет смысла. Ведь никуда ничего не выводится. Эта запись в случае !DEBUG будет эквивалентна:

E>
E>std::cout;
E>


E>А std::cout нужен для того, чтобы тип выражения в обоих ветках ?: был одинаковый -- std::ostream & (к примеру, или конкретный тип, к которому принадлежит std::cout).


Я через секунду это и сам понял, только удалить не успел . Поставьте мне минус, кто-нибудь, и сорри за шум.
Re[8]: Препроцессор и переменное число аргументов
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 22.08.05 10:10
Оценка: :)
Здравствуйте, Gleb Alexeev, Вы писали:

GA>>>Отличная идея , только я бы std::cout на какой-нибудь null_stream заменил бы (в случае, когда трассировка отключена).


E>>Нет смысла. Ведь никуда ничего не выводится. Эта запись в случае !DEBUG будет эквивалентна:

E>>
E>>std::cout;
E>>


E>>А std::cout нужен для того, чтобы тип выражения в обоих ветках ?: был одинаковый -- std::ostream & (к примеру, или конкретный тип, к которому принадлежит std::cout).


GA>Я через секунду это и сам понял, только удалить не успел . Поставьте мне минус, кто-нибудь, и сорри за шум.


Зачем же удалять?
Если что-то может быть истолковано не правильно, оно и будет истолковано неправильно ((С) кажется из законов Мерфи). Может еще у кого-то такой же вопрос возникнет, а тут сразу ответ готов.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[5]: Препроцессор и переменное число аргументов
От: Centaur Россия  
Дата: 22.08.05 14:20
Оценка:
Здравствуйте, Gleb Alexeev, Вы писали:

GA>Здравствуйте, Centaur, Вы писали:


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

GA>Не факт.
GA>Боюсь, оптимизатор может не справиться с таким случаем (для этого компилятору пришлось бы доказать, что ни одна из non_inline_functionX не имеет побочных эффектов; может быть, что глобальная оптимизация с этим и справится, но гарантии нет):
GA>
GA>null_stream() << non_inline_function1() + non_inline_function2()/non_inline_function3();

Мы всё ещё говорим о логировании, или уже перешли к минимизирующим поверхность при заданном объёме представителям семейства непарнокопытных в условиях отсутствия атмосферного давления? Какие неинлайновые функции, какие побочные эффекты? Да тут use case примерно следующей сложности:
logstream << "Hello world, we are flying at " << altitude << "m, "
  "the temperatures of engines are " << writeContainer(engineTemp, ", ") << "\n";
Никаких неинлайновых функций, большей частью строковые литералы и локальные переменные, изредка конструкторы временных объектов, реализующих ленивую запись в поток. То есть, если нас попросят записаться в std::ostream, то мы запишемся в std::ostream, а если нас (сделают вид, что) запишут в null_stream без нашего участия, то мы и не будем ничего делать.
template <typename InIter>
class WriteRange
{
private:
  InIter begin_, end_;
  const char* separator_;
public:
  explicit WriteRange(InIter begin, InIter end, const char* separator)
  : begin_(begin), end_(end), separator_(separator)
  {}

  friend std::ostream& operator<<(std::ostream& stream, const WriteRange& wr)
  {
    InIter i = wr.begin_;
    stream << *i;
    for (++i; i != wr.end_; ++i)
    {
      stream << separator << *i;
    }
    return stream;
  }
};

template <typename Container>
WriteRange<typename Container::const_iterator>
writeContainer(const Container& container, const char* separator)
{
  return WriteRange<typename Container::const_iterator>(container.begin(), container.end(), separator);
}
Re[6]: Препроцессор и переменное число аргументов
От: Gleb Alexeev  
Дата: 22.08.05 14:32
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Мы всё ещё говорим о логировании, или уже перешли к минимизирующим поверхность при заданном объёме представителям семейства непарнокопытных в условиях отсутствия атмосферного давления? Какие неинлайновые функции, какие побочные эффекты? Да тут use case примерно следующей сложности:

C>
C>logstream << "Hello world, we are flying at " << altitude << "m, "
C>  "the temperatures of engines are " << writeContainer(engineTemp, ", ") << "\n";
C>
Никаких неинлайновых функций, большей частью строковые литералы и локальные переменные, изредка конструкторы временных объектов, реализующих ленивую запись в поток. То есть, если нас попросят записаться в std::ostream, то мы запишемся в std::ostream, а если нас (сделают вид, что) запишут в null_stream без нашего участия, то мы и не будем ничего делать.




std::string lastErrorMsg() {
  // GetLastError(), FormatMessage и т.д. 
}
...
void foo() {
  trace_stream << lastErrorMsg();
}


Сферический конь, говоришь?
Соптимизирует, говоришь?

При желании можно придумать и более правдоподобный случай, когда тяжеловесный код будет выполняться впустую при отключенной трассировке.
Re[7]: Препроцессор и переменное число аргументов
От: Centaur Россия  
Дата: 22.08.05 15:11
Оценка:
Здравствуйте, Gleb Alexeev, Вы писали:

GA>
GA>std::string lastErrorMsg() {
GA>  // GetLastError(), FormatMessage и т.д. 
GA>}
GA>...
GA>void foo() {
GA>  trace_stream << lastErrorMsg();
GA>}
GA>


GA>Сферический конь, говоришь?

GA>Соптимизирует, говоришь?

Ну, во-первых, GetLastError у меня обычно вызывается сразу после возникновения ошибки, в конструкторе Win32Exception. Независимо от того, ведём мы логи или не ведём. А FormatMessage — соответственно в Win32Exception::what().

А во-вторых, даже в этом примере я попытаюсь максимально «ленивизировать» операцию — сделав lastErrorMsg классом и унеся всё тяжеловесное в его friend std::ostream& operator<<(std::ostream&, const lastErrorMsg&). В конструкторе останется один GetLastError(), и то только потому, что есть (минимальный) риск этот самый LastError прощёлкать клювом, успешно выполнив Win32API-операцию.

GA>При желании можно придумать и более правдоподобный случай, когда тяжеловесный код будет выполняться впустую при отключенной трассировке.


Пока не убеждён
Re[8]: Препроцессор и переменное число аргументов
От: Gleb Alexeev  
Дата: 22.08.05 15:32
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Ну, во-первых, GetLastError у меня обычно вызывается сразу после возникновения ошибки, в конструкторе Win32Exception. Независимо от того, ведём мы логи или не ведём. А FormatMessage — соответственно в Win32Exception::what().


C>А во-вторых, даже в этом примере я попытаюсь максимально «ленивизировать» операцию — сделав lastErrorMsg классом и унеся всё тяжеловесное в его friend std::ostream& operator<<(std::ostream&, const lastErrorMsg&). В конструкторе останется один GetLastError(), и то только потому, что есть (минимальный) риск этот самый LastError прощёлкать клювом, успешно выполнив Win32API-операцию.


С чего начали:
Замена log_stream на null_stream заставит оптимизатор полностью выкинуть код трассировки из приложения.

Чем закончили:
Я, нижеподписавшийся Centaur, лично никогда не вызываю не-inline функций при трассировке .

Удобство макросов типа ASSERT и TRACE в том, что их пишешь не задумываясь о стоимости, зная, что в релиз ничего из отладочного кода не попадет. Вы же призываете пользователя log_stream к дисциплине, ограничивая его удобство без особой на то причины (ниже ув. тов. eao197 дал более элегантное решение), тем более, что не-inline функция может не вызываться явно в выражении записи в поток.

C>Пока не убеждён

Ладно, другой пример:

namespace ThirdParty {
  class Something {
  public:
    std::string dump()const; //non-inline!
  };
}
...
void f(const ThirdParty::Something& smth) {
  log_stream << smth.dump();
  ...
}

Можно и еще более правдоподобно .
Re[3]: Препроцессор и переменное число аргументов
От: WinterMute Россия http://yarrr.ru
Дата: 22.08.05 16:22
Оценка:
WM>>
WM>>class LogOutput
WM>>{
WM>>   ...
WM>>   operator bool () const { return true; }
WM>>};
WM>>LogOutput log_print;

WM>>log_print << "text: " << 5;
WM>>


С>Нет, это немного не то. Автор топика хочет, чтобы в дебажной версии были логи, а в релизной нет. Т.е. один дефайн/ундеф — и нету никакого логирования. Или тут тоже как-то можно?

С>И под C не покатит, так что может оно и лучше, но не так универсально.

Можно, например, так :

class LogOutput
{
   ...
   operator bool () const { return true; }
};
LogOutput log_print;

ATLASSERT( log_print << "text: " << 5 );
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.