lightweight replacement for boost::format

Dear boosters, When needing to dump a lot of data into a string, we usually use boost::format. However, there have been lots of times when I needed a simple and lightweight replacement. I've used one for quite a while (see attachement). Basic usage: str_stream() << "str" << s << "int " << i; // automatic conversion to string. Example: std::string msg = str_stream() << "There have been " << users << " users logged on so far"; Also, attached an example. Any interest in something like this in boost? Best, John <cid:part1.07010200.01010400@torjo.com> ////////////////////////////////////////////////////////////////////// // example.cpp #include "str_stream.hpp" #include <iomanip> std::basic_ostream< wchar_t> & width_4( std::basic_ostream< wchar_t> & out) { out.width( 4); return out; } void print_c_like_str( const char * str) { std::cout << str << std::endl; } void test() { using namespace boost; // for wide strings. std::wstring str = wstr_stream() << std::oct << 8; str = wstr_stream() << std::setfill( L'0') << width_4 << 35 << std::endl; std::wcout << str; // for strings int users = 45; std::string s = str_stream() << "There have been " << users << " users logged on so far"; std::cout << s << std::endl; int words = 78; s = str_stream( "We found ") << words << " words."; std::cout << s << std::endl; // convert it to C-like string print_c_like_str( (str_stream() << "testing " << 1 << 2 << 3).c_str() ); std::cin.get(); } int main() { test(); } ////////////////////////////////////////////////////////////////////// // str_stream.h #ifndef BOOST_STR_STREAM__HPP_JT_2003MAR04 #define BOOST_STR_STREAM__HPP_JT_2003MAR04 // Copyright John Torjo 2003-2004 // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include <string> #include <sstream> #include <iosfwd> namespace boost { namespace stream_detail { template< class char_type, class char_traits> struct as_c_str_res { typedef std::basic_string<char_type,char_traits> str_type; as_c_str_res( const str_type & str) { m_str = str; } operator const char_type*() const { return m_str.c_str(); } private: str_type m_str; }; } // basic_str_stream - allow stream usage, and then conversion to string template< class char_type, class char_traits = std::char_traits<char_type> > class basic_str_stream { typedef std::basic_stringstream< char_type, char_traits> stringstream_type; typedef std::basic_string< char_type, char_traits> string_type; typedef stream_detail::as_c_str_res<char_type,char_traits> c_like_str_type; public: // default construction basic_str_stream() {} // allow to_string like usage template< class type> basic_str_stream( const type & value) { *this << value; } stringstream_type & underlying_stream() const { return m_out; } // implicit conversion operator string_type() const { return m_out.str(); } // explicit conversion to string string_type str() const { return m_out.str(); } // explicit conversion to C-like string c_like_str_type c_str() const { return c_like_str_type( m_out.str() ); } private: mutable stringstream_type m_out; #ifndef NDEBUG public: void recalculate_string() const { m_string = m_out.str(); } private: mutable string_type m_string; #endif }; // class basic_str_stream typedef basic_str_stream< char> str_stream; typedef basic_str_stream< wchar_t> wstr_stream; template< class char_type, class char_traits, class type> inline const basic_str_stream< char_type, char_traits> & operator<< ( const basic_str_stream< char_type, char_traits> & out, const type & value) { out.underlying_stream() << value; #ifndef NDEBUG out.recalculate_string(); #endif return out; } // allow function IO manipulators template< class char_type, class char_traits> inline const basic_str_stream< char_type, char_traits> & operator<< ( const basic_str_stream< char_type, char_traits> & out, std::ios_base & (*func)( std::ios_base&) ) { func( out.underlying_stream()); return out; } template< class char_type, class char_traits> inline const basic_str_stream< char_type, char_traits> & operator<< ( const basic_str_stream< char_type, char_traits> & out, std::basic_ios< char_type, char_traits> & (*func)( std::basic_ios< char_type, char_traits> &) ) { func( out.underlying_stream()); return out; } template< class char_type, class char_traits> inline const basic_str_stream< char_type, char_traits> & operator<< ( const basic_str_stream< char_type, char_traits> & out, std::basic_ostream< char_type, char_traits> & (*func)( std::basic_ostream< char_type, char_traits> &) ) { func( out.underlying_stream()); return out; } } // namespace boost #endif // End of file

One of the significant advantages to using formatting facilities like *printf or Boost Format is that messages with embedded value placement indicators can be stored as a single string, which makes translation of such message strings much, much easier. Thus, much of the benefit of such a facility is lost when the ability to represent message strings as single strings is lost. -- Jeremy Maitin-Shepard

