4
\$\begingroup\$

I've always wondered what's the most elegant way of implementing PrintLn in C++. I have not yet come with perfect conclusion. This is my shot.

Shortcut for std::cout, std::cerr and std::clog "print line" version. So you can type CoutLn << "Hello world!"; instead of std::cout << "Hello world!" << '\n';

CoutLn, CerrLn, ClogLn are implemented.

#include <iostream>
#include <typeinfo>
#include <mutex>
/* Template magic: determine if our type is printable. */
template<typename S, typename T, typename = void>
struct is_to_stream_writable : std::false_type {};
template<typename S, typename T>
struct is_to_stream_writable<S, T, std::void_t<decltype(std::declval<S&>()<<std::declval<T>())>> : std::true_type {};
/* Since std::cout, std::cerr etc. are all different instances of the same type, 
 * let's define dummy classes for detecting different instances. */
class Cout{};
class Cerr{};
class Clog{};
template<class Stream = Cout>
class PrintLn {
 private:
 static std::ostream* stream;
 static std::mutex m;
 public:
 PrintLn() {
 if constexpr (std::is_same<Stream, Cout>::value) stream = &std::cout;
 else if constexpr (std::is_same<Stream, Cerr>::value) stream = &std::cerr;
 else if constexpr (std::is_same<Stream, Clog>::value) stream = &std::clog;
 }
 template<class T>
 PrintLn& operator<<(const T& msg) {
 static_assert(is_to_stream_writable<std::ostream, T>::value, "your type is not printable");
 std::lock_guard<std::mutex>(PrintLn<Stream>::m);
 *stream << msg;
 return *this;
 }
 ~PrintLn() {
 std::lock_guard<std::mutex>(PrintLn<Stream>::m);
 *stream << '\n';
 }
};
/* Declare static variables. */
template<class Stream>
typename::std::mutex PrintLn<Stream>::m;
template<class Stream>
typename::std::ostream* PrintLn<Stream>::stream;
using CoutLn = PrintLn<Cout>;
using CerrLn = PrintLn<Cerr>;
using ClogLn = PrintLn<Clog>;
/* Shorter syntax, for example: `CerrLn{} << "error";` -> `CerrLn << "error";`. */
#define CoutLn CoutLn{}
#define CerrLn CerrLn{}
#define ClogLn ClogLn{}
asked Apr 2, 2020 at 8:07
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Have you looked at C++20 osyncstream? It seems to have a better interface.

Multithreading

std::lock_guard<std::mutex>(PrintLn<Stream>::m);

This line is useless, because temporary objects are destroyed at the end of the full-expression. (Does this even have a temporary in C++17?) You need a named variable instead. Also make use of class template argument deduction:

std::lock_guard lock{m};

Also,

Streams

Your approach is unnecessarily restricted because only three hardcoded streams std::cout, std::cerr, and std::clog are supported. And I think {} is OK and these don't really help a lot:

#define CoutLn CoutLn{}
#define CerrLn CerrLn{}
#define ClogLn ClogLn{}

Make the function object have regular semantics instead. You may use a hash map internally to store the mutexes, as syncbuf does.

Also SFINAE on operator<<.


Here's the same thing implemented with osyncstream:

template <
 class CharT,
 class Traits = std::char_traits<CharT>,
 class Allocator = std::allocator<CharT>
> class PrintLn : public std::basic_osyncstream<CharT, Traits, Allocator> {
 using Base = std::basic_osyncstream<CharT, Traits, Allocator>;
public:
 using Base::Base;
 PrintLn(PrintLn&&) = default;
 PrintLn& operator=(PrintLn&&) = default;
 ~PrintLn()
 {
 if (this->get_wrapped()) {
 *static_cast<Base*>(this) << '\n';
 }
 }
};
inline auto cout_ln()
{
 return PrintLn{std::cout};
}
inline auto cerr_ln()
{
 return PrintLn{std::cerr};
}
inline auto clog_ln()
{
 return PrintLn{std::clog};
}

But anyway, why would you wanna do this when you can simply print a \n?

answered Apr 2, 2020 at 11:55
\$\endgroup\$
1
  • \$\begingroup\$ I wouldn't recommend C++20 features until most major compilers (G++, Clang++, MSVC++) support them. Even then, it might not hurt to wait a couple of years so those compilers that support them get to enough platforms. \$\endgroup\$ Commented May 2, 2020 at 17:49

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.