Tip: String Wrapper for Formatted String Output in C++ | CodeGuru

Tip: String Wrapper for Formatted String Output in C++

String Wrapper for Formatted String Output in C++ For a long time, I was very reluctant to use “ostrstream,” the C++ equivalent of the “sprintf” C function, because of longer and less understandable code: ostrstream ss; ss

Written By
CodeGuru Staff
CodeGuru Staff
Aug 7, 2008
1 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

String Wrapper for Formatted String Output in C++

For a long time, I was very reluctant to use “ostrstream,” the C++ equivalent of the “sprintf” C function, because of longer and less understandable code:

ostrstream ss;
ss << "value = " << x;
string s = ss.str();

Using three lines and introducing another (besides string) class seems to me too much and inconsistent with my favorite principle of Occam’s razor.

But relatively recently, I learned (by reading others’ code) that sometimes this extra complexity can be hidden by using macros. For example, a log output macro could be defined in this manner:

#define LOG(arg) { ostringstrean ss; ss << arg;
   output(ss.str()); }

And can be used this way:

LOG("sample");
LOG("value = " << x);

So, I decided to try to implement something similar for just string formatting, without forcing the programmer to use any extra classes or variables, something like this:

String s;
s << "value = " << x;
// then using s for whatever is needed...

Here is the solution: a wrapper around the standard string and ostringstream classes:

//===============================================================
class String : public std::string
{
public:
   // Constructors
   String() {}
   String(const char * s_) : std::string(s_) {}
   String(std::string const & s_) : std::string(s_) {}
   String(const char * s_, int n_) : std::string(s_, n_) {}
   String(const char * s_, int p_, int n_) :
      std::string(s_, p_, n_) {}

   // Converting string to character pointer. Defining this
   // operator creates some danger of bugs caused by dangling
   // pointers, but is very convenient for passing string
   // parameters to "const char *" formal arguments
   operator const char *() { return c_str(); }

   char const & operator [](unsigned long int i_)
        const { return (*(std::string *)this)[i_]; }
   char const & operator [](unsigned int i_)
        const { return (*(std::string *)this)[i_]; }
   char const & operator [](int i_)
        const { return (*(std::string *)this)[i_]; }
   char & operator [](unsigned long int i_)
      { return (*(std::string *)this)[i_]; }
   char & operator [](unsigned int i_)
      { return (*(std::string *)this)[i_]; }
   char & operator [](int i_)
      { return (*(std::string *)this)[i_]; }

   String ToUpper() { String r = *this; for(unsigned int i = 0;
      i < size(); i++) r[i] = ::toupper(r[i]); return r; }
   String ToLower() { String r = *this; for(unsigned int i = 0;
      i < size(); i++) r[i] = ::tolower(r[i]); return r; }
};

template<typename T_> String operator *(String const & s_,
   T_ const & t_)
{
   return s_ + t_;
}

//===============================================================
class StringStream
{
public:

   struct StringStreamRef
   {
      explicit StringStreamRef(StringStream * ss_)
         throw() : ss(ss_) {}
      operator StringStream &() const throw()
      {
         return *ss;
      }
      StringStream * ss;
   };

   explicit StringStream(String & str_) : str(&str_)
   {
      ss = new std::ostringstream;
      *ss << *str;
   }
   explicit StringStream(StringStream & ss_) throw()
   {
      *this = ss_;
   }
   StringStream(StringStreamRef ssr_) throw()
   {
      *this = *ssr_.ss;
   }
   StringStream & operator =(StringStream & ss_) throw()
   {
      ss      = ss_.ss;
      str     = ss_.str;
      ss_.ss  = 0;
      ss_.str = 0;
      return *this;
   }
   ~StringStream()
   {
      if(ss && str)
      *str = ss->str();
      delete ss;
   }

   // Operators for converting last calculated string value to
   // String in case "<<" expression is in rhs.
   operator String() { return ss->str(); }
   template<typename T_> friend StringStream operator
      <<(StringStream ss_, T_ const & p_);
   template<typename T_> friend StringStream operator
      <<(String & s_, T_ const & p_);

   operator StringStreamRef() throw()
   {
      return StringStreamRef(this);
   }

private:
   std::ostringstream * ss;
   String * str;

};

template<typename T_> StringStream operator
   <<(StringStream ss_, T_ const & p_)
{
   *ss_.ss << p_;
   return ss_;
}

template<typename T_> StringStream operator
   <<(String & s_, T_ const & p_)
{
   StringStream w(s_);
   (*w.ss) << p_;
   return w;
}
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.