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 double
s 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
.