Jeremy Maitin-Shepard wrote:
One of the significant advantages to using formatting facilities like *printf or Boost Format is that messages with embedded value placement indicators can be stored as a single string, which makes translation of such message strings much, much easier. Thus, much of the benefit of such a facility is lost when the ability to represent message strings as single strings is lost.
I know that, it's really common sense. I'm only thinking of using str_stream() for debugging/logging. (p.s. - indeed, I should have specified this in the original post ;)) In this case, it does make a lot of sense (at least to me). Just think how easy it is to say something like: throw std::runtime_error(str_stream() << "invalid line at idx " << idx); as opposed to something like throw std::runtime_error(io::str(format("invalid line at idx %1%) % idx))); I think the former is more readable. It's not just that. It's also the fact that str_stream is **lightweight**. Have you taken a look at the implementation of boost::format? Don't get me wrong - I love boost::format. In fact I use it a lot. But for simple things I use simple classes. As a side note, you can also use it like this: std::string s = str_stream(idx); Best, John

Just think how easy it is to say something like: throw std::runtime_error(str_stream() << "invalid line at idx " << idx);
wrap_stringstream in Boost Test has similar purpose. It was requested at some point to be separated in utilities,. I made all the code changes required but did not manage to create doc. Gennadiy. P.S. Again - it has nothing to do with boost format ,which primary audience is internationalization guys. So subject is quite misleading.

Gennadiy Rozental wrote:
Just think how easy it is to say something like: throw std::runtime_error(str_stream() << "invalid line at idx " << idx);
wrap_stringstream in Boost Test has similar purpose. It was requested at some point to be separated in utilities,. I made all the code changes required but did not manage to create doc.
Gennadiy.
P.S. Again - it has nothing to do with boost format ,which primary audience is internationalization guys. So subject is quite misleading.
True, my mistake. Best, John

John Torjo wrote:
Dear boosters,
When needing to dump a lot of data into a string, we usually use boost::format. However, there have been lots of times when I needed a simple and lightweight replacement.
I've used one for quite a while (see attachement). Basic usage: str_stream() << "str" << s << "int " << i; // automatic conversion to string. Example: std::string msg = str_stream() << "There have been " << users << " users logged on so far";
Also, attached an example. Any interest in something like this in boost?
What is wrong with: std::ostringstream oss; oss << "There have been " << users << "users logged on so far"; std::string msg(oss.str()); This can probably even be: std::string msg((std::ostringstream() << "There have been " << users << "users logged on so far").str()); I have to admit that I don't understand any of the reasons for using printf... functions or boost::format instead of the C++ string streams. The latter seem much more natural and easier to me, and are further supported by boost::lexical_cast to make trivial conversions to and from strings even easier.

"Edward Diener" <eddielee@tropicsoft.com> writes:
[snip]
This can probably even be:
std::string msg((std::ostringstream() << "There have been " << users << "users logged on so far").str());
Unfortunately this is not possible, because operator<< returns a basic_ostream reference, not a stringstream reference.
I have to admit that I don't understand any of the reasons for using printf... functions or boost::format instead of the C++ string streams. The latter seem much more natural and easier to me, and are further supported by boost::lexical_cast to make trivial conversions to and from strings even easier.
Refer to my other posting for information on why printf-like mechanisms significantly aid translation. Using the stringstream mechanism, translation requires modifying the source code, which is not at all practical. -- Jeremy Maitin-Shepard

Jeremy Maitin-Shepard wrote:
"Edward Diener" <eddielee@tropicsoft.com> writes:
[snip]
This can probably even be:
std::string msg((std::ostringstream() << "There have been " << users << "users logged on so far").str());
Unfortunately this is not possible, because operator<< returns a basic_ostream reference, not a stringstream reference.
OK, I see, Doing an .str() on a basic_ostream reference won't work. Maybe: std::string msg((dynamic_cast<const std::ostringstream &>(std::ostringstream() << "There have been " << users << "users logged on so far")).str()); but it begins to look a but ugly.
I have to admit that I don't understand any of the reasons for using printf... functions or boost::format instead of the C++ string streams. The latter seem much more natural and easier to me, and are further supported by boost::lexical_cast to make trivial conversions to and from strings even easier.
Refer to my other posting for information on why printf-like mechanisms significantly aid translation. Using the stringstream mechanism, translation requires modifying the source code, which is not at all practical.
OK, I haven't done much language or locale translation so I have no strong opinion either way. I do see that printf like strings, with their embedded % identifiers can be translated whole, whereas stream strings are often made of fragments combined.

On 22/04/2004, at 12:34 PM, Edward Diener wrote:
OK, I haven't done much language or locale translation so I have no strong opinion either way. I do see that printf like strings, with their embedded % identifiers can be translated whole, whereas stream strings are often made of fragments combined.
Sorry this is a bit OT, but... The problem is that the fragments you end up with depend on the structure of the language you first wrote the message in. The same sentence in a different language might not break into the same fragments. So it's not just that you have to translate a lot of fragments instead of a few phrases, it's that you can't be sure you can translate the fragments you have into new fragments that'll give you correct phrases. Having one string to translate, as well as format's nice features that can be used for, for example, putting the logic for handling plurals into the string, is cool. cheers, Geoff

The problem is that the fragments you end up with depend on the structure of the language you first wrote the message in. The same sentence in a different language might not break into the same fragments. So it's not just that you have to translate a lot of fragments instead of a few phrases, it's that you can't be sure you can translate the fragments you have into new fragments that'll give you correct phrases.
Having one string to translate, as well as format's nice features that can be used for, for example, putting the logic for handling plurals into the string, is cool.
see my answer to Jeremy. Best, John

Edward Diener wrote:
Jeremy Maitin-Shepard wrote:
"Edward Diener" <eddielee@tropicsoft.com> writes:
[snip]
This can probably even be:
std::string msg((std::ostringstream() << "There have been " << users << "users logged on so far").str());
Unfortunately this is not possible, because operator<< returns a basic_ostream reference, not a stringstream reference.
OK, I see, Doing an .str() on a basic_ostream reference won't work. Maybe:
std::string msg((dynamic_cast<const std::ostringstream &>(std::ostringstream() << "There have been " << users << "users logged on so far")).str());
but it begins to look a but ugly.
my point exactly :D Best, John
participants (5)
-
Edward Diener
-
Gennadiy Rozental
-
Geoff Leyland
-
Jeremy Maitin-Shepard
-
John Torjo