2021-02-09 23:02:05 -05:00
/*
* osyncstream - A wrapper around ostream that properly locks while consuming stream operators
* Copyright Riley Strickland 2021
*/
# pragma once
# include <mutex>
# include <ostream>
# include <iostream>
2021-02-20 00:44:06 -05:00
template < typename T >
2021-02-09 23:02:05 -05:00
class osyncstream_locked final
{
std : : unique_lock < std : : recursive_mutex > lock_ ;
2021-02-20 00:44:06 -05:00
std : : basic_ostream < T > & stream_ ;
2021-02-09 23:02:05 -05:00
public :
2021-02-20 00:44:06 -05:00
osyncstream_locked ( std : : recursive_mutex & mtx , std : : basic_ostream < T > & stream ) : lock_ ( mtx ) , stream_ ( stream ) { } //construct and lock the mutex from osyncstream.
2021-02-09 23:02:05 -05:00
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 */
2021-02-20 00:44:06 -05:00
template < typename J >
friend auto operator < < ( osyncstream_locked & & os , const J & d ) - > osyncstream_locked & &
2021-02-09 23:02:05 -05:00
{
os . stream_ < < d ;
return std : : move ( os ) ; /* move semantics :( ... thanks Clang/GCC */
}
2021-02-20 00:44:06 -05:00
friend auto operator < < ( osyncstream_locked & & os , std : : basic_ostream < T > & ( * func ) ( std : : basic_ostream < T > & ) ) - > std : : basic_ostream < T > &
2021-02-09 23:02:05 -05:00
{
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 ;
} ;
2021-02-20 00:44:06 -05:00
template < typename T >
2021-02-09 23:02:05 -05:00
class osyncstream final
{
std : : recursive_mutex mutex_ { } ;
2021-02-20 00:44:06 -05:00
std : : basic_ostream < T > & stream_ ;
2021-02-09 23:02:05 -05:00
public :
2021-02-20 00:44:06 -05:00
explicit osyncstream ( std : : basic_ostream < T > & stream ) : stream_ ( stream ) { }
template < typename J >
friend auto operator < < ( osyncstream & os , const J & d ) - > osyncstream_locked < T >
2021-02-09 23:02:05 -05:00
{
std : : unique_lock < std : : recursive_mutex > lock ( os . mutex_ ) ;
os . stream_ < < d ;
2021-02-20 00:44:06 -05:00
return osyncstream_locked < T > { os . mutex_ , os . stream_ } ;
2021-02-09 23:02:05 -05:00
}
2021-02-20 00:44:06 -05:00
friend auto operator < < ( osyncstream & os , std : : basic_ostream < T > & ( * func ) ( std : : basic_ostream < T > & ) ) - > osyncstream_locked < T >
2021-02-09 23:02:05 -05:00
{
std : : unique_lock < std : : recursive_mutex > lock ( os . mutex_ ) ;
os . stream_ < < func ;
return osyncstream_locked { os . mutex_ , os . stream_ } ;
}
} ;
struct streams
{
2021-02-20 00:44:06 -05:00
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 ;
2021-02-09 23:02:05 -05:00
} ;
2021-02-20 00:44:06 -05:00
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 ;
2021-02-11 02:40:29 -05:00
[ [ maybe_unused ] ] inline osyncstream sscout ( streams : : cout ) ; // NOLINT(cert-err58-cpp)
2021-02-20 00:44:06 -05:00
[ [ 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)