C++11 Adding a stream output operator for std::chr

2019-05-16 21:05发布

I would like to be able to do the following:

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

And get the following:

Wed May  1 11:11:12 2013

So I wrote the following:

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
}

Which works, but I keep getting issues when calling this from different namespaces. Is it correct that this should just be in the global namespace?

2条回答
仙女界的扛把子
2楼-- · 2019-05-16 21:48

One way to keep your mess in your own namespace, and avoid the somewhat impolite thing of overloading an operator on two types neither of which you own, would be to make your output syntax slightly more verbose:

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

As follows:

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)
  {
    // ...
  }
}

and access time_point.data to get at the raw data within your << overload.

The << operator will be found via ADL (argument dependent lookup) when you use a print_wrapper<> wrapped type, even without pulling it into the namespace where you use it! To use this, you can either use pretty_print::format(blah) or you could using pretty_print::format to pull format into the current scope.

In effect, you have flagged the type T for use in your own custom set of overloads. I like this technique of "thin typed wrappers" because it reminds me of std::move.

This also lets you say "I hate how doubles are formatted", and introduce a << that formats them better that takes a print_wrapper<double>.

As a side benefit, you can specialize/overload print_wrapper and format to take formatting arguments -- so you could pretty_print::format( std::system_clock::now(), pretty_print::eDate::YMD ), or pretty_print::eFmt::compact.

查看更多
走好不送
3楼-- · 2019-05-16 22:03

When you want to be sure that the right function gets called, you should put put a using declaration in the scope of the code that will call it.

For example:

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();
}
查看更多
登录 后发表回答