C++ format macro / inline ostringstream
You've all pretty much nailed this already. But it's a little challenging to follow. So let me take a stab at summarizing what you've said...
That difficulties here are that:
We are playing with a temporary
ostringstream
object, so taking addresses is contra-indicated.Because it's a temporary, we cannot trivially convert to an
ostream
object through casting.Both the constructor [obviously] and
str()
are classostringstream
methods.
(Yes, we need to use.str()
. Using theostringstream
object directly would wind up invokingios::operator void*()
, returning a pointer-like good/bad value and not a string object.)operator<<(...)
exists as both inheritedostream
methods and global functions. In all cases it returns anostream&
reference.The choices here for
ostringstream()<<"foo"
are the inherited methodostream::operator<<(void* )
and the global functionoperator<<(ostream&,const char* )
. The inheritedostream::operator<<(void* )
wins out because we can't convert to anostream
object reference to invoke the global function. [Kudos to coppro!]
So, to pull this off, we need to:
- Allocate a temporary
ostringstream
. - Convert it to an
ostream
. - Append data.
- Convert it back to an
ostringstream
. - And invoke
str()
.
Allocating: ostringstream()
.
Converting: There are several choices. Others have suggested:
ostringstream() << std::string() // Kudos to *David Norman*
ostringstream() << std::dec // Kudos to *cadabra*
Or we could use:
ostringstream() . seekp( 0, ios_base::cur )
ostringstream() . write( "", 0 )
ostringstream() . flush()
ostringstream() << flush
ostringstream() << nounitbuf
ostringstream() << unitbuf
ostringstream() << noshowpos
- Or any other standard manipulator. [
#include <iomanip>
] Reference: See "Insert data with format" 1/3 of the way down on this webpage.
We cannot use:
operator<<( ostringstream(), "" )
(ostream &) ostringstream()
Appending: Straightforward now.
Converting back: We could just use (ostringstream&)
. But a dynamic_cast
would be safer. In the unlikely event dynamic_cast
returned NULL
(it shouldn't), the following .str()
will trigger a coredump.
Invoking str()
: Guess.
Putting it all together.
#define FORMAT(ITEMS) \
( ( dynamic_cast<ostringstream &> ( \
ostringstream() . seekp( 0, ios_base::cur ) << ITEMS ) \
) . str() )
References:
- IOstream Library
ostringstream
ostream::operator<<()
- Type Casting Tutorial
- Wiki: Type Casting
.
Inline ostringstream macro reloaded
Unfortunately I don't have access to a MSVC compiler to test against.
In my past experiences with microsoft's tools, it seems like microsoft treats language definitions and standards as little more than a rough guide. (I've lost lots of time on projects only to discover microsoft broke tradition with something as basic as C99.)
Given this regrettably situation, I suggest you experiment with a series of trivial programs. Things like:
std::ostringstream() o;
o.seekp( 0, std::ios_base::cur ) << "foo";
cout << "Test1: " << o << endl;
Or perhaps:
std::ostringstream() o;
cout << "Test2: " << typeid(o).name() << endl;
cout << "Test3: " << typeid(o.seekp( 0, std::ios_base::cur )).name() << endl;
Try to see at what point things stop working. Then work around the problem from there.
C++ stringstream inline
The reason is that the <<
operator is a member for void const*
, but a free function taking an std::ostream&
as the left hand argument for char const*
. Your std::ostringstream( "" )
is a temporary: you can call member functions (even non-const member functions) on it, but a temporary cannot be used to initialize the non-const reference of the global function.
EDIT:
Two points: first, as has been pointed out, g++ does do what you
want if you specify -std=c++11
. As T.C. points out, this is
specified in §27.7.3.9, which provides a template overload for
all <<
with an rvalue reference for the std::istream
parameter.
And second, the classic work around is
to start the expression std::ostringstream( "" ).flush() <<...
. flush
is a member function (and so can be called on
a temporary) which returns an std::ostream&
(so everything
else chains nicely); it also does nothing on
a std::ostringstream
.
Can I pass C++ strings into a method in the style of a stream?
I think you should look at this question for some hints as to what will be required to get the behavior you want.
This sort of thing seems to a bit difficult.
std::ostringstream printing the address of the c-string instead of its content
The expressionstd::ostringstream()
creates a temporary, and operator<<
which takes const char*
as argument is a free function, but this free function cannot be called on a temporary, as the type of the first parameter of the function is std::ostream&
which cannot be bound to temporary object.
Having said that, <<std::ostringstream() << "some data"
resolves to a call to a member function which is overloaded for void*
which prints the address. Note that a member function can be invoked on the temporary.
In order to call the free function, you need to convert temporary (which is rvalue) into a lvalue, and here is one trick that you can do:
std::cout << "Inline : "
<< dynamic_cast<std::ostringstream&>(
std::ostringstream().flush() << "some data"
).str()
<< "\n";
That is, std::ostringstream().flush()
returns std::ostream&
which means, now the free function can called, passing the returned reference as first argument.
Also, you don't need to use dynamic_cast
here (which is slow, as it is done at runtime), for the type of the object is pretty much known, and so you can use static_cast
(which is fast as it is done at compile-time):
std::cout << "Inline : "
<< static_cast<std::ostringstream&>(
std::ostringstream().flush() << "some data"
).str()
<< "\n";
which should work just fine.
std::string formatting like sprintf
You can't do it directly, because you don't have write access to the underlying buffer (until C++11; see Dietrich Epp's comment). You'll have to do it first in a c-string, then copy it into a std::string:
char buff[100];
snprintf(buff, sizeof(buff), "%s", "Hello");
std::string buffAsStdStr = buff;
But I'm not sure why you wouldn't just use a string stream? I'm assuming you have specific reasons to not just do this:
std::ostringstream stringStream;
stringStream << "Hello";
std::string copyOfStr = stringStream.str();
How to initialize a std::stringstream?
stringstream ss << "Number of people is " << numPeople;
Why can't I assign the
stringstream
value at the same time I declare it?
This is similar to hoping this would work...
int x + 3 + 9;
...but this doesn't parse as a variable definition, let alone a definition and assignment.
The legal way to define and initialise an object
The only ways to define and initialise a variable with a non-default value are (in a grammar sense - this isn't code):
type identifier(...args...);
type identifier{...args...};
type identifier = ...expression...;
The last notation is equivalent to the first - i.e. type identifier(arg)
where arg is passed the ...expression...
.
Trying to use the legal notation for int and stringstream
For int
, you can easily correct the code:
int x = 3 + 9;
...and it works because "3 + 9" can be evaluated independently first to give a sane value to store in x
. The compiler's behaviour for operator +
on int
s does what we want: it produces the int
result we then want to store in x
. You can think of the above as:
int x = (int)3 + (int)9; // notate the implicit types
(int)12; // evaluate expression => value to assign
int x((int)12); // construct a valid value
It works! But if you try that for stringstream
...
stringstream ss = "Number of people is " << numPeople; // BROKEN
"Number of people is " << numPeople; // bitshift?!
...it won't work, because "Number of people is " << numPeople
needs to be evaluated first but is illegal - you'll get an error like:
error C2296: '<<' : illegal, left operand has type 'const char [20]'
The problem is that the compiler's still trying to apply the bitwise shift operation, which only makes sense for numbers, because the overloads for <<
that we want to use require that any "X << Y" code has the left-hand part "X" be - or be implicitly convertible to - an ostream&
. A string literal can't be converted. At this point, the compiler is oblivious to the stringstream
to which the result of the expression will be passed.
A solution for stringstream
It's a bit of a chicken-and-egg problem, because you need to combine the right-hand values you want in the stringstream
to call the stringstream
's constructor, but for that you need... a stringstream
. You can actually pull that off with a temporary stringstream
:
static_cast<std::ostringstream&&>(std::ostringstream{} << "Number of people is " << numPeople)
The cast is unfortunately needed because the operator<<
overloads handle stringstream
s via references to their ostream
base class, returning an ostream&
, so you need to cast back to the stringstream
type manually, so you can then invoke the std::stringstream
move constructor...
The complete one-liner construction is then...
std::ostringstream ss(static_cast<std::ostringstream&&>(std::ostringstream{} << "Number of people is " << numPeople));
...or...
auto&& ss = static_cast<std::ostringstream&&>(std::ostringstream{} << "Number of people is " << numPeople);
...but that's too hideous to contemplate.
Making the solution (arguably) less hideous with macros
Yes, you read that right. Depending on your sensibilities, you may feel a macro helps or is worse...
#define OSS(VALUES) \
static_cast<std::ostringstream&&>(std::ostringstream{} << VALUES)
auto&& ss = OSS("Number of people is " << numPeople);
FWIW, you could also use the macro to create strings...
auto&& s = OSS("Number of people is " << numPeople).str();
...or create a dedicated macro...
#define STR(VALUES) \
static_cast<std::ostringstream&&>(std::ostringstream{} << VALUES).str()
auto&& s = STR("Number of people is " << numPeople);
An (arguably) better practice - separate construction and initialisation
Just create the stringstream
- optionally providing a single string
to the constructor - then use operator<<
in a second statement:
std::stringstream ss;
ss << "Number of people is " << numPeople;
This is much easier to read, and there are no weird macros required.
An alternative
C++11 introduced to_string()
overloads which are convenient if you have an integral value or two to concatentate with or into a string
:
auto&& s = "Number of people is " + std::to_string(numPeople);
This may be inefficient though (check your compiler(s) optimisation abilities if you care): each std::to_string()
is likely to dynamically allocate a buffer for an independent std::string
instance, then the individual concatenations may involve extra copying of text, and the original dynamically-allocated buffers may need to be enlarged, then most of those temporary std::string
s will take time to deallocate during destruction.
Discussion
Ideally, std::stringstream
would have a constructor accepting an arbitrary number of constructor arguments (A, B, C...)
to be formatted into the stringstream
as if by a subsequent << A << B << C...
. There are already constructors with arguments (e.g. (std::ios_base::openmode, const Allocator&)
), so we'd need a placeholder to distinguish such arguments from values we're trying to format into the stream, or a weirder workaround like requiring the values to be formatted into the stream be passed in as an initialiser list.
Still, it looks and feels very weird using strings with ,
instead of <<
:
std::stringstream ss{"n:", std::setw(4), std::hex, '\n');
And then if during code maintenance you find you need to move the streaming values to a point after construction, you'd need to change the separator. Breaking it out into two lines to start with - construction then streaming - simplifies that maintenance.
It was worse in C++03
C++03 lacked move constructors, so it was necessary to use the std::ostringstream::str()
member function on the temporary to get an extra deep-copy of the std::string
with which to construct the named stringsteam
...
stringstream ss(static_cast<std::ostringstream&>(std::ostringstream() << "Number of people is " << numPeople).str());
With this C++03 code, there's a likelihood of duplicate dynamic memory allocations (unless the strings are short enough to fit inside the string object, a commonly provided std::string
technique called "Short String Optimisation" or SSO). There's also a deep copy of textual content. Construction-followed-by-streaming was a better approach.
How do I convert a double into a string in C++?
The boost (tm) way:
std::string str = boost::lexical_cast<std::string>(dbl);
The Standard C++ way:
std::ostringstream strs;
strs << dbl;
std::string str = strs.str();
Note: Don't forget #include <sstream>
Related Topics
How to Get File Extension from String in C++
Calling a Constructor to Re-Initialize Object
How Are User-Level Threads Scheduled/Created, and How Are Kernel Level Threads Created
Can't Find Windows Forms Application for C++
Looking for 16-Bit X86 Compiler
Error C2065: 'Cout':Undeclared Identifier
What Is the Ndebug Preprocessor MACro Used for (On Different Platforms)
Cin.Getline() Is Skipping an Input in C++
How to Use a Custom Type as Key for a Map in C++
Can Lambda Functions Be Recursive
Namespaces and Operator Resolution
Allocating Vectors (Or Vectors of Vectors) Dynamically
Relative Paths Not Working in Xcode C++
Convert a C++ Program to a Windows Service