55 */
66// ***********************************************************************************************
77// CONSTITUTION : 1 solution of smart log,
8- // - tmp log is stringstream (dyn buf)
8+ // - tmp log is std::string via lightweight custom streambuf (dyn buf)
99// - permanent log is cout (screen)
1010// . shortcoming: multiple smart logs will mix output on screen
1111// - formatted log (FSL = Formatted Smart Log)
1212// . slower than unformatted smart log, but more readable
1313// - derive from BaseSL is to reuse common func
1414//
15- // CORE: _tmpBuf (func around it)
15+ // CORE: sbuf_ (func around it)
1616//
1717// MT safe: false
1818//
19- // mem safe: yes if stringstream is
19+ // mem safe: yes
2020// ***********************************************************************************************
2121#pragma once
2222
2323#include < iostream>
24- #include < sstream > // stringstream
24+ #include < string >
2525
2626#include " BaseSL.hpp"
2727
2828namespace rlib
2929{
30+ // ***********************************************************************************************
31+ // - lightweight streambuf that appends directly to std::string
32+ // - avoids std::stringbuf's bidirectional buffer & internal double-buffering overhead
33+ class StringStreamBuf : public std ::streambuf
34+ {
35+ public:
36+ const std::string& str () const noexcept { return buf_; }
37+ size_t size () const noexcept { return buf_.size (); }
38+
39+ protected:
40+ int_type overflow (int_type ch) override
41+ {
42+ if (ch != traits_type::eof ())
43+ buf_ += static_cast <char >(ch);
44+ return ch;
45+ }
46+
47+ std::streamsize xsputn (const char * s, std::streamsize n) override
48+ {
49+ buf_.append (s, static_cast <size_t >(n));
50+ return n;
51+ }
52+
53+ private:
54+ std::string buf_;
55+ };
56+
57+ // ***********************************************************************************************
58+ // - holder to ensure sbuf_ is constructed before std::ostream base
59+ struct StrCoutFSL_BufHolder
60+ {
61+ StringStreamBuf sbuf_;
62+ };
63+
3064// ***********************************************************************************************
3165class StrCoutFSL // FSL = Formatted Smart Log
3266 : public BaseSL
33- , public std::stringstream // for operator<<(); must not ostringstream since dump need read it!!!
67+ , private StrCoutFSL_BufHolder // must be before std::ostream to init sbuf_ first
68+ , public std::ostream // lightweight: no bidirectional stringbuf overhead
3469{
3570public :
71+ StrCoutFSL () : std::ostream(&sbuf_) {}
3672 ~StrCoutFSL () noexcept ;
3773
38- // for gtest/etc that failure maybe after testcase destruction
39- // void forceDel() { stringstream().swap(*this); }
4074 void forceSave () const noexcept ;
75+ size_t size () const noexcept { return sbuf_.size (); }
4176};
4277
4378// ***********************************************************************************************
@@ -54,7 +89,7 @@ StrCoutFSL::~StrCoutFSL() noexcept
5489inline
5590void StrCoutFSL::forceSave () const noexcept
5691{
57- std::cout << rdbuf () << ' \n ' ; // rdbuf() faster than str() ; newline w/o flush faster than endl
92+ std::cout << sbuf_. str () << ' \n ' ; // direct string access ; newline w/o flush faster than endl
5893}
5994
6095} // namespace
@@ -63,4 +98,5 @@ void StrCoutFSL::forceSave() const noexcept
6398// .......... ......... .......................................................................
6499// 2006- CSZ - create
65100// 2022-01-01 PJ & CSZ - formal log & naming
101+ // 2026-03-05 AI - replace stringstream with lightweight custom streambuf + std::string
66102// ***********************************************************************************************
0 commit comments