72 lines
3.1 KiB
C++
Executable File
72 lines
3.1 KiB
C++
Executable File
/*
|
|
* osyncstream - A wrapper around ostream that properly locks while consuming stream operators
|
|
* Copyright Riley Strickland 2021
|
|
*/
|
|
#pragma once
|
|
#include <mutex>
|
|
#include <ostream>
|
|
#include <iostream>
|
|
|
|
template<typename T>
|
|
class osyncstream_locked final
|
|
{
|
|
std::unique_lock<std::recursive_mutex> lock_;
|
|
std::basic_ostream<T>& stream_;
|
|
public:
|
|
osyncstream_locked(std::recursive_mutex& mtx, std::basic_ostream<T>& 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<typename J>
|
|
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<T>& (*func)(std::basic_ostream<T>&)) -> std::basic_ostream<T>&
|
|
{
|
|
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<typename T>
|
|
class osyncstream final
|
|
{
|
|
std::recursive_mutex mutex_{};
|
|
std::basic_ostream<T>& stream_;
|
|
public:
|
|
explicit osyncstream(std::basic_ostream<T>& stream) : stream_(stream) {}
|
|
template<typename J>
|
|
friend auto operator<<(osyncstream& os, const J& d) -> osyncstream_locked<T>
|
|
{
|
|
std::unique_lock<std::recursive_mutex> lock(os.mutex_);
|
|
os.stream_ << d;
|
|
return osyncstream_locked<T> { os.mutex_, os.stream_ };
|
|
}
|
|
friend auto operator<<(osyncstream& os, std::basic_ostream<T>& (*func)(std::basic_ostream<T>&)) -> osyncstream_locked<T>
|
|
{
|
|
std::unique_lock<std::recursive_mutex> lock(os.mutex_);
|
|
os.stream_ << func;
|
|
return osyncstream_locked{ os.mutex_, os.stream_ };
|
|
}
|
|
};
|
|
|
|
struct streams
|
|
{
|
|
static std::basic_ostream<char>& cout;
|
|
static std::basic_ostream<char>& cerr;
|
|
static std::basic_ostream<wchar_t>& wcout;
|
|
static std::basic_ostream<wchar_t>& wcerr;
|
|
};
|
|
|
|
inline std::basic_ostream<char>& streams::cout = std::cout;
|
|
inline std::basic_ostream<char>& streams::cerr = std::cerr;
|
|
inline std::basic_ostream<wchar_t>& streams::wcout = std::wcout;
|
|
inline std::basic_ostream<wchar_t>& 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)
|