Ленивая оценка с ostream операторами C ++ - PullRequest
8 голосов
/ 18 февраля 2011

Я ищу портативный способ реализации отложенной оценки в C ++ для ведения журнала класса. Допустим, у меня есть простая функция регистрации, как

void syslog(int priority, const char *format, ...);

тогда в функции syslog () мы можем сделать:

if (priority < current_priority)
  return;

поэтому мы никогда не вызываем функцию форматирования (sprintf). С другой стороны, если мы используем поток журналирования, такой как

log << LOG_NOTICE << "test " << 123;

все форматирование всегда выполняется, что может занять много времени. Есть ли возможность использовать все полезные свойства ostream (например, пользовательский оператор << для классов, безопасность типов, элегантный синтаксис ...) таким образом, что форматирование выполняется ПОСЛЕ проверки уровня ведения журнала? </p>

Ответы [ 4 ]

4 голосов
/ 18 февраля 2011

Что я сделал в наших приложениях, так это вернул boost::iostreams::null_stream в случае, когда уровень ведения журнала фильтрует этот оператор. Это работает достаточно хорошо, но все равно вызовет все операторы <<. </p>

Если уровень журнала установлен во время компиляции, вы можете переключиться на объект с нулевым оператором <<. </p>

В остальном, это шаблоны выражений, как сказал Джерри.

4 голосов
/ 18 февраля 2011

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

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

3 голосов
/ 18 февраля 2011

Самый простой и простой способ - просто переместить чек за пределы форматирования:

MyLogger log;  // Probably a global variable or similar.

if (log.notice())
  log << "notified!\n" << some_function("which takes forever to compute"
    " and which it is impossible to elide if the check is inside log's"
    " op<< or similar");

if (log.warn()) {
  log << "warned!\n";
  T x;
  longer_code_computing(value_for, x);  // easily separate out this logic
  log << x;
}

Если вы действительно хотите сократить общий случай, вы можете использовать макрос:

#define LOG_NOTICE(logger) if (logger.notice()) logger <<

LOG_NOTICE(log) << "foo\n";
// instead of:
if (log.notice()) log << "foo\n";

Но экономия незначительна.

Один из возможных вариантов MyLogger:

struct MyLogger {
  int priority_threshold;

  bool notice()  const { return notice_priority  < current_priority; }
  bool warn()    const { return warn_priority    < current_priority; }
  bool etc()     const { return etc_priority     < current_priority; }

  template<class T>
  MyLogger& operator<<(T const &x) {
    do_something_with(x);
    return *this;
  }
};

Проблема здесь заключается в том, что смешивается перегрузка операторов в стиле iostream с функцией журналирования в стиле printf, в частностиперевод манипуляторов и форматирование флагов / полей из iostreams в строку формата.Вы можете написать в поток строк, а затем разделить это на части в своей функции системного журнала или попробовать что-нибудь более необычное.Вышеупомянутый MyLogger работает проще всего, если он также содержит ссылку ostream, на которую он может пересылать, но вам понадобится еще несколько оп << перегруженных версий для iomanips (например, endl), если вы сделаете это. </p>

2 голосов
/ 18 февраля 2011

Для себя я создал класс debug_ostream, который имеет шаблон << операторов. Эти операторы проверяют уровень отладки перед вызовом реального оператора. </p>

Вам нужно будет определить не шаблонные переопределения для const char* и std::ostream& (*x)(std::ostream&), потому что в противном случае они не будут работать. Я не уверен почему.

С встраиванием и достаточно высокими уровнями оптимизации компилятор превратит всю строку вывода в одну проверку уровня отладки вместо одного для каждого элемента вывода.

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

...