среда, 6 мая 2009 г.

Другие стороны boost::format

Библиотека boost::format предоставляет удобный и безопасный(по сравнению с printf) способ форматирования строк. Возможен как классический printf-стиль:
cout << format("writing %s,  x=%s : %d-th step \n") % "toto" % 40.23 % 50;
Так и другие, более гибкие способы форматирования, как, например, такой:
cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.
Самая очевидная проблема с printf — это то, что мы должны точно указать размер каждого аргумента. Например, для time_t в одних случаях писать %I64d, а в других %d. У time_t разный размер может быть и на 32-х битной платформе в зависимости от компилятора. И это только один пример. С boost::format такие проблемы исключаются.
Подробнее по всем возможностям можно посмотреть в документации. Но речь сейчас не о возможностях boost::format , а о том как их применить у себя в программе. Мне кажется, что очень удобно было бы, например, писать информацию в лог файл без предварительного форматирования строки на стеке. Т.е. писать вместо:
string msg = str( boost::format("Debug message %s") % some_msg_string );
log( msg );
вот такую строку:
log("Debug message %s") % some_msg_string;
Добиться этого не сложно. Нужно, всего лишь, перегрузить оператор %. Для этого создадим класс formatted_log_t. А ещё функцию log, которая возвращает экземпляр этого класса. Класс будет содержать в себе экземпляр boost::format.
#include <sstream>
#include <boost/format.hpp>
#include <iostream>
using namespace std;

class formatted_log_t {
public:
  formatted_log_t(const wchar_t* msg ) : fmt(msg) {}
  ~formatted_log_t() { wcout << fmt << endl; }

  template <typename T>
  formatted_log_t& operator %(T value) {
    fmt % value;
    return *this;
  }

protected:
  boost::wformat                fmt;
};
Оператор % должен быть шаблонным так как мы не знаем типы форматируемых аргументов. Функция log будет иметь следующий вид:
formatted_log_t log(const wchar_t* msg)
{
  return formatted_log_t( msg );
}
На этом месте реализация функции уже является вполне работоспособной. Далее можно добавлять какие-то другие фичи. Например, фильтр по уровню сообщений. Для этого сделаем функцию и класс шаблонными. Вот так:
#include <sstream>
#include <boost/format.hpp>
#include <iostream>
using namespace std;

enum log_level_t {
  LOG_NOTHING,
  LOG_CRITICAL,
  LOG_ERROR,
  LOG_WARNING,
  LOG_INFO,
  LOG_DEBUG
};

template<int level>
class formatted_log_t {
public:
  formatted_log_t(const wchar_t* msg ) : fmt(msg) {}
  ~formatted_log_t() {
  // GLOBAL_LEVEL is a global variable and could be changed at runtime
  if ( level <= GLOBAL_LEVEL ) wcout << fmt << endl;
}

template <typename T>
formatted_log_t& operator %(T value) {
  fmt % value;
  return *this;
}

protected:
  boost::wformat                fmt;
};

template <int level>
formatted_log_t<level> log(const wchar_t* msg)
{
  return formatted_log_t<level>( msg );
}
Применять этот код можно так:
int main ()
{
  log<LOG_DEBUG>(L"TEST %3% %2% %1%") % 5 % 10 % L"privet";
  return 0;
}
Работа с логом, наверное, самое очевидно, но только одно из возможных применений синтаксиса boost::format и далее можно развивать эту идею как вам больше нравится.

Комментировать в ВКонтакте