Created
April 10, 2016 20:19
-
-
Save apriori/02d09da40bb454078d82b7e669d2f07a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef LOCKEDSTREAM_H | |
#define LOCKEDSTREAM_H | |
/** | |
* Tuplegatherer and a LockedStream class as type safe | |
* wrappers over an ostream with a convenient API | |
* | |
*/ | |
#include <iostream> | |
#include <functional> | |
#include <tuple> | |
#include <pthread.h> | |
/** | |
* @brief A dumb termination marker class | |
*/ | |
class FinishMark {}; | |
static FinishMark finish; | |
/** | |
* Forward declaration | |
*/ | |
template<std::ostream* T> | |
class LockedStream; | |
/** | |
* We do need an assymetric type converter | |
* which does convert T -> std::decay<T> for all but std::string. | |
* std::string is to be expanded with c_str() and then treated as | |
* const char*. | |
*/ | |
template<typename T> | |
struct decayStr { | |
typedef typename std::decay<T>::type type; | |
}; | |
/** | |
* Template specialization for std::string. type is now const char* | |
*/ | |
template<> | |
struct decayStr<std::string> { | |
typedef const char* type; | |
}; | |
template<typename T> | |
/** | |
* Depending on an input type T, use std::forward. | |
* If T == std::string, use c_str(). | |
* @brief forwardStr a type converter for decayed types | |
* @param v | |
* @return | |
*/ | |
typename decayStr<const T*>::type forwardStr(const T* v) { | |
typedef typename decayStr<const T*>::type T1; | |
return static_cast<T1>(v); | |
} | |
template<typename T> | |
/** | |
* Depending on an input type T, use std::forward. | |
* If T == std::string, use c_str(). | |
* @brief forwardStr a type converter for decayed types | |
* @param v | |
* @return | |
*/ | |
typename decayStr<T>::type forwardStr(T& v) { | |
typedef typename decayStr<T>::type T1; | |
//return std::forward(v); | |
return static_cast<T1>(v); | |
} | |
template<typename T> | |
/** | |
* @brief forwardStr a type converter for decayed types, move variant | |
* @param v | |
* @return | |
*/ | |
typename decayStr<T>::type forwardStr(T&& v) { | |
return std::forward(v); | |
} | |
/** | |
* @brief forwardStr a type converter for a decayed std::string, move variant | |
* @return just c_str() | |
*/ | |
template<> | |
typename decayStr<std::string>::type forwardStr<std::string>(std::string&); | |
/** | |
* @brief forwardStr a type converter for a decayed std::string, move variant | |
* @return just c_str() | |
*/ | |
template<> | |
typename decayStr<std::string>::type forwardStr<std::string>(std::string&&); | |
template<std::ostream* stream, typename... K> | |
/** | |
* @brief The TupleGatherer, a class meant to gather | |
* a chain of operator<< calls into a tuple, which is then | |
* commited to the stream in a locked way. | |
*/ | |
class TupleGatherer { | |
public: | |
/** | |
* @brief TupleGatherer constructor which takes the current tuple | |
* @param tuple | |
*/ | |
TupleGatherer(std::tuple<K...> tuple) : tuple(tuple) {} | |
/** | |
* Handle the dumb termination marker and start commiting | |
* the tuple. | |
* @note This function will still return a TupleGatherer | |
* with the FinishMark added to the tuple. This TupleGatherer | |
* has no use. | |
* @brief operator << | |
* @return | |
*/ | |
TupleGatherer<stream, K..., FinishMark> operator<<(FinishMark&); | |
template<typename V> | |
/** | |
* Add a reference of type V to the internal tuple | |
* @brief operator <<, similar to ostream::operator<< | |
* @param v | |
* @return a new TupleGatherer with an extended internal tuple by v | |
*/ | |
TupleGatherer<stream, K..., typename decayStr<V>::type> operator<<(V& v); | |
template<typename V> | |
/** | |
* Add a move reference of type V to the internal tuple | |
* @brief operator <<, similar to ostream::operator<< | |
* @param v | |
* @return a new TupleGatherer with an extended internal tuple by v | |
*/ | |
TupleGatherer<stream, K..., typename decayStr<V>::type> operator<<(V&& v); | |
private: | |
template<std::size_t I = 0, typename...Ts> | |
/** | |
* Recursive iterating template over a tuple, which commits | |
* every single entry of the tuple to the stream. This is | |
* the termination case (I == sizeof...(Ts)). | |
* @brief commitTuple | |
* @return | |
*/ | |
inline typename std::enable_if<I == sizeof...(Ts), void>::type | |
commitTuple(std::tuple<Ts...>&) {} | |
template<std::size_t I = 0, typename...Ts> | |
/** | |
* Recursive iterating template over a tuple, which commits | |
* every single entry of the tuple to the stream. This is | |
* the iterating case (I < sizeof...(Ts)), which recursively unrolls | |
* with template parameter I until every tuple entry is commited to | |
* the stream. | |
* @brief commitTuple | |
* @return | |
*/ | |
inline typename std::enable_if<I < sizeof...(Ts), void>::type | |
commitTuple(std::tuple<Ts...>& tuple); | |
/** | |
* the managed current tuple | |
* @brief tuple | |
*/ | |
std::tuple<K...> tuple; | |
}; | |
template<std::ostream* stream, typename... K> | |
TupleGatherer<stream, K..., FinishMark> TupleGatherer<stream, K...>::operator<<(FinishMark&) { | |
//lock the stream first | |
LockedStream<stream>::lock(); | |
commitTuple(tuple); | |
//always add a line break | |
*stream << std::endl; | |
//force flush to make sure we don't get mixed results by the different threads | |
std::flush(*stream); | |
LockedStream<stream>::unlock(); | |
//return new TupleGatherer, which is only meant as a dummy in this case. | |
auto newTuple = std::tuple_cat(tuple, std::make_tuple(finish)); | |
return TupleGatherer<stream, K..., FinishMark>(newTuple); | |
} | |
template<std::ostream* stream, typename... K> | |
template<typename V> | |
TupleGatherer<stream, K..., typename decayStr<V>::type> TupleGatherer<stream, K...>::operator<<(V& v) { | |
//typedef to satisfy the interface | |
typedef typename decayStr<V>::type V1; | |
//notice that the call to forwardStr is to be infered by type V (and not V1) | |
auto newTuple = std::tuple_cat(tuple, std::make_tuple(forwardStr(v))); | |
return TupleGatherer<stream, K..., V1>(newTuple); | |
} | |
template<std::ostream* stream, typename... K> | |
template<typename V> | |
TupleGatherer<stream, K..., typename decayStr<V>::type> TupleGatherer<stream, K...>::operator<<(V&& v) { | |
typedef typename decayStr<V>::type V1; | |
auto newTuple = std::tuple_cat(tuple, std::make_tuple(forwardStr(v))); | |
return TupleGatherer<stream, K..., V1>(newTuple); | |
} | |
template<std::ostream* stream, typename... K> | |
template<std::size_t I, typename...Ts> | |
inline typename std::enable_if<I < sizeof...(Ts), void>::type | |
TupleGatherer<stream, K...>::commitTuple(std::tuple<Ts...>& tuple) { | |
//get current entry | |
*stream << std::get<I>(tuple); | |
//iterate forward | |
commitTuple<I+1, Ts...>(tuple); | |
} | |
template<std::ostream* stream> | |
/** | |
* A LockedStream singleton, to be instanced over an ostream pointer (e.g. &std::cout). | |
* Sample usage: | |
* @code | |
* int value = 1 | |
* LOCKEDCOUT() << "This is a test " << value << finish; | |
* @note The used finish mark in the example is mandatory and will commit the values to the stream. | |
* @see Aliases CoutRecursiveLocked, CerrRecursiveLocked and static std::functions | |
* LOCKEDCOUT and LOCKEDCERR | |
* | |
* @brief The LockedStream class | |
*/ | |
class LockedStream { | |
/** | |
* We allow TupleGatherer to call private functions so it can use locking, which is never | |
* meant to be used externally. | |
*/ | |
template<std::ostream*, typename... K> | |
friend class TupleGatherer; | |
public: | |
LockedStream(LockedStream<stream>&) = delete; | |
LockedStream(LockedStream<stream>&&) = delete; | |
LockedStream(const LockedStream<stream>&&) = delete; | |
LockedStream(const LockedStream<stream>&) = delete; | |
LockedStream<stream>& operator=(LockedStream<stream>&) = delete; | |
LockedStream<stream>& operator=(const LockedStream<stream>&) = delete; | |
LockedStream<stream>& operator=(const LockedStream<stream>&&) = delete; | |
/** | |
* Static getInstance method that returns a reference to the initiated singleton. | |
* If the singleton is not initiated, this function will do this is a mutex locked way. | |
* @brief getInstance Returns reference to current LockedStream instance | |
* @return | |
*/ | |
static LockedStream<stream>& getInstance(); | |
/** | |
* A clean up function meant for deterministic clean up. | |
* This is meant to be called by a single process (e.g. main thread) only. | |
* @brief clean | |
*/ | |
static void clean(); | |
~LockedStream(); | |
/** | |
* Handle the dummy finish mark immediately. | |
* Using this after the first call to operator<< is effectively resulting in a | |
* NOOP commit to ostream T. | |
* @brief operator << | |
* @return | |
*/ | |
TupleGatherer<stream, FinishMark> operator<<(FinishMark&); | |
template<typename V> | |
/** | |
* Handle a new value reference and create the first TupleGatherer instance. | |
* @brief operator << Create TupleGatherer which holds the initial value v | |
* @param v | |
* @return TupleGatherer with initial value v; | |
*/ | |
TupleGatherer<stream, typename decayStr<V>::type> operator<<(V& v); | |
template<typename V> | |
/** | |
* Handle a new value reference and create the first TupleGatherer instance. (move variant) | |
* @brief operator << Create TupleGatherer which holds the initial value v (move variant) | |
* @param v | |
* @return TupleGatherer with initial value v; | |
*/ | |
TupleGatherer<stream, typename decayStr<V>::type> operator<<(V&& v); | |
protected: | |
/** | |
* private default constructor, because this class is not meant to be | |
* instantiated publicly. | |
* @brief LockedStream | |
*/ | |
LockedStream() = default; | |
/** | |
* Use the static mutex to lock on this stream | |
* @brief lock | |
*/ | |
static void lock() { pthread_mutex_lock(&mutex); } | |
/** | |
* Use the static mutex to unlock on this stream. | |
* @brief lock | |
*/ | |
static void unlock() { pthread_mutex_unlock(&mutex); } | |
/** | |
* The current static LockedStream instance | |
* @brief instance | |
*/ | |
static LockedStream<stream>* instance; | |
/** | |
* The static lock mutex | |
*/ | |
static pthread_mutex_t mutex; | |
}; | |
template<std::ostream* stream> | |
LockedStream<stream>* LockedStream<stream>::instance = nullptr; | |
template<std::ostream* stream> | |
pthread_mutex_t LockedStream<stream>::mutex = PTHREAD_MUTEX_INITIALIZER; | |
template<std::ostream* stream> | |
LockedStream<stream>& LockedStream<stream>::getInstance() { | |
lock(); | |
if (instance == nullptr) { | |
instance = new LockedStream<stream>(); | |
} | |
unlock(); | |
return *instance; | |
} | |
template<std::ostream* stream> | |
void LockedStream<stream>::clean() { | |
if (instance != nullptr) { | |
delete instance; | |
} | |
pthread_mutex_destroy(&mutex); | |
} | |
template<std::ostream* stream> | |
TupleGatherer<stream, FinishMark> LockedStream<stream>::operator<<(FinishMark&) { | |
//effectively NOOP | |
return TupleGatherer<stream, FinishMark>(std::make_tuple(finish)); | |
} | |
template<std::ostream* stream> | |
template<typename V> | |
TupleGatherer<stream, typename decayStr<V>::type> LockedStream<stream>::operator<<(V& v) { | |
typedef typename decayStr<V>::type V1; | |
auto tuple = std::make_tuple(forwardStr(v)); | |
return TupleGatherer<stream, V1>(tuple); | |
} | |
template<std::ostream* stream> | |
template<typename V> | |
TupleGatherer<stream, typename decayStr<V>::type> LockedStream<stream>::operator<<(V&& v) { | |
typedef typename decayStr<V>::type V1; | |
auto tuple = std::make_tuple(forwardStr(v)); | |
return TupleGatherer<stream, V1>(tuple); | |
} | |
template<std::ostream* stream> | |
LockedStream<stream>::~LockedStream() { | |
} | |
/** | |
* Some helpful aliases | |
*/ | |
using CoutRecursiveLocked = LockedStream<&std::cout>; | |
using CerrRecursiveLocked = LockedStream<&std::cerr>; | |
static std::function<CoutRecursiveLocked&()> LOCKEDCOUT = &CoutRecursiveLocked::getInstance; | |
static std::function<CerrRecursiveLocked&()> LOCKEDCERR = &CerrRecursiveLocked::getInstance; | |
#endif // LOCKEDSTREAM_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment