/* * osyncstream - A wrapper around ostream that properly locks while consuming stream operators * Copyright Riley Strickland 2021 */ #pragma once #include #include #include template class osyncstream_locked final { std::unique_lock lock_; std::basic_ostream& stream_; public: osyncstream_locked(std::recursive_mutex& mtx, std::basic_ostream& stream) : lock_(mtx), stream_(stream) {} //construct and lock the mutex from osyncstream. osyncstream_locked(const osyncstream_locked& o) = delete; /* A recursive mutex isn't great here (reference forwarding would be better except...only a recursive mutex can assure lock-before-release on ownership transfer */ template friend auto operator<<(osyncstream_locked&& os, const J& d) -> osyncstream_locked&& { os.stream_ << d; return std::move(os); /* move semantics :( ... thanks Clang/GCC */ } friend auto operator<<(osyncstream_locked&& os, std::basic_ostream& (*func)(std::basic_ostream&)) -> std::basic_ostream& { return os.stream_ << func; } osyncstream_locked(osyncstream_locked&& o) noexcept : osyncstream_locked(*o.lock_.mutex(), o.stream_) {} //could be better... max depth 2, usual depth 1. osyncstream_locked& operator=(osyncstream_locked&) = delete; //disallow osyncstream_locked& operator=(osyncstream_locked&&) = delete; //disallow ~osyncstream_locked() = default; }; template class osyncstream final { std::recursive_mutex mutex_{}; std::basic_ostream& stream_; public: explicit osyncstream(std::basic_ostream& stream) : stream_(stream) {} template friend auto operator<<(osyncstream& os, const J& d) -> osyncstream_locked { std::unique_lock lock(os.mutex_); os.stream_ << d; return osyncstream_locked { os.mutex_, os.stream_ }; } friend auto operator<<(osyncstream& os, std::basic_ostream& (*func)(std::basic_ostream&)) -> osyncstream_locked { std::unique_lock lock(os.mutex_); os.stream_ << func; return osyncstream_locked{ os.mutex_, os.stream_ }; } }; struct streams { static std::basic_ostream& cout; static std::basic_ostream& cerr; static std::basic_ostream& wcout; static std::basic_ostream& wcerr; }; inline std::basic_ostream& streams::cout = std::cout; inline std::basic_ostream& streams::cerr = std::cerr; inline std::basic_ostream& streams::wcout = std::wcout; inline std::basic_ostream& streams::wcerr = std::wcerr; [[maybe_unused]] inline osyncstream sscout(streams::cout); // NOLINT(cert-err58-cpp) [[maybe_unused]] inline osyncstream sscerr(streams::cerr); // NOLINT(cert-err58-cpp) [[maybe_unused]] inline osyncstream sswcout(streams::wcout); // NOLINT(cert-err58-cpp) [[maybe_unused]] inline osyncstream sswcerr(streams::wcerr); // NOLINT(cert-err58-cpp)