#pragma once #ifndef _NB_SMARTSTREAM #define _NB_SMARTSTREAM #include #include #include #include #include namespace nb { typedef std::pair SmartFormat; template struct SmartText { T msg; SmartFormat fmt; }; typedef std::string (*SmartTextFormatter)(std::string, std::string); typedef std::unordered_map SmartFormatMap; template class SmartOStreamBase { public: virtual void process(const SmartText&) = 0; static const SmartFormatMap Formatters; protected: SmartOStreamBase(ST& stream) : _ostream{&stream} {} ST* _ostream; }; template class SmartOStreamBaseImpl; template class SmartOStream; template class SmartOStreamBaseImpl, Derived> : public SmartOStreamBase>{ public: using StreamType = SmartOStream; using Base = SmartOStreamBase; using Base::process; using Base::Formatters; protected: using Base::Base; }; template class SmartOStreamBaseImpl, Derived> : public SmartOStreamBase> { public: using StreamType = std::basic_ostream; using Base = SmartOStreamBase; using Base::process; using Base::Formatters; protected: using Base::Base; }; template class SmartOStream : public SmartOStreamBaseImpl { template friend SmartOStream& operator<<(SmartOStream&, const U&); public: using Base = SmartOStreamBaseImpl; SmartOStream(StreamType& stream) : Base(stream) {} StreamType* getStream() const { return _ostream; } template void process(const X& val) { *_ostream << val; } template void process(const SmartText& msg) { using D = typename std::conditional::value, SmartOStream, Derived>::type; try { *_ostream << D::Formatters.at(msg.fmt.first)( static_cast(this)->process(static_cast(msg.msg)), msg.fmt.second ); } catch (const std::out_of_range& e) { *_ostream << D::Formatters.at(0)( static_cast(this)->process(static_cast(msg.msg)), msg.fmt.second ); } } virtual void process(const SmartText&); static const SmartFormatMap Formatters; protected: using Base::_ostream; static std::string defaultFormatter(std::string msg, std::string fmt) { return msg; } }; template const SmartFormatMap SmartOStream::Formatters = { {0, defaultFormatter} }; template SmartOStream& operator<<(SmartOStream& stream, const U& msg) { using Derived = typename std::conditional::value, SmartOStream, D>::type; static_cast(&stream)->process(msg); return stream; } template void SmartOStream::process(const SmartText& msg) { using Derived = typename std::conditional::value, SmartOStream, D>::type; std::string msg_str(msg.msg); try { *_ostream << Derived::Formatters.at(msg.fmt.first)(msg_str, msg.fmt.second); } catch (const std::out_of_range& e) { *_ostream << Derived::Formatters.at(0)(msg_str, msg.fmt.second); } } template class SmartTerminal : public SmartOStream> { using Base = SmartOStream>; public: using Base::Base; static const SmartFormatMap Formatters; enum FormatterCodes : unsigned int { DEFAULT = 0, COLOR, CLEAR, BOLD, }; protected: }; template const SmartFormatMap SmartTerminal::Formatters = { {SmartTerminal::DEFAULT, SmartOStream::defaultFormatter} }; } #endif // _NB_SMARTSTREAM