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 << "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;
}

Comments
There are no comments yet. Be the first to comment!