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
Няма system_clock в std. Това е в 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)


Когато искате да сте сигурни, че ще бъде извикана правилната функция, трябва да поставите декларация put 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<<, защото не оставя неяснота за читателя. Ако исках да направя достъпни няколко имена от едно и също пространство от имена и бях сигурен, че няма да има странични ефекти, нямаше да се противопоставя на използването на първата форма, която импортира цялото пространство от имена, но винаги искам да оставя коментар, който посочва кои неща възнамерявам да използвам от това пространство от имена. като using namespace pretty_time; // for operator<< - person Iron Savior; 22.05.2013
comment
Съгласен съм. Наистина не мислех за квалифицирането на оператора на потока с декларация за използване. Някой друг може да иска да има изход за времето в друг формат и ще влезе в конфликт с моя глобален оператор на поток. - person Matt Clarkson; 22.05.2013
comment
Освен това, ако имате тип клас/структура, който живее в пространство от имена, на това пространство от имена се дава по-висок приоритет при разрешаване на неквалифицирани имена на функции. Пример може да се види в един от моите любими проекти, който има тип, представляващ IP адрес и предоставя четим от човека operator<<. IP пространството от имена заглавка, внедряване и клиентски код. Обърнете внимание на липсата на using. - person Iron Savior; 22.05.2013
comment
Измъквам се без да използвам декларация за използване, защото функцията живее в същото пространство от имена като потребителския тип, който представлява 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.

Това също ви позволява да кажете „Мразя как се форматират doubles“ и да въведете <<, който ги форматира по-добре от 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