?

Log in

No account? Create an account
 
 
24 June 2008 @ 09:40 pm
String wrapper for formatted string output in C++  

String wrapper for formatted string output in C++.

 

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

 

ostrstream  ss;

ss << “value = “ << x;

string s = ss.str();

 

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 other’s code) that sometimes this extra complexity can be hidden by using macros. For example, log output macro could be defined:

 

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

 

And can be used:

 

LOG(“sample”);

LOG(“value = “ << x;

 

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

 

String s;

s << “value = “ << x;

// then using s for whatever is needed…

 

Here is the solution: a wrapper around 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 create some donger of bugs caused
   // dangling pointers, but is very convinient 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;
}

 

 

 
 
 
Misha Furmanmishafurman on July 7th, 2008 04:51 pm (UTC)
problem
There is a problem with this code: it works well under Microsoft C++. but does not even compile under g++!
Actually there are more then one. One is well known "auto_ptr problem": temporaries con't be bound by non-const reference formal parameter(and MSC does not check it). Well known solutions are either use "mutable" or some intermediate "proxy" reference class.
But there something else wrong in my code related to definitions of "operator <<" (or in g++, that is highly unlikely) - I will publish correct code when I have it.
Misha Furmanmishafurman on July 8th, 2008 02:03 am (UTC)
Fixed
I replaced program text in the main entry. Now it works under both MSVC and g++. I've already described main problem that I solved using trick with proxy reference class that is used in last releases auto_ptr in STL.
There were also some other minor bugs that strangele were not caught by MSVC too...
Misha Furmanmishafurman on August 13th, 2008 06:03 pm (UTC)