C++11 Добавление оператора вывода потока для std::chrono::time_point

Я хотел бы иметь возможность сделать следующее:

std::cerr << std::chrono::system_clock::now() << std::endl;

И получить следующее:

Wed May  1 11:11:12 2013

Поэтому я написал следующее:

template<typename Clock, typename Duration>
std::ostream &operator<<(std::ostream &stream,
  const std::chrono::time_point<Clock, Duration> &time_point) {
  const time_t time = Clock::to_time_t(time_point);
#if __GNUC__ > 4 || \
    ((__GNUC__ == 4) && __GNUC_MINOR__ > 8 && __GNUC_REVISION__ > 1)
  // Maybe the put_time will be implemented later?
  struct tm tm;
  localtime_r(&time, &tm);
  return stream << std::put_time(tm, "%c");
#else
  char buffer[26];
  ctime_r(&time, buffer);
  buffer[24] = '\0';  // Removes the newline that is added
  return stream << buffer;
#endif
}

Что работает, но я продолжаю получать проблемы при вызове этого из разных пространств имен. Правильно ли, что это должно быть просто в глобальном пространстве имен?


person Matt Clarkson    schedule 22.05.2013    source источник
comment
В std нет system_clock. Это в std::chrono.   -  person Stephan Dollberg    schedule 22.05.2013
comment
Извините, обновлено с правильным исправлением.   -  person Matt Clarkson    schedule 22.05.2013
comment
Обратите внимание, что вы делаете что-то немного невежливое, добавляя переопределенный оператор к двум типам, которыми вы не владеете. Более того, следующая итерация стандарта может написать свою собственную перегрузку <<.   -  person Yakk - Adam Nevraumont    schedule 22.05.2013


Ответы (2)


Если вы хотите быть уверены, что вызывается правильная функция, вы должны поместить объявление using в область действия кода, который ее вызовет.

Например:

namespace pretty_time {
  /* your operator<< lives here */
}


void do_stuff() {
  using namespace pretty_time;   // One way to go is this line
  using pretty_time::operator<<; // alternative that is more specific (just use one of these two lines, but not both)
  std::cout << std::chrono::system_clock::now();
}
person Iron Savior    schedule 22.05.2013
comment
Если я помещу оператор в глобальное пространство имен, он всегда будет найден? Это кажется более безопасным и разумным способом сделать это. Спасибо. - person Matt Clarkson; 22.05.2013
comment
Честно говоря, чем больше кода на C++ я пишу, тем менее полезным я нахожу глобальное пространство имен. Я предпочитаю вторую форму using pretty_time::operator<<, потому что она не оставляет двусмысленности для читателя. Если бы я хотел сделать доступными несколько имен из одного и того же пространства имен и был уверен, что никаких побочных эффектов не возникнет, я был бы не против использования первой формы, которая импортирует все пространство имен, но я всегда хотел бы оставить комментарий, который указывает, какие вещи я намерен использовать из этого пространства имен. Как 2_ - person Iron Savior; 22.05.2013
comment
Я согласен. На самом деле я не думал о том, чтобы квалифицировать оператор потока с помощью объявления using. Кто-то еще может захотеть вывести время в другом формате и будет конфликтовать с моим глобальным оператором потока. - person Matt Clarkson; 22.05.2013
comment
Кроме того, если у вас есть тип класса/структуры, который находится в пространстве имен, этому пространству имен предоставляется более высокий приоритет при разрешении неполных имен функций. Пример можно увидеть в одном из моих любимых проектов, который имеет тип, представляющий IP-адрес, и предоставляет удобочитаемый operator<<. Пространство имен IP заголовок, реализация и код клиента. Обратите внимание на отсутствие using. - person Iron Savior; 22.05.2013
comment
Я не использую объявление using, потому что функция находится в том же пространстве имен, что и пользовательский тип, представляющий IP-адрес. - person Iron Savior; 22.05.2013

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

std::cerr << pretty_print::format(std::system_clock::now()) << std::endl;

Следующим образом:

namespace pretty_print {
  template<typename T>
  struct print_wrapper { // boost::noopy optional -- if so, use it with && as an argument
    T const& data;
    print_wrapper( T const& t ): data(t) {}
  };
  template<typename T>
  print_wrapper<T> format( T const& t ) {
    return {t};
  }
  template<typename Clock, typename Duration>
  std::ostream &operator<<(std::ostream &stream,
    print_wrapper<std::chrono::time_point<Clock, Duration>>&& time_point)
  {
    // ...
  }
}

и получить доступ к time_point.data, чтобы получить необработанные данные в вашей перегрузке <<.

Оператор << будет найден через ADL (поиск, зависящий от аргумента), когда вы используете обернутый тип print_wrapper<>, даже не втягивая его в namespace, где вы его используете! Чтобы использовать это, вы можете использовать pretty_print::format(blah) или using pretty_print::format, чтобы вытащить format в текущую область.

По сути, вы пометили тип T для использования в своем собственном наборе перегрузок. Мне нравится эта техника "тонких типизированных оберток", потому что она напоминает мне std::move.

Это также позволяет вам сказать: «Я ненавижу то, как форматируются double», и ввести <<, который лучше их форматирует и принимает print_wrapper<double>.

В качестве дополнительного преимущества вы можете специализировать/перегружать print_wrapper и format, чтобы принимать аргументы форматирования, так что вы можете pretty_print::format( std::system_clock::now(), pretty_print::eDate::YMD ) или pretty_print::eFmt::compact.

person Yakk - Adam Nevraumont    schedule 22.05.2013